253. Ppu bus nametable vram mapping

Implement PpuBus nametable VRAM address mapping.

Lesson 253 of 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_253_ppu_bus_nametable_vram_mapping.py

Reference

https://www.nesdev.org/wiki/PPU_memory_map
https://www.nesdev.org/wiki/PPU_nametables

File to update

emulator/bus/ppu_bus.py

Constants to add

NAMETABLE_START = 0x2000
NAMETABLE_END = 0x3EFF
NAMETABLE_SIZE = 0x0800

What is a nametable? A nametable is background layout memory. It does not store pixels. It stores tile numbers that tell the PPU which CHR tile to draw at each background position.

Simple example

PPU memory $2000 contains $24

Meaning

the top-left background cell uses CHR tile index $24

The PPU exposes four logical 1KB nametable areas

$2000-$23FF
$2400-$27FF
$2800-$2BFF
$2C00-$2FFF

The range $3000-$3EFF mirrors $2000-$2EFF.

Tutorial simplification

For now, use the existing large VRAM backing and normalize nametable addresses into a simple 2KB window. Do not create a separate nametable RAM object yet.

Suggested implementation example

NAMETABLE_START = 0x2000
NAMETABLE_END = 0x3EFF
NAMETABLE_SIZE = 0x0800

def normalize_nametable_addr(self, addr: int) -> int:
    if 0x3000 <= addr <= 0x3EFF:
        addr -= 0x1000

    index = (addr - NAMETABLE_START) % NAMETABLE_SIZE
    return NAMETABLE_START + index

def read(self, addr: int) -> int:
    addr = addr & PPU_ADDRESS_MASK

    if CHR_START <= addr <= CHR_END:
        ...

    if NAMETABLE_START <= addr <= NAMETABLE_END:
        return self.vram.read(self.normalize_nametable_addr(addr))

    if PALETTE_START <= addr <= PALETTE_END:
        ...

    return self.vram.read(addr)

Out of scope

  • horizontal/vertical cartridge mirroring
  • four-screen mirroring
  • attribute table interpretation
  • rendering
  • moving nametable storage out of the large VRAM backing

Run this lesson

uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_253_ppu_bus_nametable_vram_mapping.py -v