Hey guys.
So a while back I'd started to learn 6502 and how to program for the nes, and lately I've begun to get back into it. I've read several tutorials and the Nerdy Nights threads over on NintendoAge but I'm still pretty confused on how to work with the PPU - actually displaying graphics - Sprites, backgrounds, all that.
I have a general idea of how to declare palettes, but I'm confused on what exactly a .chr file is. I've read about "name tables" and "attribute tables" and I'm confused on those as well. I've been using yy-chr to make my .chr files, but I can't figure out how to make sprites/backgrounds from them in my .asm file. Multiple sources have told me to put all of the graphics data under bank 2, and declare them with data bytes; but I'm completely stumped on how to do that.
Any help on clearing this up for me would be greatly appreciated.
Before trying to make your own stuff you really should examine how existing games work. You can do this by loading games in FCEUX, Nintendulator, and even Nesticle (no matter how old and lame Nesticle may be, it has an incredibly insightful tool which I haven't sen in any other emulator - live tile editing). Then look for the debugging tools: pattern viewers, name table viewers, OAM viewers... open as many of them at the same time as you can and see how they change as you move around in the game. That should help you see how everything connects.
The reason NES graphics are as complicated as they are is mostly because memory back then was expensive, so you couldn't simply store graphics in some straightforward way (e.g. each pixel in the screen is a byte) because that would have taken insane amounts of memory. In addition to that, CPUs were too slow to manipulate all that memory fast enough to animate everything smoothly anyway, so it was a given that graphics would have to be represented in as few bits as possible.
The first obvious way to save memory is to divide the screen in blocks, instead of allowing access to each individual pixel. This worked well for games because the background repeated a lot (look how many "?" blocks are scattered around in SMB), so it just makes sense to have a dictionary of blocks you can reuse many times and have your screen be just a list of pointers to that dictionary. This alone makes the screen occupy about 71% less space than if each pixel was individually stored (256x240-pixel screen vs. 256 8x8-pixel blocks + 960 pointers)!
The next cut had to do with colors. NES backgrounds have 16 (actually 13, but 13 or 16 takes the same number of bits to represent, which is 4) colors, but 4 bits per pixel is still too expensive to keep, even after reducing the pixel count to only 256 8x8 blocks. The solution was to remove 2 of these bits from the pixel and move them to the attribute tables, and having the same 2 bits be shared with a much larger area, 16x16 pixels, as opposed to every single pixel. The result is that each tile only has 2 bits per pixel, enough to represent 4 colors, and in order to specify which these 4 colors will be out of the 13 possible, the data in the attribute table is used. If you played NES games before you must have noticed that backgrounds are almost always divided into 16x16 blocks, and the reason is that this is the minimum area you can apply palettes to, because of how the attribute tables were design.
I'm just trying to give you an idea of why things are like they are, but you will have a hard time learning this just from reading. If you use the debugging tools like I said earlier, I'm sure everything will make more sense. You really shouldn't be trying to put together a ROM if you don't understand how the graphics are put together, because you'll only find frustration. Please take some time to study games you admire (it's much easier to learn from things you like!), and come back with questions whenever something in particular doesn't make sense.
The .chr file is most likely a "pattern table". For most mappers, it needs to be loaded at the beginning of the CHR ROM. How you do this depends on the assembler/compiler you are using. For NESASM, it involves using the command BANK followed by the MagicKit bank number of the CHR ROM; the next line uses a INCBIN command to include the contents of the .chr file into the resulting ROM image. The MagicKit bank number of the CHR ROM is twice the number specified with the INESPRG command (for example, if you specified INESPRG 2, then the CHR ROM will be at BANK 4). For other assemblers, I don't know; look at their documentation.
tokumaru wrote:
(a lot of good information)
That was a really good reply.
Also, I agree with the idea of studying how games work inside an emulator with the built in tools to get a better understanding.
I personally recommend fceux, but that is because I am used to do stuff that way, and there may be even better emulators available.
tokumaru wrote:
quote
Thank you for your explanation. It really helped a lot. I was able to figure out most of my questions by simply going back and just re-reading most of what I'd already read. I've figured bout how to display things. I'll definitely take your advice and look through existing games. Thanks again for your guys' help.