__ __ / \./ \/\_ __{^\_ _}_ ) }/^\ / /\_/^\._}_/ // / ( (__{(@)}\__}.//_/__A____A_______A________A________A_____A___A___A______ \__/{/(_)\_} )\\ \\---v-----V-----V---Y-----v----Y------v-----V-----v--- ( (__)_)_/ )\ \\ \__/ \__/\/\/ \__,--'
NAV: [Home] [Games] [Blog] [Videos] [Screenshots] [Art] [Links] [Resources] [Socials] [RIP] [RSS]
I recently went through all the effort of learning 6502 assembly and all about the NES's architecture. I figured out the basic boilerplate to make a ROM with music, tiles, sprites, and controller input. Despite writing literally assembly, The whole process was alot of fun. I think it's simpler than learning any PC graphics API, simpler than OpenGL even! I recommend that all indie game developers should try their hand at making NES games!
Ok get something to drink while I explain a decades old CPU...
First you are going to need to do some upfront research on the NES's hardware. nesdev.org is the place to get all the information. You should start learning about the CPU, which is based on the 6502 processor. Luckily for you, the 6502 only has 56 instructions to learn, and most opcodes have logical "duplicates", like: INC will add 1 to some place in memory, INX will add 1 to the X register, and INY will do the same for the Y register. That's 3 out of 56 opcodes just to add 1! There is an opcode for doing nothing, NOP! The 6502 is so so basic there isn't even an opcode for multiplication.
Since this is Assembly, the CPU also has an internal state. You got registers, or places that can store a number. The 6502 has 6 registers: The accumulator A used for math, 2 Index registers X and Y which can be used as loop counters, the program counter that stores where you are in the code, a stack pointer for the tiny 256 byte stack, and the status register which stores a bunch of flags that opcodes can modify to communicate the results of the operation.
Once you got these basic concepts, you will need an assembler. I use ca65. It is the assembler for the cc65 C compiler. Your Linux distro probably has it in a package named cc65. Install it and it's time to start writing assembly!
Starting this project shown be how I have a bad case of the C brain. Assembly is such a different style of syntax than what I am used to. Ok so because opcodes are short and take in one parameter (or 2 if doing a loop...), there is no real reason to use semicolons to denote the end of a line of code. Most of assembly is setting the CPU internal state to what you nee then running the opcode to do the thing. As such in assembly, a newline denodes a new line of code... What is this, Python? Instead, semicolons are used for comments.
Not only that, but the pre-processor in assembly is so so so much stronger and higher level than C's pre-processor. Like if you use .define in ca65 like you would use #define in C, the compiler warns you to use something else because it's not necessary.
So instead of doing something bad like:
.define two 2 ;Compiler warning
.define four ((two) + (two)) ;Also warning
You basically do have compiler time python and should do this instead:
two = 2
four = two * two ;Yes, you can multiply at compile time!
Learning Assembly as a language outside of the opcodes took some time for me! Each assembler does things differently, so ca65 assembly is a slightly different language from say, NESASM or asm6f. I haven't tried those 6502 assemblers... But anyway, once you have some understanding of the opcodes and assembly language's powerful weirdness you can move on to understanding how absolutely weird the NES graphic architecture is.
The NES has this other chip in it called the Picture Processing Unit (or PPU) which handles the background-tiles and also the 64 sprite-tiles. It's not too complicated. There are places in PPU memory, called name-tables and attribute-tables to store the tile data. The CPU needs to transfer memory from the CPU to the PPU, and this is slowwwwww. These tables store which index a tile should use when it's drawn and what color palette. But the PPU does not have the actual graphical data stored inside of it's memory. That data is actually on the cartrage and can be changed on the fly by a mapper chip! This means it's faster to change the underlying tile data by swapping graphic's bank than it is to change many tile's indexes! Much of the cool graphical effects of later NES games came out of fancy mapper chips and their ability to do this swapping very quickly.
Ok so that's the background, but what about the sprites? You basically set an entire 256 byte region of CPU RAM with the sprite position and rotation/flip data, and copy it over to the PPU's special DRAM. This Dynamic RAM decays very quickly, so if you do not send over data every frame, the sprite data becomes zero.
With all this weirdness in where VRAM is stored, I'm excited to make some sort of fun visuals or demos taking advantage of these quirks.
Ok, I didn't write an audio driver for the APU. FamiStudio exists. It's both a DAW that runs on PC and also an audio driver to play the generated output from the daw. I just clicked and dragged some notes, slapped together a song and threw it in my game. I do not have any idea how the APU works and I think that's ok. I don't plan on making large projects on the NES so moving on!
This was very easy, NesDev.org already has a page about how to properly set up the controller polling. This page is required reading because the DPCI sample playback from the APU is a bit glitched and has a chance to corrupt the controller data. NesDev has an explaniation of the glitch and a guide on the work-arounds.
And thats how I ended up with this playable NES rom. Currently, it does not use a mapper chip. This severely limits the graphical capabilities of what I can make. The first Super Mario Bros does not use a mapper chip, all later Super Mario games had one. My plan is develop this boilerplate/template game further so that hopefully I can make an NES ROM in a gamejam weekend, and have it immediately playable in a browser. I am not sure how feasible this goal is given that I only have made a hello world game cart, But I think this should be doable if I have a good enough library of general purpose tools like RNG or math function lookup tables.
Anyway, stay tuned for more NES development!
severly