journal 2026-05-05

P76 AtomVM batched input bridge

P76 is a cleanup after P75. Tetris still runs in AtomVM Erlang on the simulated chip, but input now crosses the NIF boundary in batches and the host viewer sends only key-down commands.

The path now looks like this:

pygame key event
  -> captured/input.fifo
  -> Verilator harness
  -> host_input_valid/data
  -> 16-entry RTL MMIO FIFO
  -> chip:input_events/0
  -> Erlang Tetris process applies the batch
  -> chip:draw_tetris_frame/9 framebuffer rendering

The new NIF is chip:input_events/0. It reads the FIFO count, drains up to the 16 queued event words, allocates one Erlang list, and returns the events in FIFO order.

Verification:

cd projects/76_atomvm_event_source/test
make all
make verilator-run-input-smoke

The passing smoke delivered six input events as one batch, reported max batch 6, drained FIFO status to 0, and exited after 14,284,133 post-load cycles.

Live play exposed two mistakes after the first write-up. The separate input-source process was not reliable enough under this bare-metal AtomVM port, and the viewer was sending key-up/repeat noise into a small FIFO. The working version drains the batch from the game tick and makes the viewer send key-down commands only.

A delayed no-quit run injected six controls after the game had started, rendered 301 frames, reported six input events, changed score to 40, and drained FIFO status to 0.

This is still polling. Real interrupt delivery needs RTL IRQ enable/ack, an M-mode trap handler, mie/mstatus setup, and an AtomVM scheduler or event-listener hook.

Roadmap note: AtomVM should not swallow the whole ladder. After this input path is shaped, we should keep Linux and MRuby visible as separate lanes: Linux for the real OS proof points, MRuby as the later language runtime side quest.

Honest status: RTL simulation only. F/D compliance was NOT RUN and LibreLane hardening was NOT RUN.