255. Chr tile decoder

Implement a CHR tile decoder.

Lesson 255 of 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_255_chr_tile_decoder.py

Reference

https://www.nesdev.org/wiki/PPU_pattern_tables

File to create

emulator/ppu/chr_decoder.py

Function to implement

decode_chr_tile(tile_bytes: bytes) -> list[list[int]]

What is a CHR tile? A CHR tile is one 8x8 graphics tile stored as 16 bytes.

It does not store final RGB colors. It stores 2-bit color indexes:

0, 1, 2, or 3

Those indexes will later be combined with palette RAM to choose actual NES colors.

CHR tile byte layout

bytes 0-7   -> low bitplane, one byte per row
bytes 8-15  -> high bitplane, one byte per row

For each row

low_byte  = tile_bytes[row]
high_byte = tile_bytes[row + 8]

For each column, read bits from left to right

column 0 -> bit 7
column 1 -> bit 6
...
column 7 -> bit 0

Suggested implementation example

def decode_chr_tile(tile_bytes: bytes) -> list[list[int]]:
    if len(tile_bytes) != 16:
        raise ValueError("To decode CHR tile, tile must be 16 bytes")

    rows = []

    for row in range(8):
        low_byte = tile_bytes[row]
        high_byte = tile_bytes[row + 8]
        columns = []

        for col in range(8):
            bit_position = 7 - col

            low = (low_byte >> bit_position) & 1
            high = (high_byte >> bit_position) & 1

            pixel = (high << 1) | low
            columns.append(pixel)

        rows.append(columns)

    return rows

Out of scope

  • pattern table rendering
  • RGB/NES palette colors
  • nametable background rendering
  • sprite rendering
  • PPU timing

Run this lesson

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