13 Commits
Author SHA1 Message Date
FiskbitandSour b82eb7b213 NES: Improve OAM corruption accuracy. (#122)
* NES: Improve OAM corruption accuracy.

Our understanding of OAM row corruption has significantly improved since Mesen's implementation of the feature that was based on oam_flicker_test.nes results. This change improves the accuracy of the two OAM corruption emulation options.

Primary OAM (OAM1) and secondary OAM (OAM2) live in the same block of memory, made up of 9-byte rows where the first 8 bytes are consecutive OAM1 bytes and the last byte is an OAM2 byte. The PPU accesses OAM on every dot. On some dots, it uses the OAM1 address ($2003), and on other dots, it uses its internal OAM2 address. If the address changes during an OAM access (the 2nd half of a dot) and this causes the selected row to change, then it can trigger either analog corruption (the new row gets 'random' data) or a row copy (the old row's 9 bytes are copied to the new row). More information can be found at nesdev.org/wiki/OAM_internals.

Address selection is fairly simple. When rendering is off, OAM1ADDR is always selected. When rendering is on, OAM1ADDR is selected on odd dots in 1-256, and otherwise OAM2ADDR is selected.

Corruption occurs on the NES in 3 situations:
1. Writing to $2003 on NTSC PPUs, because writes are not synchronized at all and so the CPU open bus value and the new value can both cause the row to change mid-access. Depending on the timing, this can do nothing, cause analog corruption, or cause row copy. This was fixed in PAL PPUs, but we don't know about Dendy.
2. Toggling rendering on an OAM2ADDR dot. Depending on the direction and the timing, this can do nothing or cause analog corruption of or row copy to the new row.
3. On 2C02E and later PPUs, having a row mismatch if rendering is enabled at the start of pre-render. This always causes a row copy from the OAM1ADDR row to the OAM2ADDR row and is required for Huge Insect to spawn enemies in level 1. (2C02C PPUs have some corruption in this situation, too, but it is not yet understood.)

This change adds support for OAM corruption in situations 1 and 2 using the "Enable PPU $2003/rendering-toggle OAM corruption" setting, which is off by default. This does not take alignment into account at all and only does row copy, not analog corruption. Recommended for developers, but probably undesirable for actual users. This setting was previously called "Enable PPU OAM row corruption emulation". Note that situation 1 ($2003 writes) is not perfectly matching blargg's oam_read test results in the most vulnerable alignment yet, suggesting there is more research to be done on this.

Situation 3 is now supported under the "Disable PPU frame-start OAM corruption (2C02D and earlier)" setting, so this corruption happens by default. Mesen was already doing some corruption here to support Huge Insect, with the previous version of this setting being called "Disable PPU OAMADDR bug emulation". These changes make the corruption more accurate and apply in more situations, so there is a chance it will break some user software, but because it is required for a game, we've opted to have it on by default. This feature should match hardware very closely, as it doesn't have the timing and analog aspects seen in situations 1 and 2.

To support these, sprite rendering was reworked a bit to more accurately manage the OAM1 and OAM2 addresses during rendering. This should make sprite rendering behavior more accurate when rendering is toggled mid-frame, although there are still inaccuracies that come up if you do this. The OAM2 address can also now be seen in the register viewer below $2003.

This was tested against a suite of NES tests, AccuracyCoin $2004 Stress Test, and oam_flicker_test.nes/oam_flicker_test_reenable.nes. The $2003-based corruption causes a few tests to change behavior when enabled (which is expected), but oam_read and related tests are not failing exactly right yet. Tests otherwise pass.

* Changes based on code review.

* Removed _spriteAddrL/_spriteAddrH members, use local variables instead.

* Simplify firstVisibleSpriteAddr assignment

---------

Co-authored-by: Sour <[email protected]>
2026-05-19 03:17:34 -07:00
Sour 8563162da4 Debugger: NES - Various event viewer fixes
-Fixed incorrect ntsc border colors in some scenarios
-Fixed incorrect color/filtering for DMA writes (2004)
-Added "first register write" note to 2005/6 writes
-Fixed mapper reads using mapper write settings
2022-09-27 19:38:24 -04:00
Sour 152abf690b Rename stdafx.h to pch.h 2022-09-08 21:29:52 -04:00
Sour 8354f48357 NES: Rename some functions/variables to match nomenclature used in the rest of the code 2022-07-24 20:31:19 -04:00
Sour 60889176b7 Debugger: Added "draw partial frame" option 2022-06-26 21:57:03 -04:00
Sour aedea1862f NES: PPU - Fixed color emphasis not being applied correctly 2022-02-09 17:13:15 -05:00
Sour 496eaddc64 Debugger: Event Viewer - Fixed issues with NTSC border color for NES (fixes Metal Storm border color at the bottom) 2022-01-30 23:31:53 -05:00
Sour 2922b1ccfe Debugger: Refactor NES state updates and improved UI 2022-01-15 18:09:26 -05:00
Sour 6146019f6d Debugger: Allow changing NES state (wip) 2022-01-15 12:13:24 -05:00
Sour e5a2388911 Register viewer 2021-07-01 21:55:28 -04:00
Sour ff825fc4de Tilemap viewer 2021-06-22 20:12:37 -04:00
Sour 77d4fe1696 Add missing options to event viewers (ntsc borders, dma channel filters) 2021-06-17 21:50:45 -04:00
Sour a0a06e15a4 Event viewer 2021-06-09 21:51:03 -04:00