Reset $2003 at scanline 238
Moderator: Moderators
Reset $2003 at scanline 238
Testing my new MMC1 implementation, I found some problems with "Dusty Diamond's All-Star Softball (U) [!]. nes". After the initial choices the rom hangs in an infinite loop. Tested with other emulators I noticed that there are the same problem. I understood what was the problem and now I know what you think. The rom does something like this:
1) writes 0x00 in $2003
2) writes 4 bytes in the OAM through the $2004 (setting sprite 0)
3) in the next video frame update the entire OAM using $4014 starting to write, however, from the the sprite 1 instead of 0.
In this way, the sprite 0 will always have a Y coordinate "0xFF" and then never gets hit and the game goes into an infinite loop waiting that bit 6 of $2002 is set.
It seems that before the writing of the $4014, the $2003 should be cleaned.
Following this logic, I tried to reset the $2003 at scanline 238 after the last evaluated sprite and the games started to work.
They also started to run even the chinese MMC3 rom that not working properly such as:
Aladdin 2 (Unl). Nes
Bing Kuang Ji Dan Zi - flighty Chicken (Ch). Nes
and many others
1) writes 0x00 in $2003
2) writes 4 bytes in the OAM through the $2004 (setting sprite 0)
3) in the next video frame update the entire OAM using $4014 starting to write, however, from the the sprite 1 instead of 0.
In this way, the sprite 0 will always have a Y coordinate "0xFF" and then never gets hit and the game goes into an infinite loop waiting that bit 6 of $2002 is set.
It seems that before the writing of the $4014, the $2003 should be cleaned.
Following this logic, I tried to reset the $2003 at scanline 238 after the last evaluated sprite and the games started to work.
They also started to run even the chinese MMC3 rom that not working properly such as:
Aladdin 2 (Unl). Nes
Bing Kuang Ji Dan Zi - flighty Chicken (Ch). Nes
and many others
- cpow
- NESICIDE developer
- Posts: 1097
- Joined: Mon Oct 13, 2008 7:55 pm
- Location: Minneapolis, MN
- Contact:
Re: Reset $2003 at scanline 238
Doesn't the OAM address get reset by the PPU somewhere in each scanline? I thought it used it during the sprite evaluation process which happens during pixels 0-255?
Re: Reset $2003 at scanline 238
Interesting. Cleaning the sprite address ($2003) before the VBlank was already discussed, but nobody got a definitive "yes" or a definitive "no".FHorse wrote:It seems that before the writing of the $4014, the $2003 should be cleaned. Following this logic, I tried to reset the $2003 at scanline 238 after the last evaluated sprite and the games started to work.
There's something regarding using the upper $2003 bits to write sprites #0 and #1 data, but I'm unsure of it.
EDIT: here's the old discussion (sort of).
Zepper
RockNES author
RockNES author
Just purely incidentally, after reading this, I ran across this page on the wiki, which mentions $2003 being cleared.
I guess there's still some controversy as to how $2003 behaves during rendering?
I guess there's still some controversy as to how $2003 behaves during rendering?
I discovered this long ago and thought it was well known... guess not? I clear it at the end of rendering on scanline 239. There are a few games that rely on this or else there's some subtle sprite errors.Drag wrote:Just purely incidentally, after reading this, I ran across this page on the wiki, which mentions $2003 being cleared.
I guess there's still some controversy as to how $2003 behaves during rendering?
I cannot remember which game it is now, but one game does strange stuff to 2003 so that sprite 0 is moved around (i.e. OAM entry 0 is NOT sprite 0). Huge Insect does this too. This is why it does not work on many emulators. The symptoms are the game starts fine, and the game screen works and everything... but no insects ever show up. This is because sprite 0 never hits, and that is caused by sachen "moving" sprite 0 by writing something to 2003 before sprite DMA'ing from what I recall.
The other game I'm thinking of (but unfortunately cannot remember the name of) does this too, but the game still works fine if you do not implement this. The problem is many sprites disappear due to them having more than 8 on a scanline. They did it to cycle the sprites without actually moving around the OAM entries like most other games did.
It appears that whatever sprite 2003 is pointing at will be sprite 0 as far as the rendering hardware is concerned. Since 2003 is reset at the end of rendering (or thereabouts), it usually is OAM entry 0. I do not know what happens if you set a non-MOD 4 value into 2003 before rendering though. i.e. if you load 01h 02h, or 03h into 2003. It's possible that the lower 2 bits are cleared before evaluation occurs but I don't know.
The actual OAM counter is composed of TWO separate counters on the PPU. The lower 2 bits is one counter, and the upper 6 are another counter. This is known because of how the OAM sprite overflow flag bug works. The state machine accidentally increments both counters at the same time instead of just the upper one after 8 sprites are rendered.
/* this is a comment */
Huge Insect works fine on nemulator too. I wonder if I'm doing something else incorrectly...kevtris wrote:Huge Insect does this too. This is why it does not work on many emulators. The symptoms are the game starts fine, and the game screen works and everything... but no insects ever show up.
get nemulator
http://nemulator.com
http://nemulator.com
The OAM address does get cleared *somewhere*, so it seems that the myth is a myth. I'm not sure if it's at the end of the scanline, or at the end of the frame, but here's a simple test I wrote:
ROM: http://kkfos.aspekt.fi/downloads/2003-test.zip
Correct output (based on my PAL NES) is:
Nintendulator prints "04", Nestopia prints "00" and FCEUX prints "88" (doesn't support OAM readback?)
EDIT: Forgot to initialize "i" to 0 in the OAM init code, however this shouldn't change the behaviour because the reset code initializes the RAM.
Code: Select all
#include <knes.h>
#pragma bss-name(push, "ZEROPAGE")
#pragma data-name(push, "ZEROPAGE")
void hang(void)
{
while ( 1 );
}
const char* text;
void write_text(void)
{
poll_vblank();
// Just to make sure the writes won't occur outside vblank...
PPU.mask = BGREND_OFF;
// Copy the text to $21C4.
PPU_ADDR( 0x21C4 );
while ( *text )
{
PPU.data = *text++;
}
// Hopefully we're still in vblank. :)
PPU_SCROLL( 0, 0 );
PPU.mask = BGREND_ON;
}
byte hexnum;
void write_hexnum(void)
{
static const char* hex_lut = "0123456789ABCDEF";
poll_vblank();
// Just to make sure the writes won't occur outside vblank...
PPU.mask = BGREND_OFF;
// Copy the text to $21E4.
PPU_ADDR( 0x21E4 );
PPU.data = hex_lut[ hexnum >> 4 ];
PPU.data = hex_lut[ hexnum & 0xF ];
// Hopefully we're still in vblank. :)
PPU_SCROLL( 0, 0 );
PPU.mask = BGREND_ON;
}
void set_palette(void)
{
// Palette is initially initialized to black by KNES.
poll_vblank();
// Set the second palette color to white.
PPU_ADDR( PPU_BG_PALETTE + 1 );
PPU.data = 0x30; // White
}
void test1(void)
{
// Test #1:
// Test if $2003 is cleared at the end of the frame.
// Disable rendering.
PPU.mask = 0;
// Initialize the OAM to numbers 0-255.
{
byte i = 0;
byte *p = ( byte * )OAM;
do
{
*p++ = i;
}
while ( ++i != 0 );
poll_vblank();
OAM_DMA();
}
// Set the OAM address.
poll_vblank();
PPU.oam_addr = 0x4;
// Enable rendering.
PPU.mask = BGREND_ON;
// Wait for the vblank (assuming the flag gets cleared during the frame).
poll_vblank();
// If the hypothesis is true, $2003 should now be 0.
// We can determine the value by reading from OAM.
hexnum = PPU.oam_data; write_hexnum();
text = "TEST 1 READY"; write_text();
}
int main(void)
{
set_palette();
text = "READY"; write_text();
test1();
// Wait for the judgement day.
hang();
return 0;
}
Correct output (based on my PAL NES) is:
Code: Select all
TEST 1 READY
00
EDIT: Forgot to initialize "i" to 0 in the OAM init code, however this shouldn't change the behaviour because the reset code initializes the RAM.
Last edited by thefox on Mon Jul 11, 2011 11:16 pm, edited 2 times in total.
- cpow
- NESICIDE developer
- Posts: 1097
- Joined: Mon Oct 13, 2008 7:55 pm
- Location: Minneapolis, MN
- Contact:
Weird, this is what I get too, yet I hang at the Akira(J) Start/Continue screen and I don't get any insects in Huge Insect (I waited at least five minutes for one to show up).thefox wrote:ROM: http://kkfos.aspekt.fi/downloads/2003-test.zip
Correct output (based on my PAL NES) is:Code: Select all
TEST 1 READY 00
I also forgot that OAM data readback isn't reliable, however this shouldn't matter on PAL NES as the OAM bytes 0-31 seem to be stable (based on blargg's earlier tests). It might produce strange results on NTSC NES though, depending on CPU-PPU synchronization. Can anybody try it out?
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
I'm getting some *very* strange results when testing this on my PAL NES. Remember that PAL VBL is 70 scanlines long.
If I write a value to OAM addr, and read back (using $2004) during the first ~20 or so scanlines of the vblank, it returns the expected value. However, if I read after that point, the OAM addr seems to have changed (and the value depends on how many cycles after that point I read it). It's almost as if the PAL PPU starts evaluating sprites while still in vblank?
Also in the test I posted earlier, if I change the oam_addr init value to something like 0x69, it ends up as 0x80 after the end of the frame instead of 0. So it seems to be incorrect to say (per se) that the OAM addr is cleared at some point in the frame. It's more likely that it gets cleared (under some conditions only!) as a result of the scanline sprite evaluation logic (which uses the same register).
Take this with a grain of salt, there are so many factors in play (like OAM readback corruption) that it's easy to make mistakes. I need to run some more tests later. And finally get me an NTSC NES.
If I write a value to OAM addr, and read back (using $2004) during the first ~20 or so scanlines of the vblank, it returns the expected value. However, if I read after that point, the OAM addr seems to have changed (and the value depends on how many cycles after that point I read it). It's almost as if the PAL PPU starts evaluating sprites while still in vblank?
Also in the test I posted earlier, if I change the oam_addr init value to something like 0x69, it ends up as 0x80 after the end of the frame instead of 0. So it seems to be incorrect to say (per se) that the OAM addr is cleared at some point in the frame. It's more likely that it gets cleared (under some conditions only!) as a result of the scanline sprite evaluation logic (which uses the same register).
Take this with a grain of salt, there are so many factors in play (like OAM readback corruption) that it's easy to make mistakes. I need to run some more tests later. And finally get me an NTSC NES.
I've confirmed this. Doing OAM DMA on PAL NES more than ~20 or so (didn't time this exactly) scanlines into vblank will *not* copy the sprites over properly.thefox wrote:If I write a value to OAM addr, and read back (using $2004) during the first ~20 or so scanlines of the vblank, it returns the expected value. However, if I read after that point, the OAM addr seems to have changed (and the value depends on how many cycles after that point I read it). It's almost as if the PAL PPU starts evaluating sprites while still in vblank?
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Yes.tepples wrote:Does this newly discovered OAM copy failure also happen if you have forced blank on?
This I couldn't reproduce for whatever reason. Strange.Also in the test I posted earlier, if I change the oam_addr init value to something like 0x69, it ends up as 0x80 after the end of the frame instead of 0.