Skip to content

Add nametable helpers: nt_put_tile, nt_put_row, nt_write, nt_set_palette #423

@jonathanpeppers

Description

@jonathanpeppers

Nametable and attribute table operations are the most error-prone part of NES development. Every sample manually computes VRAM addresses and the attribute table bit-packing is notoriously confusing.

Nametable tile helpers

Today: computing addresses manually and calling low-level VRAM functions:

// Place text on screen
vram_adr(NTADR_A(2, 2));
vram_write("HELLO, .NET!");

// Place a row of tiles (climber scroll redraw)
byte rowy = (byte)((byte)(ROWS - 1) - (byte)(rh % ROWS));
ushort addr;
if (rowy < 30)
    addr = NTADR_A(1, rowy);
else
    addr = NTADR_C(1, (byte)(rowy - 30));
vrambuf_put(addr, buf, COLS);

Proposed:

// Write a single tile at col, row on a nametable
nt_put_tile(NAMETABLE_A, 5, 10, 0xF4);

// Write a row of tiles (wraps address calc + vrambuf_put)
nt_put_row(NAMETABLE_A, col, row, buf, len);

// Write a string at col, row (wraps vram_adr + vram_write)
nt_write(NAMETABLE_A, 2, 2, "HELLO, .NET!");

These are thin wrappers that inline the NTADR_* math. Easy to implement, nice readability win.

Attribute table palette helper

Today: the attribute table controls which palette (0-3) each 16x16 pixel region uses, but the data is packed -- each byte covers a 32x32 area with 2 bits per quadrant. The climber sample has ~60 lines of arcane bit-packing for this:

vram_adr(0x23C0);  // magic: attribute table base for nametable A
for (byte ar = 0; ar < 8; ar++)
{
    byte top_pal = 0;
    byte bot_pal = 0;
    // ... 20+ lines of quadrant detection and bit-packing ...
    byte attr_val;
    if (top_pal != 0 && bot_pal != 0) attr_val = 0x55;
    else if (top_pal != 0) attr_val = 0x05;
    else if (bot_pal != 0) attr_val = 0x50;
    else attr_val = 0x00;
    for (byte c = 0; c < 8; c++) vram_put(attr_val);
}

The attributetable sample exists specifically to demonstrate this complexity.

Proposed:

// Set palette (0-3) for a 16x16 region at tile col/row
nt_set_palette(NAMETABLE_A, col, row, palette);

Under the hood this computes the attribute table address (0x23C0 + (row/4)*8 + (col/4)), selects the correct 2-bit field within the packed byte based on the quadrant, and does a read-modify-write. This is a more complex 6502 subroutine (needs bit manipulation + possibly a shadow attribute buffer), but the attribute table is the #1 stumbling block for new NES developers.

Adoption plan

Once implemented, the climber sample should be updated to use these helpers -- particularly nt_set_palette which would replace ~60 lines of attribute table code. The hello, attributetable, and other samples that do nametable setup would also benefit.

Related to #30

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions