Stuck at implementing PPU
Moderator: Moderators
Stuck at implementing PPU
Hey guys,
So I just finished writing a stable 6502 core in C and want to move on to the graphics aspect of the NES. Thing is, I'm a little stuck in terms of how everything gets rolling during start-up, though...
First and foremost, how does the Name Table get populated during start up? I've gone through numerous documentation but none of it is exactly specific on what happens. What exactly does the name table contain? I don't understand how it contains enough data to reference both sprites and background data.
So I just finished writing a stable 6502 core in C and want to move on to the graphics aspect of the NES. Thing is, I'm a little stuck in terms of how everything gets rolling during start-up, though...
First and foremost, how does the Name Table get populated during start up? I've gone through numerous documentation but none of it is exactly specific on what happens. What exactly does the name table contain? I don't understand how it contains enough data to reference both sprites and background data.
Re: Stuck at implementing PPU
Usually by loops copying blocks of data or writing blocks of repeating tiles.smora015 wrote:First and foremost, how does the Name Table get populated during start up?
The first 960 bytes are the tile numbers for each 8x8 pixel area: left to right, in rows from top to bottom. The rest is attribute data, which specifies which color set is assigned to each 16x16 pixel area.I've gone through numerous documentation but none of it is exactly specific on what happens. What exactly does the name table contain?
Sprites have almost* nothing to do with the nametable. They don't interact with the background at all except at the compositor stage of PPU processing, which combines the sprites with the background.I don't understand how it contains enough data to reference both sprites and background data.
Re: Stuck at implementing PPU
The game does it, not the emulator. The 6502 program writes to specific memory locations ($2000 to $2007, plus mirrors) to communicate with the PPU, and through these registers they can select an address in PPU memory and then send data to be written from this address on.smora015 wrote:First and foremost, how does the Name Table get populated during start up?
Maybe what's confusing you is that in some cases, part of the PPU memory is filled automatically, but this only happens for pattern tables in case of CHR-ROM. The rest of the PPU memory, and even the pattern tables in case of CHR-RAM, is completely maintained by the game program running on the CPU.
On power up, the contents are undefined and somewhat random, the NES itself doesn't initialize the PPU memory at all. After that, it contains whatever the game program puts in there, and it changes often (several times per second, depending on the game) during gameplay. Games that scroll will update the name tables more frequently, because new parts of the level are drawn as the screen scrolls.What exactly does the name table contain?
The name tables have nothing to do with sprites. It goes like this: The pattern tables hold the actual tile graphics that will be used for the background and sprites. The name tables are arrays of tile indices, that work like walls you can "glue" tiles to in order to form larger images. The attribute tables let you select what palettes will be used for the tiles in the name tables. The OAM is somewhat analogous to the name table, but for sprites. Since sprites are not aligned in a grid like the background, their attributes include X and Y coordinates, so the PPU knows where to draw them. A few more attributes are necessary for sprites, like X and Y flipping and priority, which are packed in the same byte that selects their palette.I don't understand how it contains enough data to reference both sprites and background data.
Re: Stuck at implementing PPU
Okay, now I get why each Name Table is 0x3C0 in length (not including the attribute data.) This makes soooo much more sense! I was under the impression that the Name Tables hold both BG and Sprite data, and once its time for the PPU to render all the scanlines, it would start reaching for the actual data and glue together palette data as well.tepples wrote:The first 960 bytes are the tile numbers for each 8x8 pixel area: left to right, in rows from top to bottom. The rest is attribute data, which specifies which color set is assigned to each 16x16 pixel area.
I'm assuming you only need 1 byte to address the tile because there's only 256 total tiles per Pattern Table?
Awesome, this is what I was assuming, but I was a little confused when using FCEUX's debugger. I noticed that during the first few instructions, for say, Super Mario Bros., it loops around, waiting for the VBlank flag to be set. But I couldn't catch when the Name Table got populated for the title screen, so I was very confused as to how it worked. It seemed almost like magic to me =/tokumaru wrote: The game does it, not the emulator. The 6502 program writes to specific memory locations ($2000 to $2007, plus mirrors) to communicate with the PPU, and through these registers they can select an address in PPU memory and then send data to be written from this address on.
Maybe what's confusing you is that in some cases, part of the PPU memory is filled automatically, but this only happens for pattern tables in case of CHR-ROM. The rest of the PPU memory, and even the pattern tables in case of CHR-RAM, is completely maintained by the game program running on the CPU.
I'm probably not putting in the right breakpoints somewhere. At what point should I be checking for the program to populate the Name Table with the intro screen?
Thanks so much guys, this saved me tons of hours of pulling my hair out...
Re: Stuck at implementing PPU
Lots of $2007 writes in a row often means pumping data into nametables.
Re: Stuck at implementing PPU
The subject has now changed from "how do I implement the PPU in my emulator" to "how do I use FCEUX's debugger to look at Super Mario Brothers". *confused look* :Psmora015 wrote:I'm probably not putting in the right breakpoints somewhere. At what point should I be checking for the program to populate the Name Table with the intro screen?
To answer the latter question: this is fairly easy -- you set up breakpoints on writes to PPU address ranges. Pay attention to the Add Breakpoint dialog and you'll see there's an Address range at the top, and a radio button that lets you pick PPU Mem. That's what you want.
You'll be inclined to look for writes to, say, PPU addresses $2000-23FF which correlate with the first nametable. But due to how mirroring is configured, and depending on how the game is programmed or what their intentions are, and some of the scroll settings, they might actually be writing to $2400-27FF, or $2800-2BFF, or $2C00-2FFF.
So you may want to just block on addresses $2000-2FFF.
The next thing you're probably going to think is "okay great!", go off and do it, then (probably, I'm speculating) drops to the debugger and does a ton of $2007 writes and a bunch of other complex things. You'll go through the loop a ton of times, and eventually it'll finish... except the screen doesn't contain the title screen you expect. Instead it just shows nothing.
This is because you're thinking the very first time the game starts + first time it starts writing to nametables, it's going to draw a title screen. Don't think that / don't assume that. It's very possible the game is zeroing out parts of the unused nametables (and possibly the active one -- i.e. blanking the screen), or setting up something else first. So really you end up having to step through a few situations until you find what you want. That is simply the art of debugging/reverse-engineering. It's a huge time sink.
Furthermore, many/most games have a "common writing routine" that they JSR to do a bunch of PPU writes, based on some values they write dynamically into zero page first. So what you end up having to do there is figure out what's written to those zero page addresses, then figure out where within the preceding code that was done and break on that (as a ROM execution breakpoint), often by examining the stack (because of the JSR, or multiple JSRs). Again: the art of debugging/reverse-engineering.
So what can you do to help narrow down the conditionals so you get something accurate, rather than spend 90 hours sitting around in loops + clicking buttons + praying? Gotta use your brain a little bit, along with knowing FCEUX's debugger:
The Super Mario Brothers title screen has some text that reads "(C)1985 NINTENDO" on it. It's pretty likely that if we could break on when it writes the copyright symbol (tile) to the screen, that we'd have a "general" idea of where the routine might be.
I'll focus on the ROM titled Super Mario Bros. (JU) [!].nes (you have to give this because there are a billion ROMs floating around and not all are the same/have the same addresses. SMB is the worst of the bunch, there must be a hundred+ ROMs of this damn thing).
Load it up in FCEUX and when the title screen is drawn, hit Pause (on your keyboard) and the emulation will pause.
Now go to Debug / Name Table Viewer. You'll see the title screen in the upper left nametable ($2000-23FF) as well as in the lower left nametable ($2800-2BFF). This is what I was talking about, re: mirroring. At this point we don't know if the game is writing to PPU memory in the $2000-23FF region or the $2800-2BFF region. But here's what we do know:
If you move your mouse over the copyright symbol/tile, you'll see the debugger say Tile: $CF. Okay, so we know the copyright symbol tile is tile $CF -- which means in turn we know that that's a byte we could "key off of" when doing a breakpoint on a PPU write. But $CF just a value; that same tile value could be used at any other time but with different CHR data (potentially), so we can't just go off of that.
The other thing we know is where on the screen it writes $CF: again moving your mouse over the copyright symbol, in the upper left nametable you'll see it's at PPU address $21ED, and in the lower left it's at $29ED (makes perfect sense given the game's mirroring).
Now we know what we can add 2 breakpoints on: a write to PPU address $21ED, and another breakpoint with a write to $29ED. But hold on.
You would also think you could use a conditional value of #CF -- not $CF or #$CF (the syntax here matters greatly because of how FCEUX's debugger works. Read the documentation under Help / Debugger, specifically the stuff under Conditional Breakpoints. The syntax is very very sensitive and it's easy to mess up.) But guess what: nope -- it doesn't work. Feature/bug in the debugger. Nothing we can do about it.
So what, are we just gonna blindly break on every time something writes to $21ED or $29ED? Sure, that's possible... or we could make an assumption -- the assumption that the $CF tile value is in the accumulator (hoping it's not in X or Y when writing to $2007). So we add a conditional of A == #CF (again note syntax)
I'll save you some time and a breakpoint: the title screen code writes to the $21xx range, not $29xx. (I already took the time to look)
So we add a breakpoint when PPU memory is written to at address $21ED, with the conditional of the accumulator containing the value $CF. Crossing fingers. Press Pause (to resume emulation), then choose NES / Reset. (BTW, doing a NES / Reset while the debugger has tripped a breakpoint won't work -- you need to go into the debugger and click Run for it to resume. Just the way it works. It's easy to forget this when doing NES / Reset.)
Bam -- we get our first breakpoint hit:
Code: Select all
00:8EB6:B0 01 BCS $8EB9
00:8EB8:C8 INY
00:8EB9:B1 00 LDA ($00),Y @ $03E5 = #$CF
>00:8EBB:8D 07 20 STA $2007 = #$FF
00:8EBE:CA DEX
00:8EBF:D0 F5 BNE $8EB6
Imagine that -- the title screen is drawn and the game picks up where it left off. Okay, so now you have some information: the code being used to draw the title screen is within that $8EB9 area of ROM.
If you look closely however, you'll see what it's doing: it's doing an indirect indexed read from the 16-bit address stored at ZP locations $00 and $01, which FCEUX is nice enough to inform you is address $03E5 -- that's in normal RAM. So we can safely assume the game pre-populates a part of RAM with the nametable contents (or maybe just some of the contents, i.e. a little bit at a time -- we don't know)
If we look down a little ways more, we can see that this is certainly just a generic drawing routine used for things, because the rest of the code has:
Code: Select all
00:8EC1:38 SEC
00:8EC2:98 TYA
00:8EC3:65 00 ADC $0000 = #$00
00:8EC5:85 00 STA $0000 = #$00
00:8EC7:A9 00 LDA #$00
00:8EC9:65 01 ADC $0001 = #$4F
00:8ECB:85 01 STA $0001 = #$4F
00:8ECD:A9 3F LDA #$3F
00:8ECF:8D 06 20 STA $2006 = #$00
00:8ED2:A9 00 LDA #$00
00:8ED4:8D 06 20 STA $2006 = #$00
00:8ED7:8D 06 20 STA $2006 = #$00
00:8EDA:8D 06 20 STA $2006 = #$00
00:8EDD:AE 02 20 LDX $2002 = #$50
00:8EE0:A0 00 LDY #$00
00:8EE2:B1 00 LDA ($00),Y @ $4F03 = #$FF
00:8EE4:D0 AC BNE $8E92
00:8EE6:8D 05 20 STA $2005 = #$00
00:8EE9:8D 05 20 STA $2005 = #$00
00:8EEC:60 RTS -----------------------------------------
Code: Select all
C5,80,A5,57,80
That sure looks like a ROM address, does it not? So given that information, we can safely assume immediately preceding $80C5 is where it does a JSR to somewhere around/near where our breakpoint. So in the debugger, we go looking around $80C5 or so:
Code: Select all
00:80BE:BD 6D 80 LDA $806D,X @ $807B = #$8D
00:80C1:85 01 STA $0001 = #$03
00:80C3:20 DD 8E JSR $8EDD
00:80C6:A0 00 LDY #$00
00:80C8:AE 73 07 LDX $0773 = #$05
Code: Select all
00:8EDD:AE 02 20 LDX $2002 = #$10
00:8EE0:A0 00 LDY #$00
00:8EE2:B1 00 LDA ($00),Y @ $03E5 = #$CF
00:8EE4:D0 AC BNE $8E92
Back to emulation: if you want my advice: do not focus on Super Mario Brothers as your first game to get emulated. Focus on games like Mario Brothers (the original, not Super), Pinball, or Donkey Kong. SMB is kind of a pain in the ass. When you get the others working, then try to accomplish doing SMB. (For example, I'll be surprised if your 6502 emulation core is actually correct -- people keep getting ADC/SBC wrong, or having weird quirky flag behaviour problems that cause weird things to happen, like a ball shooting off diagonally somewhere and it's all because of a single bug in a single opcode). Start simple -- SMB is not simple. :)
Edit: Fixed 1995 typo (should be 1985); thanks Quietust!
Re: Stuck at implementing PPU
I'll assume you meant "1985". It's also useful to remember that the values being written are not ASCII, but are instead based on the pattern tiles located in CHR ROM; in this case, the copyright symbol is 0xCF.koitsu wrote:The Super Mario Brothers title screen has some text that reads "(C)1995 NINTENDO" on it. It's pretty likely that if we could break on when it writes the copyright symbol (tile) to the screen, that we'd have a "general" idea of where the routine might be.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
P.S. If you don't get this note, let me know and I'll write you another.
Re: Stuck at implementing PPU
Yeah I meant 1985, sorry. And yeah, like I described in my debugging step-by-step, the tile value is $CF at that time.Quietust wrote:I'll assume you meant "1985". It's also useful to remember that the values being written are not ASCII, but are instead based on the pattern tiles located in CHR ROM; in this case, the copyright symbol is 0xCF.koitsu wrote:The Super Mario Brothers title screen has some text that reads "(C)1995 NINTENDO" on it. It's pretty likely that if we could break on when it writes the copyright symbol (tile) to the screen, that we'd have a "general" idea of where the routine might be.
Re: Stuck at implementing PPU
Thanks for the amazing help guys, I owe you guys big time.
And good point with starting out with a simpler game. I know lots of debugging awaits for me, but this cleared up so much haha. And I'm sure I have some bugs with my CPU, but its kinda hard to check them out if I don't have any sort of graphics implemented.
And good point with starting out with a simpler game. I know lots of debugging awaits for me, but this cleared up so much haha. And I'm sure I have some bugs with my CPU, but its kinda hard to check them out if I don't have any sort of graphics implemented.
Re: Stuck at implementing PPU
Totally valid points!
And yeah, just based on my own experience from working on emulators long ago, I'd suggest using Mario Bros as a base test, or any of the first-gen games (yeah I know SMB was first-gen, but it's complex). Try things like Nuts & Milk, Clu-Clu Land, Donkey Kong, Ice Hockey -- games like that. Go for NROM (mapper #0 / "no mapper") games for starters.
When you get around to implementing mappers, I'd suggest doing MMC1 first, then try giving Zelda (#1) a try -- it tends to act as somewhat of an interesting subject for scrolling (I think horizontally -- I believe the game changes mirroring from H to V in real-time) and PPU behaviour.
And yeah, just based on my own experience from working on emulators long ago, I'd suggest using Mario Bros as a base test, or any of the first-gen games (yeah I know SMB was first-gen, but it's complex). Try things like Nuts & Milk, Clu-Clu Land, Donkey Kong, Ice Hockey -- games like that. Go for NROM (mapper #0 / "no mapper") games for starters.
When you get around to implementing mappers, I'd suggest doing MMC1 first, then try giving Zelda (#1) a try -- it tends to act as somewhat of an interesting subject for scrolling (I think horizontally -- I believe the game changes mirroring from H to V in real-time) and PPU behaviour.
Re: Stuck at implementing PPU
Super Mario Bros. was an NES launch title, but it arrived about two years into the original Famicom's life. I'd agree that Donkey Kong and other 1983-1984 Famicom games, almost all of which are NROM-128, would probably be the best way to start.
Re: Stuck at implementing PPU
Donkey Kong and Donkey Kong Jr are great early test games since they don't require much to be implemented to function. Donkey Kong was my original test game when I first tried writing an emulator.
SMB should be considered the next step up since it has a number of things that need to be working for it to work properly. I think even just Mario Bros had something else, PPU reading and the "delay" needs to be right. If you have not implemented the ability to read name tables I think all characters fall off the screen or something. I can't recall exactly. SMB has the sprite hit flag plus screen splitting to be dealt with.
So in some ways it might be easier to get a game like Contra or Mega Man running than SMB since they don't have those issues.
SMB should be considered the next step up since it has a number of things that need to be working for it to work properly. I think even just Mario Bros had something else, PPU reading and the "delay" needs to be right. If you have not implemented the ability to read name tables I think all characters fall off the screen or something. I can't recall exactly. SMB has the sprite hit flag plus screen splitting to be dealt with.
So in some ways it might be easier to get a game like Contra or Mega Man running than SMB since they don't have those issues.
Re: Stuck at implementing PPU
How does Pinball fare? I know somebody who used that to test his emulator.
Also yeah, this is one of my problems with the wiki, it tells people to avoid Super Mario Bros as their first game because of how hard it can be to get emulated, but it never gives any suggestions about which games are easy ones to start with (but then again, simple test programs are likely going to be even easier to start testing with).
Anyway, should the wiki contain a list of which games are the easiest to get running?
Also yeah, this is one of my problems with the wiki, it tells people to avoid Super Mario Bros as their first game because of how hard it can be to get emulated, but it never gives any suggestions about which games are easy ones to start with (but then again, simple test programs are likely going to be even easier to start testing with).
Anyway, should the wiki contain a list of which games are the easiest to get running?
Re: Stuck at implementing PPU
You're referring to Tricky, right? You could always post your problem on the talk page where people who edit the wiki will see it. That's why I left talk pages open to anonymous editing.Sik wrote:Also yeah, this is one of my problems with the wiki, it tells people to avoid Super Mario Bros as their first game because of how hard it can be to get emulated, but it never gives any suggestions about which games are easy ones to start with