Making a basic PPU (Fully working CPU)

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

nuker
Posts: 11
Joined: Tue Apr 25, 2017 1:10 pm

Re: Making a basic PPU (Fully working CPU)

Post by nuker »

tokumaru wrote:I believe it has been pointed out that SMB isn't a good test subject for a barebones PPU implementation. It relies on raster effects (status bar), pallette mirroring, VRAM/ROM readback, scrolling... Better start with something simpler, like Donkey Kong or Mario Bros., which have already been suggested.

It doesn't mean you will get nothing out of SMB, as even the simple approach to PPU rendering should show something discernible for SMB, it's just that it may be harder to debug the initial PPU implementation using a game that relies on more advanced PPU features. There certainly is something wrong there, but the problem may be easier to catch if you try a less demanding game.

EDIT: Those patterns do resemble the pattern tables of SMB, but I don't think there's any reason for them to show up like that in the final emulated picture.
I tried other ROM's but a similar thing happens. I moreso wanted to know if there was a plausible or easy-to-spot explanation for why this is happening. Here is a screenshot from Donkey Kong.
donkeyKong.JPG
donkeyKong.JPG (25.61 KiB) Viewed 2648 times
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Making a basic PPU (Fully working CPU)

Post by Pokun »

Are drawing the nametable? It looks more like you are drawing the pattern table, although corrupted. It looks like they are not properly aligned.

Also the second pattern table is usually used as background tiles, not the first one. Both Donkey Kong and Mario Bros are doing that.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Making a basic PPU (Fully working CPU)

Post by tokumaru »

Pokun wrote:Also the second pattern table is usually used as background tiles, not the first one. Both Donkey Kong and Mario Bros are doing that.
Better honor the $2000 setting for this than hardcode it. A lot of games do it the other way.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Making a basic PPU (Fully working CPU)

Post by Pokun »

Yes of course, eventually you'll need to do that. But if you just want to get Donkey Kong to display for now, it sounds like you got them backwards.
nuker
Posts: 11
Joined: Tue Apr 25, 2017 1:10 pm

Re: Making a basic PPU (Fully working CPU)

Post by nuker »

Pokun wrote:Are drawing the nametable? It looks more like you are drawing the pattern table, although corrupted. It looks like they are not properly aligned.

Also the second pattern table is usually used as background tiles, not the first one. Both Donkey Kong and Mario Bros are doing that.
Thanks for the suggestion. It is very possible I have either made an arithmetic mistake or misunderstood something. Here is what I am doing in pseudocode:


Code: Select all

//Go through each row and column of nametable
for(row<30) {
    for(col<32) {
         //Fetch nametable, attrTable, and pattern
         nameTable = read(0x2000+col+(32*row));
         attrTable = read(0x2C30+(col/4)+(row*8));
         patternTableAddr = 16*nameTable + (((ctrl>>4)&1)*0x1000);

         //Draw pattern Table entry
         for(i<8) {
             for(bit<8) {
                 currentPixel = extractCurrentBitFromPatternTable(patternTableAddr,bit);
                 // only draw background pixels
                 if(currentPixel ==0) {
                       pixelColor = palette[ram[0x3F00]];
                       screen[8*row+i][8*col+bit] = pixelColor;
                  }
             }
         }

    }
}
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Making a basic PPU (Fully working CPU)

Post by lidnariq »

What writes to screen if currentPixel is not 0?
How does extractCurrentBitFromPatternTable work?
nuker
Posts: 11
Joined: Tue Apr 25, 2017 1:10 pm

Re: Making a basic PPU (Fully working CPU)

Post by nuker »

lidnariq wrote:What writes to screen if currentPixel is not 0?
How does extractCurrentBitFromPatternTable work?
Right now i am only drawing background pixels in order to just see if the ppu works at all. That way i dont have to worry about sprites or different palettes other than the background color. Therefore it is just white where i dont write and grey where i do write.

Sorry, i was too lazy to write out extractCurrentBits. It just gets one bit from pattern table address and the second from that address + 8 and combines them
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Making a basic PPU (Fully working CPU)

Post by lidnariq »

Well, anyway, row*8 is not equal to floor(row/4)*8.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Making a basic PPU (Fully working CPU)

Post by tokumaru »

I didn't catch anything that would result in the output you showed us, but I did notice a few problems:

- The attribute table base address is wrong;
- The attribute data is not used at all;
- extractCurrentBitFromPatternTable doesn't use i;

I rewrote a few formulas and completed some things that were missing (attributes, mostly), hopefully I didn't make any horrible mistakes:

Code: Select all

for (row < 30) {
	for (col < 32) {
		nameTable = read(0x2000 + (row << 5) + col);
		attrTable = read(0x23c0 + ((row >> 2) << 3) + (col >> 2));
		attribute = (attrTable >> (((row & 0x02) << 1) + (col & 0x02))) & 0x03;
		patternTableAddr = (nameTable << 4) + ((ctrl & 0x10) << 8);
		
		for (i < 8) {
			for (bit < 8) {
				currentPixel = ((pattern[patternTableAddr + i] >> (7 - bit)) & 0x01) + (((pattern[patternTableAddr + i + 8] >> (7 - bit)) & 0x01) << 1);
				pixelColor = palette[(attribute << 2) + currentPixel];
				screen[(row << 3) + i][(col << 3) + bit] = pixelColor;
			}
		}
	}
}
There's another thing I'd like to get out of the way: Are you separating CPU RAM from PPU RAM? People are sometimes confused by the fact that there are 2 separate buses, and if you don't emulate this correctly there's no way that the various tables will be populated and read correctly. I'm particularly worried about your read() function... is it reading from VRAM?
nuker
Posts: 11
Joined: Tue Apr 25, 2017 1:10 pm

Re: Making a basic PPU (Fully working CPU)

Post by nuker »

tokumaru wrote:I didn't catch anything that would result in the output you showed us, but I did notice a few problems:

- The attribute table base address is wrong;
- The attribute data is not used at all;
- extractCurrentBitFromPatternTable doesn't use i;

I rewrote a few formulas and completed some things that were missing (attributes, mostly), hopefully I didn't make any horrible mistakes:

Code: Select all

for (row < 30) {
	for (col < 32) {
		nameTable = read(0x2000 + (row << 5) + col);
		attrTable = read(0x23c0 + ((row >> 2) << 3) + (col >> 2));
		attribute = (attrTable >> (((row & 0x02) << 1) + (col & 0x02))) & 0x03;
		patternTableAddr = (nameTable << 4) + ((ctrl & 0x10) << 8);
		
		for (i < 8) {
			for (bit < 8) {
				currentPixel = ((pattern[patternTableAddr + i] >> (7 - bit)) & 0x01) + (((pattern[patternTableAddr + i + 8] >> (7 - bit)) & 0x01) << 1);
				pixelColor = palette[(attribute << 2) + currentPixel];
				screen[(row << 3) + i][(col << 3) + bit] = pixelColor;
			}
		}
	}
}
There's another thing I'd like to get out of the way: Are you separating CPU RAM from PPU RAM? People are sometimes confused by the fact that there are 2 separate buses, and if you don't emulate this correctly there's no way that the various tables will be populated and read correctly. I'm particularly worried about your read() function... is it reading from VRAM?
Sorry about that. I made it pseudocode and left a few things out. (I am using i in my real calculation for the pixel).

Also, I am seperating PPU and CPU ram, so that isn't an issue. If you don't mind helping I can PM you the repo where my code is located.

edit: By examining your code i realized I was rendering each pattern mirrored and fixed that. Now the letters are in the correct direction and I have color. However, it is still the same jumbled pattern and still turns to grey after only a few frames of this jumble.
CaptureColor.JPG
CaptureColor.JPG (25.75 KiB) Viewed 2535 times
Post Reply