| English | Português (Brasil) |
Milestone 0.4.4 provides bounded updates to nametable 0 after rendering starts.
All coordinates and values are byte expressions:
nes.set_tile($0F, $0E, $03);
Tile := nes.get_tile($0F, $0E);
nes.set_attribute($03, $03, $E4);
nes.set_tile(x, y, tile) accepts tile coordinates x = 0..31 and
y = 0..29. It queues the corresponding PPU write. When NMI writes the byte
to the PPU, it also updates the optional 960-byte confirmed tile shadow.
nes.get_tile(x, y) reads this shadow and therefore returns the value confirmed
in the PPU, not a value merely waiting in the queue.
nes.set_attribute(x, y, value) accepts hardware attribute-table coordinates
x = 0..7 and y = 0..7. The value is a raw NES attribute byte for the
selected 4-by-4-tile region; the compiler does not encode palette quadrants.
Attributes have no separate RAM shadow.
Direct literal or constant coordinates outside these ranges are compile-time
errors. Other coordinate expressions are checked at runtime: out-of-range tile
or attribute writes do nothing, and an out-of-range nes.get_tile returns
$00.
The runtime owns four fixed queue slots. Each successful nes.set_tile or
nes.set_attribute occupies one slot until the next NMI consumes it. NMI scans
all four slots and writes at most four bytes through $2007 per frame. A slot’s
ready flag is published only after its address and value are complete.
When all four slots are occupied, a later write is dropped and
runtime_background_queue_overflow becomes $01. A rejected tile or attribute
write changes neither PPU memory nor the confirmed tile shadow. Existing queue
entries are never overwritten. Once NMI frees the slots, later calls can be
accepted even while the sticky overflow flag remains set.
nes.background_updates_overflowed() returns a boolean view of that sticky
flag. nes.clear_background_update_overflow() resets only the flag and does not
affect queued writes.
nes.clear_background_updates() discards every write that has not yet been
consumed. It does not clear the overflow flag and does not change the confirmed
tile shadow or PPU memory. If NMI has already consumed a write, that confirmed
write cannot be cancelled.
When the program calls nes.clear_background_updates(), the compiler links a
one-byte cancellation lock checked once at the beginning of the bounded NMI
uploader. The store that acquires this lock is the race boundary. If NMI
passes the check first, it completes that whole bounded upload before main code
resumes, and cancellation then removes only writes still pending afterward. If
main code acquires the lock first, an intervening NMI skips the whole queue;
main code clears all four ready flags and then releases the lock. NMI can
therefore never observe a sequentially half-cleared queue. Tile and attribute
writes use the same protocol, and the independent sticky overflow flag is not
touched.
Repeated writes to one address occupy separate slots and are uploaded in queue
order. Before that NMI, nes.get_tile() still returns the previously confirmed
value. After NMI, it returns the last write processed for that tile.
The 960-byte shadow is linked only when nes.get_tile() appears in the program.
When the program calls nes.load_background(), its
initial upload copies the first 960 asset bytes into that shadow. Without a
configured background, generated RESET code zeroes both nametable 0 and the
shadow while rendering and NMI are disabled. Thus the first get_tile result
always represents the compiler-established PPU state.
The immutable map queried by
nes.background_collision is separate from this
confirmed visual shadow. Runtime tile writes do not modify collision flags.
Write-only background programs omit the shadow. A tile-only program reserves
26 bytes: 22 bytes for queue state and helpers plus four shared PPU
restoration bytes. Attribute-only writes need 24 bytes because they do not use
the two tile-index helpers. The cancellation lock adds one byte only when
nes.clear_background_updates() is present. A program combining tile writes
with nes.get_tile() reserves 986 bytes without cancellation or 987 bytes with
it. A
get_tile-only program reserves 968 bytes and does not install the NMI queue
uploader. Programs using only the overflow inspection/clear APIs reserve only
the one-byte sticky flag. The generated .map identifies each conditional
block.
The backend also emits only the public background helpers referenced by the program. Tile-only, attribute-only, and read-only programs therefore omit the other entry points. The shared queue publisher, uploader, and tile-index helper remain present whenever a retained public entry point calls them; this is explicit dependency selection, not a general dead-code optimizer.
The background uploader runs before the optional user VBlank callback. One
shared NMI epilogue then restores PPUCTRL, scroll X/Y, and PPUMASK after all
runtime and user VBlank work. Background operations and nes.get_tile are not permitted on
a VBlank callback path because NMI owns queue consumption.
Background updates support only nametable 0, one-byte writes, and raw attribute entries. They do not add multiple nametables, a generic PPU queue, or streaming.