The NES (and AV Famicom) both ignore consecutive reads to 4016/4017 (when the reads on 2 consecutive CPU cycles). This implements that behavior when the corresponding console type is selected.
These changes also simplify the DMA code, because a lot of extra code was in the DMA logic to simulate the same result. Moving this logic to the 4016/4017 reads makes the code simpler and allows it to work properly when a regular instruction reads from 4016/4017 twice in a row, too.
On late-G and H CPUs, starting a 1-byte sample just before the end of a DMC period can cause the sample to be fetched and played twice. This change fixes 2 issues related to this.
First, new research found that this bug is because there is normally at least a 1-APU cycle gap between the end of one DMC DMA and the start of the next DMC DMA on earlier CPUs, but these later CPUs remove that gap, allowing the aborted DMA behavior to occur sooner such that it can't be aborted.
But this means it actually affects any sample that is started at the end of a DMC period, not just ones that trigger the extra DMA. Mesen was previously only doing this in the case of 1-byte non-looping samples. This change fixes this issue. This was validated with AccuracyCoin's Implicit DMA Abort test and with dmc_dma_stop_test_v3 test F.
Second, when there are two DMC DMA's back to back while reading a joypad register, both of them should cause an extra clock of the joypads when using NES behavior (unless suppressed by the APU register activation bug). However, we were only seeing extra clocks with HVC-001 behavior (which is handled on every DMA cycle always) because Mesen only runs its halt cycle logic for the first of the two DMAs. This change makes ProcessPendingDma call itself in the case where we handle DMC DMA back to back so it can do this extra joypad clock if need be.
This second change was validated with dmc_dma_start_test_v2 test C, dmc_dma_implicit_stop_level_test, and a variant of apu_register_activation_test.
When the CPU is reading from $4000-$401F and DMA occurs, the DMA can cause 2A03 registers to erroneously activate. When joypad registers are activated in this way, this causes a bus conflict between the driven joypad bits and the DMA value. The DMA unit sees the result of this conflict and uses it.
However, if you inspect external open bus afterward, you do not see this value. This is because conflicting devices stop driving the bus at different times, so the last device to drive a bit wins on that bit for external open bus. Joypads stop driving later than the cartridge does, so its bits win.
This change fixes it so the DMA still sees the conflicting value, but the resulting open bus value uses the driven joypad bits, instead.
This fixes AccuracyCoin test "DMC DMA Bus Conflicts".
This change also makes it so reads that *don't* activate readable 2A03 registers can still read values from $4000-$4014 and $401B-$401F (ignoring the readable normal and test mode register addresses). In particular, this means OAM DMA can read $4011 from mappers that support IPCM.
This could cause DMC DMA to happen on the wrong cycle parity, causing issues with code that uses sprite DMA to align 4016 reads to prevent DMC DMA from corrupting the read value.
* Code: Add auto-formatting rules for C++ projects (clang-format) and enforce code style via "clang-format" and "dotnet format"
* fix ui format warnings
When reading from $4015, the internal and external CPU buses are isolated. This means the CPU reads the value from the internal $4015 register, which is not repeated onto the external bus, and any response to the read from an external device is not seen by the CPU (not even in the form of a bus conflict).
As a result, the internal $4015 result updates only the internal open bus value, and any external result updates only the external open bus value. If the buses are isolated again on the next CPU cycle, then open bus bits on the internal bus read will not be affected by the external open bus value.
This has been verified in a new test that lands a DMA-that-activates-$4015 on a $4015 read, so the two buses diverge on the DMA read cycle and $4015 is then read by the CPU on the next cycle to verify that the internal open bus was not affected by the DMA read. Furthermore, if the CPU then reads from external open bus afterward, it gets the DMA value from external open bus, which persisted but wasn't previously accessible due to bus isolation.
Mesen previously had a bug where its GetInternalOpenBus function was returning the wrong bus value (_externalOpenBus instead of _internalOpenBus). Fixing this was not enough to fix the test, because SetOpenBus would update both buses on any external read. This change fixes the GetInternalOpenBus bug and changes SetOpenBus to be able to update either bus independently.
This was tested with the new AccuracyCoin "Internal Data Bus" test.
Predictive breakpoints use a dummy CPU for prediction, and that dummy CPU needs to not cause side effects. However, there was no way to peek at the state of the frame counter IRQ, so prediction could cause this flag to be unexpectedly cleared.
This change adds peeking. It has a limitation in that it may mispredict the value of the flag on double reads and break when it shouldn't.
This change also fixes a related issue on PAL with dummy CPUs processing DMA.
Thanks to Sour for identifying the code for both issues.
Many 6502 instructions read from memory and discard the value, called dummy reads. Mesen implemented many of these with a DummyRead function that reads from PC. However, many of these should actually read from the stack, instead. This change changes DummyRead to DummyPcRead, adds a DummyStackRead function, and fixes instructions to use the stack version where relevant.
Tested: No change in current AccuracyCoin. No change in Mesen automated test results.
Jumping to an open bus address with JSR should leave the MSB of the target address on the data bus, not the LSB of the current PC which is the last write to the stack
The CPU will read 4016/4017 for 2 consecutive cycles, which the controllers will see a single read (except on the original famicom), so skip the dummy read to reproduce this behavior