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
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:
Proposed:
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:
The
attributetablesample exists specifically to demonstrate this complexity.Proposed:
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_palettewhich would replace ~60 lines of attribute table code. The hello, attributetable, and other samples that do nametable setup would also benefit.Related to #30