The branch's evidence was captured on an older MAME binary whose frames were VR150 (1024x864). Rebuild MAME from this tree's driver and re-run verify.sh --skip-mame at the VR319 default (1280x1024 active). All six tests pass. Event counts, focus=00400001, tick=887 and quit=1 are unchanged; only the main-loop counts moved with the frame cadence: move 650, panels 648, quit 555 (was 354/346/300). The evidence PNGs are regenerated at 1280x1024. Record the numbers and the geometry change in VT1200.md, TERMTRIS_REPORT.md and HANDOFF.md, and note the qemu-i386 toolchain fallback.
7.3 KiB
Handoff — termtris running inside the VT1200
Everything here is on branch termtris of
https://git.wut.ee/opencode.energia.arti.ee/vt1200.git (remote origin).
The branch was developed in a worktree at
/home/arti/.local/share/opencode/worktree/26e92e/termtris; any checkout of the
branch is equivalent.
git fetch origin termtris
git checkout -b termtris origin/termtris
apps/termtris/verify.sh
verify.sh is the whole acceptance run: ROM checksum, toolchain check, MAME
build (skip with --skip-mame), the six tests, the acceptance summary and the
evidence list. It exits non-zero on the first failure and keeps every test log
under apps/termtris/build/verify_<name>.log. RUNTIME is roughly 12 minutes
once MAME is built; the MAME build itself is the long part.
What is done
The upstream game (John Tsiombikas' termtris, commit 7c5435d) builds as a
native TMS34010 program with the 1990 TI r5.01 C toolchain under 2ine, loads
into VT1200 RAM through dwt/loader, opens a 480×312 window on the terminal's
own X server through the ROM Xlib, and reads real X11 key events. It is the
actual game: board, score/level/lines panels, piece preview, help and
high-score panels, moving/rotating/dropping pieces, and q to quit.
| task | state | commit |
|---|---|---|
| vendor + toolchain + XG smoke test | done, smoke.sh PASS |
a861268 |
sprintf/stdio compat |
done, fmt.sh PASS |
a804181 |
| cell-grid X11 backend | done, render.sh PASS |
264b53a |
| key map, clock, main loop | done, input.sh PASS |
c1945cf |
| game integration, scores, COFF | done, play.sh/play_keys.sh PASS |
41d3cec |
| docs + harness report | done | 3e2ddde |
| review pass (seed, focus, expose, assertions) | done | 7a2cef5 |
| handoff script + this note | done | a12fb47 |
| qemu-i386 fallback for kernels without 16-bit LDT | done, verify.sh PASS |
ce40582 |
| VR319 re-run, regenerated evidence, updated numbers | done, verify.sh PASS |
(this commit) |
VT1200.md is the port documentation; harness/mame/TERMTRIS_REPORT.md is the
harness-side report; the design and plan are in
docs/superpowers/specs/2026-11-03-termtris-vt1200-design.md and
docs/superpowers/plans/2026-11-03-termtris-vt1200.md.
The VR319 re-run — done
The screenshots and the acceptance numbers used to come from an older MAME
binary (/home/arti/code/k-space/mame0289/vt1200d, 30 Oct) that predates the
driver's VR319 default. That binary has no VT1200_MONITOR, and its frames are
1024×864, i.e. the old VR150 geometry. The driver in this tree defaults to
VR319 (1280×1024 active, 1696×1063 raster), which changes the frame cadence
and therefore the frame at which the loader and the tests act.
The branch has now been re-run against a fresh MAME 0.289 build of this tree's
driver at the VR319 default. apps/termtris/verify.sh --skip-mame passes all
six tests, the evidence PNGs were regenerated at 1280×1024, and the new
DIAG values are in VT1200.md and harness/mame/TERMTRIS_REPORT.md. The
behaviour did not change: same events, same window (focus=00400001), same
quit=1, board/panels/preview all visible. Only the loop counts moved, because
the VR319 raster runs at a different emulated frame rate. The loader still acts
at frame 2500 and the keys at 3095–3190; no re-timing was needed.
Prerequisites on the new box
- a MAME 0.289 checkout (
MAME_SRC, default/home/arti/code/k-space/mame0289); - the 2ine build with
lx_loader(NINE=, default/home/arti/os2-work/2ine/build); - the r5.01 SDK zips, already in the repo under
tools/tms34010-sdk/1990-11 TMS340 Family SDK/; vt1200_v22.binin the repo root (the harness ROM dir symlinks to it).
The r5.01 tools are 16-bit OS/2 NE programs. If the host kernel was built
without CONFIG_X86_16BIT, lx_loader cannot create their 16-bit LDT
segments and aborts at its first one; build.sh detects that and runs the same
loader under qemu-i386 (install qemu-user), or honours an explicit
LX_RUNNER=. This box's cloud kernel is one of those; the generated COFF is
unchanged.
verify.sh checks the ROM and the 2ine loader before doing anything, and
harness/mame/build.sh re-registers the driver in mame.lst and builds the
single-driver vt1200 subtarget.
Expected results
Last full run (MAME 0.289, this tree's driver, VR319 default, 1280×1024 frames; the earlier figures were VR150):
smoke PASS type=2 keycode=194 lookup_n=1 ch=61 ks=0061
fmt PASS six formats exact
render PASS flus=464C5553 progress=00FFFFFF
input PASS bytes 61 9B 41 9B 42 9B 44 9B 43 30 0A 7F 71 70
(a/up/down/left/right/0/Return/Delete/q/p)
play PASS DIAG: tick=887 quit=0
play_keys PASS move DIAG: loops=650 events=18 last=3 focus=00400001 rev=0 quit=0
panels DIAG: loops=648 events=12 last=3 focus=00400001 rev=0 quit=0
quit DIAG: loops=555 events=10 last=3 focus=00400001 rev=0 quit=1
focus=00400001 is the window the process created; quit=1 is the game's own
global after q — that is the proof the key reached the game and not just the
window.
Known failure modes (all seen, all diagnosed)
| symptom | cause | handling |
|---|---|---|
| build stage "fails" with exit 139 but produced its file | 2ine-hosted TI tools crash during teardown | build.sh retries only when the output is missing; gspac needs -i. |
linker points cinit into .text |
empty .cinit section |
build.sh relinks with the one-word CINITZ.obj terminator |
XNextEvent never returns an event, keycode zero |
child's per-process Xlib context is NULL | src/vt1200/xgfix.c writes a zeroed 1 KiB block before XOpenDisplay (RAM only) |
| the game exits immediately at startup, sometimes | XOpenDisplay/window creation while the display server settles |
main() retries 40 × 50 ms, then returns 1 |
| window paints once, then hangs; a debug marker stops mid-flush | per-cell flushing overflowed the X socket / XSync per row deadlocked the client |
flush repaints whole rows (fill + one 80-char string + inverse spans + one XFlush) |
| playfield is a white block with invisible pieces | the 1-bit raster cannot show colour | backend drops colour and uses bold (0x80) as reverse; pieces are [/] glyphs |
blanked well border with stray X tiles at the top |
ExposureMask + map-time Expose runs the game's full redraw before it has a piece |
do not select ExposureMask (current code); if repainting on expose is ever needed, guard full_redraw against cur_piece < 0 |
Layout
apps/termtris/
├── build.sh TI r5.01 -> COFF (OUTNAME=termtris for the game)
├── run.sh interactive MAME session (lightgun/tablet input)
├── verify.sh one-command acceptance (see above)
├── VT1200.md port documentation
├── src/game.c upstream game; only srand(vt_rand_seed()) differs
├── src/vt1200/ main.c x11.c xgfix.c compat.c xlib.h scoredb.c *.asm
├── tests/ smoke fmt render input play play_keys (+ .lua drivers)
├── evidence/ screenshots
└── build/termtris.coff the built image (tracked for convenience)
The tests build from source every run (play.sh/play_keys.sh rebuild
termtris.coff), so an edit cannot silently pass by using a stale binary.