NES PPU pixel accurate rendering problems

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
LuckyLuke
Posts: 6
Joined: Fri Feb 14, 2020 9:00 am

NES PPU pixel accurate rendering problems

Post by LuckyLuke » Wed Mar 11, 2020 10:31 am

Hi, I'm writing my NES emulator in C++, but I'm stuck implementing the PPU.
I 'm trying to implement pixel accurate rendering, but iI have some problems with timing.
If I display the nametable manually fetching the tiles and the palettes at the end of the frame I get a correct output:

Image

If I output the screen pixel by pixel it just shows garbage (kind of):

Image

I can provide the code if necessary.

I can't get it right...

Myself086
Posts: 18
Joined: Sat Nov 10, 2018 2:49 pm

Re: NES PPU pixel accurate rendering problems

Post by Myself086 » Wed Mar 11, 2020 8:48 pm

First thing I recommend fixing is the fine Y coordinate, it seems stuck on a constant value.

Coarse Y seems to have a shifting error, maybe 2 too many bits to the right.

Palette and X seem fine.

LuckyLuke
Posts: 6
Joined: Fri Feb 14, 2020 9:00 am

Re: NES PPU pixel accurate rendering problems

Post by LuckyLuke » Thu Mar 12, 2020 5:14 am

Myself086 wrote:
Wed Mar 11, 2020 8:48 pm
First thing I recommend fixing is the fine Y coordinate, it seems stuck on a constant value.

Coarse Y seems to have a shifting error, maybe 2 too many bits to the right.

Palette and X seem fine.
I double and triple checked the code and the fine Y seems to increment correctly and so does the coarse Y.
But it seems like you say, since the patterns are repeated two times and it seems to go outside the correct frame (the verical bands).
How can I debug it? It's been two days, I am missing something here..

Myself086
Posts: 18
Joined: Sat Nov 10, 2018 2:49 pm

Re: NES PPU pixel accurate rendering problems

Post by Myself086 » Thu Mar 12, 2020 8:29 am

You can post your code here.

The increment may be fine but how the Y value is used could be questioned. Have you tried placing a breakpoint on rendering the second scanline?

I just noticed that the nametable is repeated twice before showing the next nametable. So I'm assuming you're recalculating Y every single time. Tile address should be "0010 NNYY YYYX XXXX" where "NN" is always "YX" regardless of nametable dimensions (unlike SNES).

Don't be afraid to make more debugging tools or experiments. For example, draw a RGB representing either fine or coarse coordinates rather than the final color.

LuckyLuke
Posts: 6
Joined: Fri Feb 14, 2020 9:00 am

Re: NES PPU pixel accurate rendering problems

Post by LuckyLuke » Fri Mar 13, 2020 2:04 pm

Myself086 wrote:
Thu Mar 12, 2020 8:29 am
You can post your code here.

The increment may be fine but how the Y value is used could be questioned. Have you tried placing a breakpoint on rendering the second scanline?

I just noticed that the nametable is repeated twice before showing the next nametable. So I'm assuming you're recalculating Y every single time. Tile address should be "0010 NNYY YYYX XXXX" where "NN" is always "YX" regardless of nametable dimensions (unlike SNES).

Don't be afraid to make more debugging tools or experiments. For example, draw a RGB representing either fine or coarse coordinates rather than the final color.
I rewrote the PPU from scratch, and at first it was showing this:

Image

I don't know why the previous (identical) implementation was wrong, but now I have a starting point to debug the PPU.
After a whole day I found out that the issue is in the way I represent the internal VRAM address registers: bit-fields

Code: Select all

union
	{
		struct
		{
			uint16_t coarseX : 5;
			uint16_t coarseY : 5;
			uint16_t baseNametableAddressX : 1;
			uint16_t baseNametableAddressY : 1;
			uint16_t fineY : 3;
			uint16_t unused : 1;
		} bits;
		uint16_t reg;
	} addressT, addressV;   // temporary VRAM adddress register and VRAM address register
If I use uint8_t type for the fields and uint16_t for the register:

Code: Select all

union
	{
		struct
		{
			uint8_t coarseX : 5;
			uint8_t coarseY : 5;
			uint8_t baseNametableAddressX : 1;
			uint8_t baseNametableAddressY : 1;
			uint8_t fineY : 3;
			uint8_t unused : 1;
		} bits;
		uint16_t reg;
	} addressT, addressV;   // temporary VRAM adddress register and VRAM address register
incrementing a field (like address.bits.coarse++) generates a wrong reg field. So when I was fetching a tile ID from the nametable the address was off.
Now the rendering function is working fine, I don't know how the type of the bit field influences the whole register. I'm going to study that aspect of the language, because it was surely a really nasty bug!

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

Re: NES PPU pixel accurate rendering problems

Post by Quietust » Sat Mar 14, 2020 7:12 am

For what it's worth, in all of the places I've used bitfields in my own emulator (which is currently restricted to mappers), I've always specified types of "signed" or "unsigned" rather than types which imply specific widths (e.g. "unsigned coarseX : 5;").
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

turboxray
Posts: 58
Joined: Thu Oct 31, 2019 12:56 am

Re: NES PPU pixel accurate rendering problems

Post by turboxray » Sat Mar 14, 2020 11:15 am

Oh wow that's a nasty bug haha. Did you happen to debug and see what values it was producing??

LuckyLuke
Posts: 6
Joined: Fri Feb 14, 2020 9:00 am

Re: NES PPU pixel accurate rendering problems

Post by LuckyLuke » Thu Mar 26, 2020 4:00 am

turboxray wrote:
Sat Mar 14, 2020 11:15 am
Oh wow that's a nasty bug haha. Did you happen to debug and see what values it was producing??
It was storing adjacent bit fields without crossing allocation unit boundaries. I learned the hard way that bit fields are implementation (aka compiler) defined behavior, I'm using Visual Studio 2019. So using uint16_t It was storing different bitfields in different 16-bit allocation units, padding the rest. So the value was different when accessing the register as a 8-bit word than when accessing individual bit fields.
Hence a very nasty bug.

Post Reply