LEARN NES DEVELOPMENT FROM ZERO
Learn NES Development: From Zero to 8-Bit Hero
Goal: To learn how to create a functional Nintendo Entertainment System (NES) game from scratch. This journey involves mastering 6502 assembly language, understanding the unique hardware of the NES (especially the PPU), and embracing the extreme limitations of 1980s console development.
Why Learn NES Development?
In an era of multi-gigabyte game engines and near-infinite resources, why learn to code for a 40-year-old console with only 2KB of RAM?
- You Will Truly Understand Hardware: You can’t use the NES without understanding its hardware. You will learn about CPUs, Picture Processing Units (PPUs), memory maps, and timing cycles on an intimate level.
- It’s the Ultimate Optimization Challenge: The NES CPU runs at a mere 1.79 MHz. You only have 2KB of RAM. You are limited to 64 colors (from a palette of 54) and 64 sprites on screen at once. Every byte and every CPU cycle matters. Learning to work within these limits is a masterclass in efficient programming.
- No Abstractions, Pure Control: There is no operating system. There is no
printf. There are no libraries to do the heavy lifting for you. Your code is in complete control of the machine. When you draw a character on screen, it’s because you told the PPU exactly where to find its pixel data. - A Rewarding, Tangible Result: The end product is a single
.nesROM file that you can run in an emulator or, with the right hardware (like a flash cart), play on an actual NES console.
After this journey, you will look at modern computing with a completely new perspective, and you will have earned a black belt in low-level programming.
Core Concept Analysis
The NES is not one processor; it’s a team of specialized chips. Your job as the programmer is to be the director, telling each chip what to do and when.
The NES Architecture
┌──────────────────────────────────────────┐
│ Game Cartridge │
│ PRG-ROM (Program Code) + CHR-ROM (Graphics Data) │
└──────────────────────────────────────────┘
▲
│ (64-pin Edge Connector)
▼
┌──────────────────────────────────────────┐
│ NES Console │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ CPU │───│ PPU │───│ APU │ │
│ │ (6502) │ │(Graphics) │ │ (Sound) │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │
│ ┌─────────┐ ┌─────────┐ │
│ │ 2KB RAM │ │ 2KB VRAM│ │
│ └─────────┘ └─────────┘ │
│ │
└──────────────────────────────────────────┘
Fundamental Concepts
-
The CPU (Ricoh 2A03/6502): This is the “brain.” It runs your game logic. It’s an 8-bit processor with only a handful of registers (A, X, Y). You will program it exclusively in 6502 Assembly Language.
- The PPU (Picture Processing Unit): This is the graphics chip. It is a separate processor that draws the screen. It has its own memory and its own rules. You don’t “draw pixels” to the screen. Instead, you configure the PPU’s memory and registers, and it handles the drawing.
- Pattern Tables: Contain the 8x8 pixel tile data for both backgrounds and sprites. This is your “tileset” or “sprite sheet.”
- Name Tables: A grid (like a tilemap) that tells the PPU which background tile from the Pattern Table to draw at each position on the screen.
- Palettes: The PPU has a very limited color palette. You must define small sub-palettes (4 colors each) for the background and for sprites.
- Sprites & OAM: Sprites are the movable objects. The PPU can draw 64 of them. Their properties (X/Y position, tile number, palette) are stored in a special 256-byte area of memory called Object Attribute Memory (OAM). You must update this memory yourself.
-
The Main Loop and VBlank: The PPU is constantly drawing the screen, scanline by scanline. If you try to write to its memory while it’s drawing, you’ll get graphical glitches. The only “safe” time to update the PPU is during the Vertical Blanking (vblank) period—the brief moment after the PPU has finished drawing one frame and before it starts the next. Your main game loop will look like this:
loop: wait for vblank, update graphics, run game logic, repeat. The vblank interrupt (called an NMI) is the heartbeat of every NES game. - The Toolchain: You will need a set of specialized tools.
- Assembler:
ca65is the standard assembler for 6502 development. - Emulator:
Mesenis the best emulator for NES development due to its incredible debugger, which lets you inspect the PPU, memory, and CPU state in real time.FCEUXis another solid choice. - Graphics Editor:
YY-CHRis a classic tool for drawing and editing the 8x8 tiles for your pattern tables.
- Assembler:
Project List
This path is designed to build your skills incrementally. Each step is a significant achievement.
Project 1: “Hello, World!” - A Static Screen
- File: LEARN_NES_DEVELOPMENT_FROM_ZERO.md
- Main Programming Language: 6502 Assembly
- Alternative Programming Languages: C (using cc65, but this is NOT recommended for learning as it hides the hardware)
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 2: Intermediate
- Knowledge Area: NES / 6502 Assembly / PPU
- Software or Tool: ca65 (assembler), Mesen (emulator), YY-CHR (graphics tool)
- Main Book: “Programming the 6502” by Rodnay Zaks, and the Nesdev Wiki
What you’ll build: A .nes ROM that, when loaded, displays a single, static screen of text or graphics. You will not write complex logic; the goal is to successfully configure the PPU to display something.
Why it teaches the fundamentals: This project is 90% setup and toolchain, 10% code. It forces you to learn the entire workflow: creating graphics, assembling the ROM, and writing the minimal startup code to initialize the PPU and get it drawing from your graphics data.
Core challenges you’ll face:
- Setting up the toolchain (
cc65) → maps to being able to assemble a.sfile into a.nesROM - Creating a valid iNES header → maps to telling the emulator how your cartridge is laid out
- Writing the reset/NMI interrupt handlers → maps to the fundamental structure of an NES program
- Loading palette and background data into PPU memory → maps to learning to write data to PPU registers like
$2006and$2007during vblank
Key Concepts:
- iNES Header: The first 16 bytes of every
.nesfile. - PPU Registers: The memory-mapped addresses (
$2000-$2007) used to control the PPU. - NMI (Non-Maskable Interrupt): The vblank interrupt that is the heartbeat of your game.
- Memory-Mapped I/O: The CPU talks to the PPU/APU by writing to specific memory addresses.
Difficulty: Intermediate (The setup is the hardest part for a beginner) Time estimate: 1-2 weeks Prerequisites: Patience. Familiarity with hexadecimal and binary is essential.
Real world outcome:
You will have a hello.nes file. When you drag it into the Mesen emulator, you will see your own custom message or graphic appear on the screen. It won’t move, but it will be yours.
Implementation Hints:
- Draw your graphics: Use YY-CHR to draw some 8x8 tiles. Save this as a
.chrfile. This will be your Pattern Table data. - The Assembly File: Your main
.sfile needs several key parts:- A
.segment "HEADER"with the iNES header. - A
.segment "CODE"for your program logic. - Reset Handler: This code runs once at power-on. Its job is to initialize the hardware: disable the PPU, set up memory, and then enter an infinite loop.
- NMI Handler: This code runs every vblank (~60 times a second). Its job is to update graphics. For this first project, it can be empty after the initial setup is done.
- A
- The Reset Logic:
- Wait for two vblanks to ensure the PPU has stabilized.
- Turn off the screen by writing to
PPUCTRL($2000) andPPUMASK($2001). - Load your color palettes into PPU memory addresses
$3F00-$3F1F. - Load your background Name Table data.
- Turn the screen back on.
- Enter an empty infinite loop:
forever: jmp forever.
Learning milestones:
- You can assemble a ROM without errors → Your toolchain is working.
- The ROM loads in Mesen without crashing → Your iNES header and reset vector are correct.
- You can see your custom colors on screen → You have successfully written to the PPU palette memory.
- Your background tiles appear correctly → You have successfully configured the PPU to render your graphics. You have made something from nothing.
Project 2: Player Control - Moving a Sprite
- File: LEARN_NES_DEVELOPMENT_FROM_ZERO.md
- Main Programming Language: 6502 Assembly
- Alternative Programming Languages: N/A
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 2: Intermediate
- Knowledge Area: NES / 6502 Assembly / Input Handling
- Software or Tool: ca65, Mesen
- Main Book: Nesdev Wiki - PPU OAM
What you’ll build: You will take your static screen ROM and add a single “player” sprite. You will write code to read input from the NES controller and update the sprite’s X and Y coordinates, making it move around the screen.
Why it teaches sprite handling: This project introduces the second major component of NES graphics: sprites. You’ll learn about Object Attribute Memory (OAM) and the strict timing requirements for updating it. You will also write your first real game logic: reading input and changing state.
Core challenges you’ll face:
- Understanding OAM → maps to the 256-byte memory area that defines all 64 sprites
- Uploading data to OAM → maps to using the OAMDMA register (
$4014), which triggers a DMA transfer and halts the CPU for 513 cycles - Reading the controller ports (
$4016/$4017) → maps to the specific strobe-and-read sequence required to get button states - Updating sprite state in RAM → maps to keeping track of your player’s X/Y coordinates in CPU RAM and copying them to OAM during vblank
Key Concepts:
- OAM (Object Attribute Memory): The PPU’s internal memory for sprite data.
- DMA (Direct Memory Access): A way to quickly copy a block of memory to the PPU.
- Controller Input: The process of reading the state of the D-pad and buttons.
Difficulty: Intermediate Time estimate: Weekend Prerequisites: Project 1.
Real world outcome:
A .nes ROM where you can move a character around the screen with the D-pad. It will be the first time your creation feels interactive.
Implementation Hints:
- Reserve RAM for Sprite Data: In your CPU RAM (the 2KB), reserve 256 bytes to act as a “shadow OAM”. It’s much easier and safer to update this copy during your game loop.
- The NMI (VBlank) Handler: This is where you’ll do the graphics update.
- Use the
$4014OAMDMA register to copy your 256-byte shadow OAM into the real PPU OAM. This must be done every frame.
- Use the
- The Main Game Loop (in your
foreverloop from Project 1):- Read the controller state into a variable.
- Check if the ‘up’ button is pressed. If so, decrement the player’s Y-coordinate variable in RAM.
- Check for ‘down’, ‘left’, ‘right’ and update the X/Y variables accordingly.
- Wait for the next NMI to occur. This is crucial for stable timing. A common way is to have the NMI set a flag, and have the main loop wait for that flag.
Learning milestones:
- You can display a single, non-moving sprite → You’ve initialized OAM correctly.
- You can read the state of the A button → You understand the controller reading sequence.
- The sprite moves in response to D-pad input → You have connected input to game state.
- The sprite’s motion is smooth and flicker-free → You have correctly structured your main loop and NMI handler to update OAM only during vblank.
Project 3: A Scrolling World
- File: LEARN_NES_DEVELOPMENT_FROM_ZERO.md
- Main Programming Language: 6502 Assembly
- Alternative Programming Languages: N/A
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 3: Advanced
- Knowledge Area: NES / PPU / Graphics
- Software or Tool: ca65, Mesen
- Main Book: Nesdev Wiki - PPU scrolling
What you’ll build: A ROM where the player sprite stays in the middle of the screen while the background scrolls smoothly left and right as the player moves. This creates the illusion of a large, explorable world.
Why it teaches advanced PPU control: Scrolling is one of the most iconic features of NES games, and one of the most complex to program. It requires a deep understanding of how the PPU uses its Name Tables and a very precise manipulation of the PPU’s scroll registers ($2005 and $2006) during the frame.
Core challenges you’ll face:
- Setting up a “world” larger than one screen → maps to using two or more of the PPU’s Name Tables to create a larger map
- Manipulating the scroll registers (
$2005) → maps to telling the PPU where to start drawing the background from - Updating background tiles as you scroll → maps to calculating which new column of tiles is about to come on screen and loading it into the Name Table just in time
- Managing complex PPU writes during vblank → maps to interleaving palette, attribute, and Name Table updates within the very tight vblank time window
Key Concepts:
- PPU Scrolling: The registers that control the background’s X and Y offset.
- Name Tables & Mirroring: How the PPU’s background memory is laid out. Cartridges can be wired for horizontal or vertical mirroring, affecting how scrolling works.
- Attribute Tables: A separate area of PPU memory that defines which 4-color palette is used for each 16x16 pixel area of the background.
Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 2.
Real world outcome:
A .nes ROM that feels like a real side-scrolling game. You’ll be able to move your character from left to right across a world that is much larger than a single screen.
Implementation Hints:
- Camera/Scroll Variables: Your game logic will now track a “camera X” position. The player’s screen position will be fixed, but their “world” position will change.
- Update Scroll Registers: During vblank, you need to write the low 8 bits of your camera’s X and Y position to the PPU’s scroll registers. This is the core of scrolling.
- Loading New Data: As the camera scrolls, you’ll eventually “wrap around” the Name Table. Your code must detect this. When the camera is about to reveal a new column of tiles, you must calculate which tiles to load and write them to the correct Name Table address in PPU memory before they appear on screen. This is the main challenge.
- The Vblank Queue: You will quickly find you have a lot to do in vblank (update OAM, update palettes, update scrolling, update background tiles). A good architecture is to have your main loop add “jobs” to a queue in RAM, and have the NMI handler process that queue.
Learning milestones:
- You can manually set the scroll registers and see the background shift → You understand the basic scroll mechanism.
- The background scrolls smoothly as the player moves → You have linked game state (camera position) to the PPU registers.
- The scrolling can continue past one screen without graphical glitches → You are successfully loading new column data into the off-screen part of the Name Table.
- You can scroll both left and right seamlessly → You have mastered bi-directional scrolling logic.
Project 4: A Playable Game - “Jumper”
- File: LEARN_NES_DEVELOPMENT_FROM_ZERO.md
- Main Programming Language: 6502 Assembly
- Alternative Programming Languages: N/A
- Coolness Level: Level 5: Pure Magic (Super Cool)
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 4: Expert
- Knowledge Area: Game Development / Collision Detection / Physics
- Software or Tool: ca65, Mesen
- Main Book: “Making Games for the NES” by Steven Hugg
What you’ll build: A complete, simple platformer game. The player can move left, right, and jump. The world scrolls. There are solid platforms to stand on and jump from. This project combines everything from the previous three projects and adds game physics and collision detection.
Why it’s a capstone project: This is a real game. It requires you to manage input, physics, scrolling, rendering, and collision detection, all within the tight constraints of the NES. You will have to balance your CPU time carefully. Finishing this project means you have the core skills to build almost any simple 2D game on the NES.
Core challenges you’ll face:
- Implementing Gravity and Jumping → maps to using state variables for player velocity and applying acceleration each frame
- Background Collision Detection → maps to checking the tile data in the Name Table at the player’s future position to see if it’s solid
- Managing Game State → maps to keeping track of whether the player is on the ground, jumping, falling, etc.
- Balancing CPU Time → maps to making sure all your logic (input, physics, collision) can run in the ~29,000 CPU cycles available per frame
Key Concepts:
- Fixed-Point Math: Using 16-bit numbers to represent position and velocity, where the high byte is the integer part and the low byte is the fractional part.
- Tile-Based Collision: Instead of checking against pixels, you check against the properties of the background tiles your sprite is touching.
Difficulty: Expert Time estimate: 1 month+ Prerequisites: Project 3.
Real world outcome:
A playable platformer game on a .nes ROM. You can share this file with friends, and they can play your creation in an emulator. You have made an NES game.
Implementation Hints:
- Player State: Create variables in RAM for
player_x_pos,player_y_pos,player_x_vel,player_y_vel. Use 16-bit values for position and velocity to allow for fractional movement. - Game Loop Update:
- Apply gravity to
player_y_vel(e.g.,player_y_vel += 1). - Read controller input. If ‘A’ is pressed and the player is on the ground, set
player_y_velto a negative value (e.g.,-5). - Update positions based on velocities:
player_x_pos += player_x_vel.
- Apply gravity to
- Collision Detection: This is the hardest part.
- Before you move the player, check the tile at the target position.
- You need a copy of your level layout in CPU RAM to check this quickly.
- If the player is moving down and the tile below them is solid, stop their downward movement (
player_y_vel = 0) and align their feet with the top of the tile. - If they hit a wall, stop their horizontal movement.
- Camera Logic: The camera’s scroll position should follow the player’s world position, likely with some logic to keep the player centered on the screen.
Learning milestones:
- Your player sprite is affected by gravity → You have a working physics loop.
- The player can jump → You have connected input to your physics state.
- The player stops when they land on a platform → You have implemented vertical collision detection.
- The player can run and jump through a complete, scrolling level → You have successfully integrated all the core components of an NES game.
Project Comparison Table
| Project | Difficulty | Time | Core Focus | Why it’s Important |
|---|---|---|---|---|
| “Hello, World!” | Level 2: Intermediate | 1-2 weeks | Toolchain & PPU Init | Proves you can make the hardware do anything. |
| Moving a Sprite | Level 2: Intermediate | Weekend | Sprites & Input | Makes your creation interactive. |
| Scrolling World | Level 3: Advanced | 1-2 weeks | Advanced PPU Control | Creates the feeling of a large, explorable space. |
| Playable Game | Level 4: Expert | 1 month+ | Game Logic & Collision | Turns your tech demos into an actual game. |
Recommendation
This path is a steep climb, and it must be done in order.
- Start with Project 1: “Hello, World!”. Do not underestimate the difficulty of this first step. Getting the toolchain running and understanding the PPU initialization sequence is a massive hurdle. Many great resources, like the Nerdy Nights tutorials, are designed to walk you through this.
- Once you can draw a static screen, Project 2 (Moving a Sprite) will feel like a huge leap forward in interactivity.
- Project 3 (Scrolling) is where you’ll start to feel like a real NES developer, wrestling with the PPU’s arcane but powerful features.
- Finally, the Playable Game is your thesis project, where you synthesize all your knowledge.
Welcome to the challenging but immensely satisfying world of NES development.
Summary
| Project | Main Programming Language |
|---|---|
| “Hello, World!” - A Static Screen | 6502 Assembly |
| Player Control - Moving a Sprite | 6502 Assembly |
| A Scrolling World | 6502 Assembly |
| A Playable Game - “Jumper” | 6502 Assembly |