diff --git a/src/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs b/src/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs index b964d8080f..ea59cdf22b 100644 --- a/src/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs +++ b/src/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs @@ -196,7 +196,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 /*TYA [implied]*/ new Uop[] { Uop.Imp_TYA, Uop.End }, /*STA addr,Y [absolute indexed WRITE]*/ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_Y, Uop.AbsIdx_Stage4, Uop.AbsIdx_WRITE_Stage5_STA, Uop.End }, /*TXS [implied]*/ new Uop[] { Uop.Imp_TXS, Uop.End }, - /*SHS* addr,Y [absolute indexed WRITE Y] [unofficial] */ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_Y, Uop.AbsIdx_Stage4, Uop.AbsIdx_WRITE_Stage5_ERROR, Uop.End }, + /*SHS* addr,Y [absolute indexed WRITE Y] [unofficial] */ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_Y, Uop.AbsIdx_Stage4_SHS, Uop.AbsIdx_WRITE_Stage5_SHS, Uop.End }, /*SHY** [absolute indexed WRITE] [unofficial]*/ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_X, Uop.AbsIdx_Stage4_SHY, Uop.AbsIdx_WRITE_Stage5_SHY, Uop.End }, /*STA addr,X [absolute indexed WRITE]*/ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_X, Uop.AbsIdx_Stage4, Uop.AbsIdx_WRITE_Stage5_STA, Uop.End }, /*SHX* addr,Y [absolute indexed WRITE Y] [unofficial]*/ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_Y, Uop.AbsIdx_Stage4_SHX, Uop.AbsIdx_WRITE_Stage5_SHX, Uop.End }, @@ -416,7 +416,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 //[absolute indexed WRITE] AbsIdx_WRITE_Stage5_STA, AbsIdx_WRITE_Stage5_SHY, AbsIdx_WRITE_Stage5_SHX, //unofficials - AbsIdx_WRITE_Stage5_ERROR, + AbsIdx_WRITE_Stage5_SHS, //[absolute indexed READ] AbsIdx_READ_Stage4, AbsIdx_READ_Stage5_LDA, AbsIdx_READ_Stage5_CMP, AbsIdx_READ_Stage5_SBC, AbsIdx_READ_Stage5_ADC, AbsIdx_READ_Stage5_EOR, AbsIdx_READ_Stage5_LDX, AbsIdx_READ_Stage5_AND, AbsIdx_READ_Stage5_ORA, AbsIdx_READ_Stage5_LDY, AbsIdx_READ_Stage5_NOP, @@ -474,7 +474,8 @@ namespace BizHawk.Emulation.Cores.Components.M6502 IndIdx_WRITE_Stage5_SHA, AbsIdx_Stage4_SHX, AbsIdx_Stage4_SHY, - AbsIdx_Stage4_SHA + AbsIdx_Stage4_SHA, + AbsIdx_Stage4_SHS, } private void InitOpcodeHandlers() @@ -524,6 +525,8 @@ namespace BizHawk.Emulation.Cores.Components.M6502 public int mi; //microcode index private bool iflag_pending; //iflag must be stored after it is checked in some cases (CLI and SEI). public bool rdy_freeze; //true if the CPU must be frozen + private byte H; //internal temp variable used in the "unstable high byte group" of unofficial instructions + public ushort address_bus; // The 16 bit address bus. //tracks whether an interrupt condition has popped up recently. //not sure if this is real or not but it helps with the branch_irq_hack @@ -605,6 +608,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 opcode = _link.ReadMemory(PC++); mi = -1; } + address_bus = PC; } private void Fetch2() @@ -622,6 +626,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { opcode3 = _link.ReadMemory(PC++); + address_bus = (ushort) ((opcode3 << 8) | opcode2); } } @@ -939,8 +944,10 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { alu_temp = ea + Y; - ea = (_link.ReadMemory((byte)(opcode2 + 1)) << 8) + ea = (_link.ReadMemory((byte) (opcode2 + 1)) << 8) | ((alu_temp & 0xFF)); + address_bus = (ushort) ea; + H = 0; // In preparation for SHA (indirect, X), set H to 0. } } @@ -957,16 +964,20 @@ namespace BizHawk.Emulation.Cores.Components.M6502 private void IndIdx_WRITE_Stage5_SHA() { rdy_freeze = !RDY; + if (RDY) { + H |= (byte) ((ea >> 8) + 1); _link.ReadMemory((ushort) ea); if (alu_temp.Bit(8)) { ea = (ushort) (ea & 0xFF | ((ea + 0x100) & 0xFF00 & ((A & X) << 8))); } - - ea += unchecked((ushort) (alu_temp & 0xFF00)); + } + else + { + H = 0xFF; //If the RDY line is low here, the SHA instruction omits the bitwise AND with H } } @@ -1008,13 +1019,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 private void IndIdx_WRITE_Stage6_SHA() { - alu_temp = A & X; - - if (RDY) - { - alu_temp &= unchecked((byte) ((ea >> 8) + 1)); - } - + alu_temp = A & X & H; _link.WriteMemory((ushort) ea, unchecked((byte) alu_temp)); } @@ -2045,6 +2050,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { ea = _link.ReadMemory((ushort)alu_temp); + address_bus = (ushort) ea; } } @@ -2453,7 +2459,8 @@ namespace BizHawk.Emulation.Cores.Components.M6502 opcode3 = _link.ReadMemory(PC++); alu_temp = opcode2 + Y; ea = (opcode3 << 8) + (alu_temp & 0xFF); - + address_bus = (ushort) ea; + H = 0; // In preparation for SHA, SHS, and SHX, set H to 0. //new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_Y, Uop.AbsIdx_Stage4, Uop.AbsIdx_WRITE_Stage5_STA, Uop.End }, } } @@ -2466,6 +2473,8 @@ namespace BizHawk.Emulation.Cores.Components.M6502 opcode3 = _link.ReadMemory(PC++); alu_temp = opcode2 + X; ea = (opcode3 << 8) + (alu_temp & 0xFF); + address_bus = (ushort) ea; + H = 0; // In preparation for SHY, set H to 0. } } @@ -2509,6 +2518,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { + H |= (byte) ((ea >> 8) + 1); var adjust = alu_temp.Bit(8); alu_temp = _link.ReadMemory((ushort) ea); @@ -2517,6 +2527,10 @@ namespace BizHawk.Emulation.Cores.Components.M6502 ea = (ushort) (ea & 0xFF | ((ea + 0x100) & 0xFF00 & (X << 8))); } } + else + { + H = 0xFF; //If the RDY line is low here, the SHX instruction omits the bitwise AND with H + } } private void AbsIdx_Stage4_SHY() @@ -2525,6 +2539,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { + H |= (byte) ((ea >> 8) + 1); var adjust = alu_temp.Bit(8); alu_temp = _link.ReadMemory((ushort) ea); @@ -2533,6 +2548,10 @@ namespace BizHawk.Emulation.Cores.Components.M6502 ea = (ushort) (ea & 0xFF | ((ea + 0x100) & 0xFF00 & (Y << 8))); } } + else + { + H = 0xFF; //If the RDY line is low here, the SHY instruction omits the bitwise AND with H + } } private void AbsIdx_Stage4_SHA() @@ -2541,6 +2560,28 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { + H |= (byte) ((ea >> 8) + 1); + var adjust = alu_temp.Bit(8); + alu_temp = _link.ReadMemory((ushort) ea); + + if (adjust) + { + ea = (ushort) ((ea & 0xFF) | ((ea + 0x100) & 0xFF00 & ((A & X) << 8))); + } + } + else + { + H = 0xFF; //If the RDY line is low here, the SHA instruction omits the bitwise AND with H + } + } + + private void AbsIdx_Stage4_SHS() + { + rdy_freeze = !RDY; + + if (RDY) + { + H |= (byte) ((ea >> 8) + 1); var adjust = alu_temp.Bit(8); alu_temp = _link.ReadMemory((ushort) ea); @@ -2549,6 +2590,10 @@ namespace BizHawk.Emulation.Cores.Components.M6502 ea = (ushort) (ea & 0xFF | ((ea + 0x100) & 0xFF00 & ((A & X) << 8))); } } + else + { + H = 0xFF; //If the RDY line is low here, the SHS instruction omits the bitwise AND with H + } } private void AbsIdx_WRITE_Stage5_STA() @@ -2558,44 +2603,26 @@ namespace BizHawk.Emulation.Cores.Components.M6502 private void AbsIdx_WRITE_Stage5_SHY() { - alu_temp = Y; - - if (RDY) - { - alu_temp &= unchecked((byte)((ea >> 8) + 1)); - } - + alu_temp = Y & H; _link.WriteMemory((ushort) ea, (byte) alu_temp); } private void AbsIdx_WRITE_Stage5_SHX() { - alu_temp = X; - - if (RDY) - { - alu_temp &= unchecked((byte)((ea >> 8) + 1)); - } - + alu_temp = X & H; _link.WriteMemory((ushort) ea, (byte) alu_temp); } private void AbsIdx_WRITE_Stage5_SHA() { - alu_temp = A & X; - - if (RDY) - { - alu_temp &= unchecked((byte)((ea >> 8) + 1)); - } - + alu_temp = A & X & H; _link.WriteMemory((ushort) ea, (byte) alu_temp); } - private void AbsIdx_WRITE_Stage5_ERROR() + private void AbsIdx_WRITE_Stage5_SHS() { S = (byte)(X & A); - _link.WriteMemory((ushort)ea, (byte)(S & (opcode3+1))); + _link.WriteMemory((ushort) ea, (byte) (S & H)); } private void AbsIdx_RMW_Stage5() @@ -3225,11 +3252,12 @@ namespace BizHawk.Emulation.Cores.Components.M6502 case Uop.AbsIdx_Stage4_SHX: AbsIdx_Stage4_SHX(); break; case Uop.AbsIdx_Stage4_SHY: AbsIdx_Stage4_SHY(); break; case Uop.AbsIdx_Stage4_SHA: AbsIdx_Stage4_SHA(); break; + case Uop.AbsIdx_Stage4_SHS: AbsIdx_Stage4_SHS(); break; case Uop.AbsIdx_WRITE_Stage5_STA: AbsIdx_WRITE_Stage5_STA(); break; case Uop.AbsIdx_WRITE_Stage5_SHY: AbsIdx_WRITE_Stage5_SHY(); break; case Uop.AbsIdx_WRITE_Stage5_SHX: AbsIdx_WRITE_Stage5_SHX(); break; case Uop.AbsIdx_WRITE_Stage5_SHA: AbsIdx_WRITE_Stage5_SHA(); break; - case Uop.AbsIdx_WRITE_Stage5_ERROR: AbsIdx_WRITE_Stage5_ERROR(); break; + case Uop.AbsIdx_WRITE_Stage5_SHS: AbsIdx_WRITE_Stage5_SHS(); break; case Uop.AbsIdx_RMW_Stage5: AbsIdx_RMW_Stage5(); break; case Uop.AbsIdx_RMW_Stage7: AbsIdx_RMW_Stage7(); break; case Uop.AbsIdx_RMW_Stage6_DEC: AbsIdx_RMW_Stage6_DEC(); break; @@ -3292,6 +3320,11 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (!rdy_freeze) mi++; + + if (Microcode[opcode][mi] is Uop.End) + { + address_bus = PC; // If the next cycle is the start of a new instruction, the address bus needs to be set to the PC now, so a DMC DMA's halt cycles don't use the wrong address bus value. + } } public bool AtInstructionStart() diff --git a/src/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs b/src/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs index 71bf48b041..13ab34b24b 100644 --- a/src/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs +++ b/src/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs @@ -157,7 +157,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 public bool NMI; public bool RDY; - // ppu cycle (used with SubNESHawk) + // ppu cycle public int ext_ppu_cycle = 0; public void SyncState(Serializer ser) @@ -184,6 +184,8 @@ namespace BizHawk.Emulation.Cores.Components.M6502 ser.Sync(nameof(branch_irq_hack), ref branch_irq_hack); ser.Sync(nameof(rdy_freeze), ref rdy_freeze); ser.Sync(nameof(ext_ppu_cycle), ref ext_ppu_cycle); + ser.Sync(nameof(H), ref H); + ser.Sync(nameof(address_bus), ref address_bus); ser.EndSection(); } diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs index efb83c245e..7a0bee9559 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs @@ -54,8 +54,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS _cpu = new MOS6502X(new CpuLink(this)) { // Required to pass the Lorenz test suite. - AneConstant = 0xEF, - LxaConstant = 0xFE + AneConstant = 0xEE, + LxaConstant = 0xEE, }; // perform hard reset diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs index fdeab530e8..49a37dc5f6 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs @@ -1032,6 +1032,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES dmc.Fetch(); } + public void RunDMCHaltFetch() + { + nes.ReadMemory(nes.cpu.address_bus); + } + private readonly int[][] sequencer_lut = new int[2][]; private int sequencer_check_1, sequencer_check_2; @@ -1298,7 +1303,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES { byte ret = PeekReg(0x4015); // Console.WriteLine("{0} {1,5} $4015 clear irq, was at {2}", nes.Frame, sequencer_counter, sequencer_irq); - sequencer_irq_flag = false; + sequencer_irq_clear_pending = true; SyncIRQ(); return ret; } @@ -1329,6 +1334,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public void RunOneLast() { + if (dmc.timer % 2 is 1) + { + // The controllers only get strobed when transitioning from a get cycle to a put cycle. + if (nes.joypadStrobed) + { + nes.joypadStrobed = false; + nes.strobe_joyport(); + } + } + else + { + // The frame counter interrupt flag is only cleared when transitioning from a put cycle to a get cycle. + if (sequencer_irq_clear_pending) + { + sequencer_irq_clear_pending = false; + sequencer_irq_flag = false; + } + } + // we need to predict if there will be a length clock here, because the sequencer ticks last, but the // timer reload shouldn't happen if length clock and write happen simultaneously // I'm not sure if we can avoid this by simply processing the sequencer first diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs index dde58186f6..d87a402d34 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs @@ -83,6 +83,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public int old_s = 0; + public long double_controller_read = 0; + public ushort double_controller_read_address = 0; + public byte previous_controller1_read = 0; + public byte previous_controller2_read = 0; + public bool joypadStrobed; + public byte joypadStrobeValue; + public bool CanProvideAsync => false; internal void ResetControllerDefinition(bool subframe) @@ -343,6 +350,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES VS_coin_inserted &= 1; } + cpu.ext_ppu_cycle = 0; // Reset this value at the beginning of each frame + if (ppu.ppudead > 0) { while (ppu.ppudead > 0) @@ -443,9 +452,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public byte oam_dma_byte; public bool dmc_dma_exec = false; public bool dmc_realign; - public bool reread_trigger; - public int do_the_reread_2002, do_the_reread_2007, do_the_reread_cont_1, do_the_reread_cont_2; - public int reread_opp_4016, reread_opp_4017; public byte DB; //old data bus values from previous reads internal void RunCpuOne() @@ -509,45 +515,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES apu.dmc_dma_countdown--; if (apu.dmc_dma_countdown == 0) { - reread_trigger = true; - - do_the_reread_2002++; - - do_the_reread_2007++; - - // if the DMA address has the same bits set as the re-read address, they don't occur - // TODO: need to check if also true for ppu regs - /* - if ((apu.dmc.sample_address & 0x2007) != 0x2002) - { - do_the_reread_2002++; - } - - if ((apu.dmc.sample_address & 0x2007) != 0x2007) - { - do_the_reread_2007++; - } - */ - if ((apu.dmc.sample_address & 0x1F) != 0x16) - { - do_the_reread_cont_1++; - } - - if ((apu.dmc.sample_address & 0x1F) == 0x16) - { - reread_opp_4016++; - } - - if ((apu.dmc.sample_address & 0x1F) != 0x17) - { - do_the_reread_cont_2++; - } - - if ((apu.dmc.sample_address & 0x1F) == 0x17) - { - reread_opp_4017++; - } - apu.RunDMCFetch(); dmc_dma_exec = false; @@ -568,6 +535,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES apu.dmc.fill_glitch_2 = true; } } + else + { + // the DMC DMA Halt, Put cycles + apu.RunDMCHaltFetch(); + + } } ///////////////////////////// @@ -601,17 +574,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES apu.RunOneLast(); - if (reread_trigger && cpu.RDY) - { - do_the_reread_2002 = 0; - do_the_reread_2007 = 0; - do_the_reread_cont_1 = 0; - do_the_reread_cont_2 = 0; - reread_opp_4016 = 0; - reread_opp_4017 = 0; - reread_trigger = false; - } - if (!cpu.RDY && !dmc_dma_exec && !oam_dma_exec) { cpu.RDY = true; @@ -660,24 +622,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES else { // special hardware glitch case + if (dmc_dma_exec && ppu.region is not PPU.Region.NTSC) + { + return DB; + } ret_spec = read_joyport(addr); - //if (reread_trigger && (do_the_reread_cont_1 == 0)) { Console.WriteLine("same 1 " + (apu.dmc.sample_address - 1)); } - - if ((reread_opp_4017 > 0) && ppu.region == PPU.Region.NTSC) - { - read_joyport(0x4017); - //Console.WriteLine("DMC glitch player 2 opposite " + cpu.TotalExecutedCycles + " addr " + (apu.dmc.sample_address - 1)); - } - - if ((do_the_reread_cont_1 > 0) && ppu.region==PPU.Region.NTSC) - { - ret_spec = read_joyport(addr); - do_the_reread_cont_1--; - if (do_the_reread_cont_1 > 0) { ret_spec = read_joyport(addr); } - //Console.WriteLine("DMC glitch player 1 " + cpu.TotalExecutedCycles + " addr " + (apu.dmc.sample_address - 1)); - } - return ret_spec; } case 0x4017: @@ -696,22 +646,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES // special hardware glitch case ret_spec = read_joyport(addr); - //if (reread_trigger && (do_the_reread_cont_2 == 0)) { Console.WriteLine("same 2 " + (apu.dmc.sample_address - 1)); } - - if ((reread_opp_4016 > 0) && ppu.region == PPU.Region.NTSC) - { - read_joyport(0x4016); - //Console.WriteLine("DMC glitch player 1 opposite " + cpu.TotalExecutedCycles + " addr " + (apu.dmc.sample_address - 1)); - } - - if ((do_the_reread_cont_2 > 0) && ppu.region == PPU.Region.NTSC) - { - ret_spec = read_joyport(addr); - do_the_reread_cont_2--; - if (do_the_reread_cont_2 > 0) { ret_spec = read_joyport(addr); } - //Console.WriteLine("DMC glitch player 2 " + cpu.TotalExecutedCycles + " addr " + (apu.dmc.sample_address - 1)); - } - return ret_spec; } default: @@ -838,12 +772,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES private void write_joyport(byte value) { - //Console.WriteLine("cont " + value + " frame " + Frame); + joypadStrobeValue = value; + joypadStrobed = true; + } - var si = new StrobeInfo(latched4016, value); + public void strobe_joyport() + { + // The controllers only get strobed when transitioning from a get cycle to a put cycle. + + //Console.WriteLine("cont " + value + " frame " + Frame); + var si = new StrobeInfo(latched4016, joypadStrobeValue); ControllerDeck.Strobe(si, _controller); - latched4016 = value; - new_strobe = (value & 1) > 0; + latched4016 = joypadStrobeValue; + new_strobe = (joypadStrobeValue & 1) is not 0; if (current_strobe && !new_strobe) { controller_was_latched = true; @@ -863,7 +804,32 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES } else { - ret = addr == 0x4016 ? ControllerDeck.ReadA(_controller) : ControllerDeck.ReadB(_controller); + if (TotalExecutedCycles == double_controller_read && addr == double_controller_read_address) + { + if (addr == 0x4016) + { + ret = previous_controller1_read; + } + else + { + ret = previous_controller2_read; + } + } + else + { + if (addr == 0x4016) + { + ret = ControllerDeck.ReadA(_controller); + previous_controller1_read = ret; // If the following CPU cycle is also reading from this controller port, read the same value without clocking the controller. + } + else + { + ret = ControllerDeck.ReadB(_controller); + previous_controller2_read = ret; + } + } + double_controller_read_address = (ushort) addr; + double_controller_read = TotalExecutedCycles + 1; // The shift register in the controller is only updated if the previous CPU cycle did not read from the controller port. } ret &= 0x1f; @@ -997,6 +963,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public byte ReadMemory(ushort addr) { + if (!oam_dma_exec && !dmc_dma_exec) + { + cpu.address_bus = addr; + } byte ret; if (addr >= 0x8000) @@ -1041,7 +1011,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES // this means that OAM DMA can actually access memory that the cpu cannot if (oam_dma_exec) { - if ((cpu.PC >= 0x4000) && (cpu.PC < 0x4020)) + if (cpu.address_bus is >= 0x4000 and < 0x4020) { ret = ReadReg(addr); } @@ -1119,6 +1089,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public void WriteMemory(ushort addr, byte value) { + if (!oam_dma_exec) + { + cpu.address_bus = addr; + } if (addr < 0x0800) { ram[addr] = value; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs index a448bd8a9d..4238fa99d7 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs @@ -28,13 +28,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES ser.Sync(nameof(oam_dma_byte), ref oam_dma_byte); ser.Sync(nameof(dmc_dma_exec), ref dmc_dma_exec); ser.Sync(nameof(dmc_realign), ref dmc_realign); - ser.Sync(nameof(reread_trigger), ref reread_trigger); - ser.Sync(nameof(do_the_reread_2002), ref do_the_reread_2002); - ser.Sync(nameof(do_the_reread_2007), ref do_the_reread_2007); - ser.Sync(nameof(do_the_reread_cont_1), ref do_the_reread_cont_1); - ser.Sync(nameof(do_the_reread_cont_2), ref do_the_reread_cont_2); - ser.Sync(nameof(reread_opp_4016), ref reread_opp_4016); - ser.Sync(nameof(reread_opp_4017), ref reread_opp_4017); // VS related ser.Sync("VS", ref _isVS); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.cs index ea679148df..8b62eba3b3 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.cs @@ -258,7 +258,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES ser.Sync("Spr_zero_in_Range", ref sprite_zero_in_range); ser.Sync(nameof(is_even_cycle), ref is_even_cycle); ser.Sync(nameof(soam_index), ref soam_index); - ser.Sync(nameof(soam_m_index), ref soam_m_index); ser.Sync(nameof(oam_index), ref oam_index); ser.Sync(nameof(oam_index_aux), ref oam_index_aux); ser.Sync(nameof(soam_index_aux), ref soam_index_aux); @@ -438,6 +437,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES nes.Board.ClockPpu(); } _totalCycles += 1; + nes.cpu.ext_ppu_cycle += 1; } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs index 9b695877a3..1018af6566 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs @@ -344,25 +344,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES private byte read_2002() { byte ret = peek_2002(); - /* - if (nes.do_the_reread_2002 > 0) - { - if (Reg2002_vblank_active || Reg2002_vblank_active_pending) - Console.WriteLine("reread 2002"); - } - */ // reading from $2002 resets the destination for $2005 and $2006 writes vtoggle = false; Reg2002_vblank_active = 0; Reg2002_vblank_active_pending = false; - if (nes.do_the_reread_2002 > 0) - { - ret = peek_2002(); - // could be another reread, but no other side effects, so don't bother - } - // update the open bus here ppu_open_bus = ret; PpuOpenBusDecay(DecayType.High); @@ -443,6 +430,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES { // glitchy increment of OAM index oam_index += 4; + oam_index &= 0x1FC; + reg_2003 += 4; + reg_2003 &= 0xFC; } else { @@ -681,7 +671,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES case 6: return read_2006(); case 7: { - if (nes.cpu.TotalExecutedCycles == double_2007_read) + if (nes.cpu.TotalExecutedCycles == double_2007_read && !nes.dmc_dma_exec) { return ppu_open_bus; } @@ -690,13 +680,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES ret_spec = read_2007(); double_2007_read = nes.cpu.TotalExecutedCycles + 1; } - - if (nes.do_the_reread_2007 > 0) - { - ret_spec = read_2007(); - ret_spec = read_2007(); - // always 2? - } return ret_spec; } default: throw new InvalidOperationException(); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs index 9f93dbd297..c2cef0f937 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs @@ -24,7 +24,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public byte read_value; public int soam_index; public int soam_index_prev; - public int soam_m_index; public int oam_index; public int oam_index_aux; public int soam_index_aux; @@ -219,7 +218,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES spr_true_count = 0; soam_index = 0; - soam_m_index = 0; oam_index_aux = 0; oam_index = 0; is_even_cycle = true; @@ -297,10 +295,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES if (is_even_cycle) { - if ((oam_index + soam_m_index) < 256) - read_value = OAM[oam_index + soam_m_index]; - else - read_value = OAM[oam_index + soam_m_index - 256]; + read_value = OAM[oam_index & 0xFF]; } else if (sprite_eval_write) { @@ -316,21 +311,24 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES { //a flag gets set if sprite zero is in range if (oam_index == reg_2003) { sprite_zero_in_range = true; } - + oam_index++; spr_true_count++; - soam_m_index++; } else if (spr_true_count > 0 && spr_true_count < 4) { - soam[soam_index * 4 + soam_m_index] = read_value; - - soam_m_index++; + soam[soam_index * 4 + spr_true_count] = read_value; + oam_index++; spr_true_count++; if (spr_true_count == 4) { - oam_index += 4; soam_index++; + // The X coordinate makes the same checks as the Y position to see if "read_value" is in range of the scanline. + if (!(yp >= read_value && yp < read_value + spriteHeight) && spr_true_count == 4) + { + // and if it isn't, clear the lower 2 bits of the OAM index. + oam_index &= 0x1FC; // This is an integer instead of a byte. Bit 8 is used to check if the OAM address overflows. + } if (soam_index == 8) { // oam_index could be pathologically misaligned at this point, so we have to find the next @@ -338,13 +336,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES oam_index_aux = (oam_index % 4) * 4; } - soam_m_index = 0; spr_true_count = 0; } } else { + // This object is out of the range of the scanline oam_index += 4; + oam_index &= 0x1FC; } } else @@ -357,30 +356,24 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES if (yp >= read_value && yp < read_value + spriteHeight && spr_true_count == 0) { spr_true_count++; - soam_m_index++; } else if (spr_true_count > 0 && spr_true_count < 4) { - soam_m_index++; - + oam_index++; spr_true_count++; if (spr_true_count == 4) { - oam_index += 4; + if (!(yp >= read_value && yp < read_value + spriteHeight) && spr_true_count == 4) + { + oam_index &= 0x1FC; + } soam_index++; - soam_m_index = 0; spr_true_count = 0; } } else { - oam_index += 4; - if (soam_index == 8) - { - soam_m_index++; // glitchy increment - soam_m_index &= 3; - } - + oam_index += 5; // glitchy increment } read_value = soam[0]; //writes change to reads diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubNESHawk/SubNESHawk.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubNESHawk/SubNESHawk.IEmulator.cs index a190ac9e8a..2f9242c8b3 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubNESHawk/SubNESHawk.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SubNESHawk/SubNESHawk.IEmulator.cs @@ -111,6 +111,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SubNESHawk private void DoFrame(IController controller) { + _nesCore.cpu.ext_ppu_cycle = 0; // Reset this value at the beginning of each frame stop_cur_frame = false; while (!stop_cur_frame) { @@ -121,7 +122,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SubNESHawk } _nesCore.do_single_step(controller, out pass_new_input, out pass_a_frame); current_cycle++; - _nesCore.cpu.ext_ppu_cycle = current_cycle; stop_cur_frame |= pass_a_frame; stop_cur_frame |= pass_new_input; }