No. 76 / project of 147 on the ladder

AtomVM batched input bridge

introduces — batched host-input NIF; viewer key filtering; Tetris input bridge cleanup

harden statelast run2026-05-05
signoff
  • DRCNOT RUN
  • LVSNOT RUN
  • antennaNOT RUN

P75 made Tetris playable, but input still had two bad properties: the chip consumed one event per NIF call, and the host viewer sent key-up and repeat noise into a tiny FIFO. P76 fixes the practical problem first. The host sends key-down commands only, and AtomVM drains queued input in one batched NIF call.

AtomVM on P76 (batched input bridge)
Found startup beam: game.beam
P76 AtomVM batched input bridge starting
rendered frames: 1
input events: 6
input batches: 1
input max batch: 6
input fifo status: 0
P76 AtomVM batched input bridge PASS

What changed

P76 keeps the P75 RTL FIFO, but changes the AtomVM side:

layernew piece
hostpygame writes event words into captured/input.fifo
harnesswaits for host_input_ready before popping host events
RTL16-entry input FIFO, count field, saturated drop counter
viewersends key-down commands only, disables key repeat
AtomVM NIFchip:input_events/0 drains queued events into one Erlang list
gameTetris process applies the input batch and renders frames

The scripted smoke queued six key events. The game drained all six as one batch, the RTL FIFO returned to zero, and the simulation exited cleanly after 14,284,133 post-load cycles.

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

The first P76 attempt tried to make this a separate Erlang input-source process. That is the shape we still want eventually, but it did not work well enough here. receive after 1 missed live input, and a cooperative after 0 process still did not produce useful on-screen control under this AtomVM port. The working result is the conservative batched drain inside the game tick.

Why not interrupts?

Interrupts need both sides wired. The hardware side can expose “input FIFO nonempty” as an IRQ condition, but the AtomVM bare-metal port still needs an M-mode trap handler, interrupt-enable setup, an IRQ acknowledge path, and a scheduler hook that wakes or runs the Erlang input source.

Just asserting external_irq would not safely deliver a process message. P76 keeps the NIF boundary we need, but the actual separate event source moves to the interrupt project.

Roadmap balance

AtomVM is one lane, not the whole project. The next few projects should keep three lanes visible:

lanenear-term work
AtomVMbuild the real interrupt/wakeup input source
Linuxreturn to userspace/init and platform-device proof points
MRubykeep as a runtime side quest after the chip/host IO substrate is reusable

What is not proven

F/D architectural compliance was NOT RUN. LibreLane hardening was NOT RUN. This is still RTL simulation, and P76 still polls from the game tick. It is not a hardware interrupt delivery result yet.

What just happened?

Host input now crosses into AtomVM in batches, and the viewer no longer spams the FIFO with key-up/repeat traffic. That made the game playable again and gave the interrupt project a sharper target.