What exactly is the behavior of $2003/$2004 on real NES hardware?
Any documentation that I've come across claims that you write the SPR-RAM address to $2003, and the byte you want to store to $2004. Okay -- that seems REALLY straightforward. But guess what? Clearly not everyone agrees on it.
Okay. Here's the situation:
I want to perform a DMA to SPR-RAM and then hide certain sprites by using $2003/$2004 and overwriting their Y-coordinates. I'm not using my entire "local" SPR-RAM page for sprites, and need to hide the sprites which overlap my other data.
Example:
I'm using FCE Ultra because it's the only emulator I've found that has reasonable debugging features. However, the way it responds to writes to $2003 is bizarre. I had to get into the source code to understand it, and here's the relevant portion:
If I understand the above right, you can only position ($2003) and write to ($2004) the first eight locations of SPR-RAM, which corresponds to the first two sprites (sprites #0 & #1). If I want to write to any other sprite, I must first write to address 7 (sprite #1's X-coordinate), then "make my way" to the "true" address (unless the sprite is even numbered and I want to access the Y-coordinate, in which case I'm there).
Example (doesn't work in FCE Ultra):
So, the quickest way to get to the intended address would be to:
Obviously unacceptable. So my question is, what happens on real hardware? I looked at the source for two other emulators (Nintendulator & Nestopia) and neither of them handle $2003 the way FCE Ultra does (they just accept the sprite address as is, as one would expect). I'd try it myself, but I don't have any development hardware and it looks like the PowerPak from RetroZone is temporarily unavailable.
Unfortunately, my project cannot move forward until I can resolve this. Can someone please respond?
Thanks in advance, Carl
Any documentation that I've come across claims that you write the SPR-RAM address to $2003, and the byte you want to store to $2004. Okay -- that seems REALLY straightforward. But guess what? Clearly not everyone agrees on it.
Okay. Here's the situation:
I want to perform a DMA to SPR-RAM and then hide certain sprites by using $2003/$2004 and overwriting their Y-coordinates. I'm not using my entire "local" SPR-RAM page for sprites, and need to hide the sprites which overlap my other data.
Example:
Code:
lda #0
sta $2003
sta $4014 ; SPR-RAM <- $0000-$00FF
ldx #$FF ; used to hide sprites (any value in $EF-$FF will do)
lda #35 * 4 ; sprite #35 @ Y-coordinate
sta $2003
stx $2004
.. (etc., for each sprite I want to hide)
sta $2003
sta $4014 ; SPR-RAM <- $0000-$00FF
ldx #$FF ; used to hide sprites (any value in $EF-$FF will do)
lda #35 * 4 ; sprite #35 @ Y-coordinate
sta $2003
stx $2004
.. (etc., for each sprite I want to hide)
I'm using FCE Ultra because it's the only emulator I've found that has reasonable debugging features. However, the way it responds to writes to $2003 is bizarre. I had to get into the source code to understand it, and here's the relevant portion:
Code:
static DECLFW(B2003)
{
//printf("$%04x:$%02x, %d, %d\n",A,V,timestamp,scanline);
PPUGenLatch=V;
PPU[3]=V;
PPUSPL=V&0x7;
}
static DECLFW(B2004)
{
//printf("$%04x:$%02x, %d, %d\n",A,V,timestamp,scanline);
//printf("$%04x:$%02x, %d\n",X.PC,V,scanline);
PPUGenLatch=V;
if(PPUSPL>=8)
{
if(PPU[3]>=8)
SPRAM[PPU[3]]=V;
}
else
{
//printf("$%02x:$%02x\n",PPUSPL,V);
SPRAM[PPUSPL]=V;
}
PPU[3]++;
PPUSPL++;
}
{
//printf("$%04x:$%02x, %d, %d\n",A,V,timestamp,scanline);
PPUGenLatch=V;
PPU[3]=V;
PPUSPL=V&0x7;
}
static DECLFW(B2004)
{
//printf("$%04x:$%02x, %d, %d\n",A,V,timestamp,scanline);
//printf("$%04x:$%02x, %d\n",X.PC,V,scanline);
PPUGenLatch=V;
if(PPUSPL>=8)
{
if(PPU[3]>=8)
SPRAM[PPU[3]]=V;
}
else
{
//printf("$%02x:$%02x\n",PPUSPL,V);
SPRAM[PPUSPL]=V;
}
PPU[3]++;
PPUSPL++;
}
If I understand the above right, you can only position ($2003) and write to ($2004) the first eight locations of SPR-RAM, which corresponds to the first two sprites (sprites #0 & #1). If I want to write to any other sprite, I must first write to address 7 (sprite #1's X-coordinate), then "make my way" to the "true" address (unless the sprite is even numbered and I want to access the Y-coordinate, in which case I'm there).
Example (doesn't work in FCE Ultra):
Code:
lda #$8C ; (= 35 * 4,address of sprite #35's Y-coordinate)
sta $2003 ; PPU[3] = $8C, PPUSPL = $04
stx $2004 ; SPRAM[$04] <- $FF (and not SPRAM[$8C] <- $FF)
sta $2003 ; PPU[3] = $8C, PPUSPL = $04
stx $2004 ; SPRAM[$04] <- $FF (and not SPRAM[$8C] <- $FF)
So, the quickest way to get to the intended address would be to:
Code:
lda #$87
sta $2003 ; PPU[3] = $87, PPUSPL = $07
stx $2004 ; SPRAM[$07] <- $FF (trash sprite #1's Y-coordinate)
stx $2004 ; SPRAM[$88] <- $FF (trash sprite #34's Y-coordinate)
stx $2004 ; SPRAM[$89] <- $FF (trash sprite #34's Tile Index)
stx $2004 ; SPRAM[$8A] <- $FF (trash sprite #34's Attribute)
stx $2004 ; SPRAM[$8B] <- $FF (trash sprite #34's X-coordinate)
stx $2004 ; finally, store out sprite #35's Y-coordinate
sta $2003 ; PPU[3] = $87, PPUSPL = $07
stx $2004 ; SPRAM[$07] <- $FF (trash sprite #1's Y-coordinate)
stx $2004 ; SPRAM[$88] <- $FF (trash sprite #34's Y-coordinate)
stx $2004 ; SPRAM[$89] <- $FF (trash sprite #34's Tile Index)
stx $2004 ; SPRAM[$8A] <- $FF (trash sprite #34's Attribute)
stx $2004 ; SPRAM[$8B] <- $FF (trash sprite #34's X-coordinate)
stx $2004 ; finally, store out sprite #35's Y-coordinate
Obviously unacceptable. So my question is, what happens on real hardware? I looked at the source for two other emulators (Nintendulator & Nestopia) and neither of them handle $2003 the way FCE Ultra does (they just accept the sprite address as is, as one would expect). I'd try it myself, but I don't have any development hardware and it looks like the PowerPak from RetroZone is temporarily unavailable.
Unfortunately, my project cannot move forward until I can resolve this. Can someone please respond?
Thanks in advance, Carl