Reset $2003 at scanline 238

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

FHorse
Posts: 232
Joined: Sat May 08, 2010 9:31 am

Reset $2003 at scanline 238

Post by FHorse »

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
User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Re: Reset $2003 at scanline 238

Post by cpow »

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?
User avatar
Zepper
Formerly Fx3
Posts: 3262
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Re: Reset $2003 at scanline 238

Post by Zepper »

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.
Interesting. Cleaning the sprite address ($2003) before the VBlank was already discussed, but nobody got a definitive "yes" or a definitive "no".

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).
User avatar
James
Posts: 431
Joined: Sat Jan 22, 2005 8:51 am
Location: Chicago, IL
Contact:

Post by James »

These games work with nemulator; I never reset $2003.
get nemulator
http://nemulator.com
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Post by Drag »

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?
User avatar
kevtris
Posts: 504
Joined: Sat Oct 29, 2005 2:09 am
Location: Indianapolis
Contact:

Post by kevtris »

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 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.

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 */
User avatar
James
Posts: 431
Joined: Sat Jan 22, 2005 8:51 am
Location: Chicago, IL
Contact:

Post by James »

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.
Huge Insect works fine on nemulator too. I wonder if I'm doing something else incorrectly...
get nemulator
http://nemulator.com
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Post by thefox »

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:

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;
}
ROM: http://kkfos.aspekt.fi/downloads/2003-test.zip

Correct output (based on my PAL NES) is:

Code: Select all

TEST 1 READY
00
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.
Last edited by thefox on Mon Jul 11, 2011 11:16 pm, edited 2 times in total.
User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Post by cpow »

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
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).
User avatar
Zepper
Formerly Fx3
Posts: 3262
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper »

RockNES prints 04 too.
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Post by thefox »

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
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Post by thefox »

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.
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Post by thefox »

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?
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.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

So it might not be 70 lines of vblank and 1 line of pre-render as much as 20 lines of vblank and 51 lines of pre-render. Does this newly discovered OAM copy failure also happen if you have forced blank on?
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Post by thefox »

tepples wrote:Does this newly discovered OAM copy failure also happen if you have forced blank on?
Yes.
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.
This I couldn't reproduce for whatever reason. Strange.
Post Reply