P74 turns the framebuffer viewer into the front half of an interactive device. P73 could show frames produced by AtomVM; P74 sends host input back into the simulated chip and lets Erlang decide what to do.
The path is:
pygame key event
-> captured/input.fifo
-> Verilator harness queue
-> host_input_valid/data top-level pins
-> MMIO_INPUT_STATUS / MMIO_INPUT_DATA
-> chip:input/0
-> Erlang server process state update
-> framebuffer redraw
-> chip:frame_ready/1
-> host frame dump and viewer refresh
The game itself is intentionally small: a square moves on a gradient background, Space changes color, and a meter at the bottom reflects how many input events the chip consumed.
Verification target:
cd projects/74_atomvm_interactive_game/test
make all
make verilator-run-input-smoke
The smoke target writes scripted key events into the FIFO so the
non-interactive run can prove the input path without needing a human at
the keyboard. The interactive target is make view-interactive.
Follow-up jank pass:
cd projects/74_atomvm_interactive_game/test
make profile-interactive-smoke
That showed the original roughness was mostly simulated chip work, not
frame transfer. The project now has a coarse chip:draw_game_frame/5
NIF so Erlang crosses into C once per frame instead of roughly 109
times. The current profile renders 32 frames in 13,764,739 post-load
cycles, runs Verilator at about 1.40 MHz wall-clock, and averages about
137,700 chip cycles per steady-state frame. The harness framebuffer dump
cost is only about 0.072 ms/frame, and the pygame read/convert/draw path
profiles around 4.77 ms/frame.
Note: the first implementation tried AtomVM’s OTP gen_server, but
that path parked before the first framebuffer render in this bare-metal
setup. The checked-in P74 demo uses a small Erlang mailbox server so
the host-input bridge is proven now; swapping back to OTP gen_server
is a follow-up runtime task.
Honest status: RTL simulation only. F/D compliance was NOT RUN and LibreLane hardening was NOT RUN.