PPU Blarg tests

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
jotac
Posts: 12
Joined: Wed Feb 17, 2021 1:08 pm

PPU Blarg tests

Post by jotac » Fri Mar 26, 2021 6:20 pm

Hello,

I was trying out the "sprite_ram.nes" test from the blarg PPU test suite and I am getting error 0x07 which states:

"$4014 DMA copy should start at the value in $2003 and wrap"

So, the test does two OAM DMA transfers. In the first, the OAM ADDR($2003) is 0 and the page set is 2. So my DMA is reading from 0x200..=0x2FF and storing it in the OAM memory.

In the second test, the OAMADDR is 1. The test says it should "wrap". So I suppose it should go from 0x201..=0x2FF and then 0x201 again? or 0x200? either way, I tried both and both seem to fail so I might be understanding this wrong.

If anyone passes the test, can you print out and post your addresses (from where you are reading) for each OAM mem byte (the 256 bytes) ?

Thx in advance :)

Joe
Posts: 460
Joined: Mon Apr 01, 2013 11:17 pm

Re: PPU Blarg tests

Post by Joe » Fri Mar 26, 2021 7:02 pm

OAMDMA ($4014) sets the high byte of the source address. The low byte is always zero. OAMADDR ($2003) sets the destination address.

With OAMADDR set to 1, writing 2 to OAMDMA reads 255 bytes from 0x200 to 0x2FE and writes them to OAM 0x01 to 0xFF, then reads one byte from 0x2FF and writes it to OAM 0x00.

User avatar
Quietust
Posts: 1719
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: PPU Blarg tests

Post by Quietust » Fri Mar 26, 2021 7:33 pm

If you're implementing Sprite DMA as some sort of memcpy() between the CPU RAM and Sprite RAM, then you're doing it wrong.

Code: Select all

SprPage = <value written to $4014>;
for (int i = 0; i < 256; i++)
{
    int val = Read((SprPage * 256) + i);
    Write(0x2004, val);
}
The easiest way to implement Sprite DMA is by doing the above, because that's literally what the RP2A03 itself does.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

jotac
Posts: 12
Joined: Wed Feb 17, 2021 1:08 pm

Re: PPU Blarg tests

Post by jotac » Sat Mar 27, 2021 6:36 pm

Thank you both, I was not understanding what wrapping meant. Works like a charm now. Thanks!

jotac
Posts: 12
Joined: Wed Feb 17, 2021 1:08 pm

Re: PPU Blarg tests

Post by jotac » Sun Mar 28, 2021 6:37 am

Quietust wrote:
Fri Mar 26, 2021 7:33 pm
If you're implementing Sprite DMA as some sort of memcpy() between the CPU RAM and Sprite RAM, then you're doing it wrong.

Code: Select all

SprPage = <value written to $4014>;
for (int i = 0; i < 256; i++)
{
    int val = Read((SprPage * 256) + i);
    Write(0x2004, val);
}
The easiest way to implement Sprite DMA is by doing the above, because that's literally what the RP2A03 itself does.
This worked great.

Still on the PPU tests that blarg wrote, on the vram_access.nes I am getting:
"6) Palette read should also read VRAM into read buffer"

Now, currently when the palette is read I return the data on that same cycle but I don't understand what it means for the buffer to have the VRAM, since I am already doing that I think:

Code: Select all

let mut data = self.buffer;
self.buffer = self.bus.read(u16::from(self.vram_address));

if u16::from(self.vram_address) >= 0x3F00 {
     data = self.buffer
};

let increment = if self.control.increment_mode { 32 } else { 1 };
self.vram_address = (u16::from(self.vram_address) + increment).into();

return data;

User avatar
Quietust
Posts: 1719
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: PPU Blarg tests

Post by Quietust » Sun Mar 28, 2021 9:54 am

jotac wrote:
Sun Mar 28, 2021 6:37 am
Still on the PPU tests that blarg wrote, on the vram_access.nes I am getting:
"6) Palette read should also read VRAM into read buffer"

Now, currently when the palette is read I return the data on that same cycle but I don't understand what it means for the buffer to have the VRAM, since I am already doing that I think:

Code: Select all

let mut data = self.buffer;
self.buffer = self.bus.read(u16::from(self.vram_address));

if u16::from(self.vram_address) >= 0x3F00 {
     data = self.buffer
};

let increment = if self.control.increment_mode { 32 } else { 1 };
self.vram_address = (u16::from(self.vram_address) + increment).into();

return data;
It looks as if you're storing the palette data within VRAM itself, which is incorrect - palette RAM is a small block of SRAM located inside the PPU chip, while VRAM $3F00-$3FFF is usually just a mirror of $2F00-$2FFF (in fact, all of $3000-$3FFF is usually a mirror of $2000-$2FFF).

When you point the VRAM address at $3F00-$3FFF and perform a read, the PPU immediately returns data from the palette RAM, and afterwards it reads nametable data from VRAM into the read buffer; if you then point the VRAM address at anywhere outside $3F00-$3FFF and read $2007, you can see that value.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

jotac
Posts: 12
Joined: Wed Feb 17, 2021 1:08 pm

Re: PPU Blarg tests

Post by jotac » Sun Mar 28, 2021 10:32 am

Thanks. I have a separate palette RAM yes. Taking what you said I hard-coded this hack:

Code: Select all

let mut data = self.buffer;
self.buffer = self.bus.read(u16::from(self.vram_address));

if u16::from(self.vram_address) >= 0x3F00 {
   data = self.buffer;
   // NOTICE THIS NEW LINE BELOW
   self.buffer = self.bus.read(u16::from(self.vram_address) - 0x1000);
};

let increment = if self.control.increment_mode { 32 } else { 1 };
self.vram_address = (u16::from(self.vram_address) + increment).into();
data
This seems to pass the test. But just so I get it, you are telling me the data in the palette ($3F00 to$3FFF) is usually a mirror of $2000-$2FFF ? But this is the nametable address space right?

User avatar
Quietust
Posts: 1719
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: PPU Blarg tests

Post by Quietust » Sun Mar 28, 2021 11:06 am

jotac wrote:
Sun Mar 28, 2021 10:32 am
This seems to pass the test. But just so I get it, you are telling me the data in the palette ($3F00 to$3FFF) is usually a mirror of $2000-$2FFF ? But this is the nametable address space right?
The palette isn't actually located at $3F00-$3FFF - it lives in its own special address space just like sprite RAM does. The PPU normally never reads from $3000-$3FFF on its own, but the nametable RAM is technically still mapped there so you can still read from it manually.

When you point the VRAM address at $3F00-$3FFF and write to $2007, it stores the data into the palette instead of putting it in VRAM. Similarly, when you read from $2007 it returns data from the palette instead of from the VRAM read buffer, but it still schedules a read from VRAM in order to update the VRAM read buffer.

For comparison, this is roughly what my emulator's code looks like for reading $2007:

Code: Select all

StartVramRead = true; // schedule a VRAM read, which will update 'buf2007' after a few cycles and increment the VRAM address
if (((VRAMAddr & 0x3F00) == 0x3F00) && !IsRendering)
{
	if (Reg2001 & 0x01)
		return Palette[VRAMAddr & 0x1F] & 0x30;
	else	return Palette[VRAMAddr & 0x1F];
}
else	return buf2007;
Similarly, for writing to $2007:

Code: Select all

if (((VRAMAddr & 0x3F00) == 0x3F00) && !IsRendering)
{
	int Addr = VRAMAddr & 0x1F;
	Value &= 0x3F;
	Palette[Addr] = Val;
	if (!(Addr & 0x3))
		Palette[Addr ^ 0x10] = Value;
	IncrementAddr(); // immediately increment VRAM address by 1 or 32
}
else
{
	VramWriteVal = Value;
	StartVramWrite = true; // schedule a VRAM write, which will store 'VramWriteVal' after a few cycles and increment the VRAM address
}
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

Post Reply