termtris (John Tsiombikas' game, upstream 7c5435d) as a native TMS34010 program running inside the VT1200: built with the 1990 TI r5.01 C toolchain under 2ine, loaded into main RAM by dwt/loader, drawing through the terminal's own ROM Xlib server, and reading real X11 KeyPress events. No host X client, no terminal escape output, no ROM patches.
The branch is based on master (17dfd24) and carries the whole port in 14 commits.
What is in the branch
Toolchain: apps/termtris/build.sh drives gspac/gspopt/gspcg/gspa/gsplnk under 2ine; handles the tools' teardown segfaults, gspac -i., and the empty-.cinit relink.
Runtime: src/vt1200/main.c (event loop, key map, ms clock), x11.c (80x24 cell grid, 6x13 cells, whole-row flush), xgfix.c (the per-process Xlib context fix), scoredb.c (RAM-only high scores), compat.c (stdio shims), entry.asm/cinit.asm.
Tests: apps/termtris/verify.sh runs smoke, fmt, render, input, play and play_keys. The play tests act at frame 2500 (loader) and 3095-3190 (keys).
Docs: VT1200.md, HANDOFF.md, harness/mame/TERMTRIS_REPORT.md, design + plan under docs/superpowers/.
Latest two commits (this round)
ce40582 — run the 2ine loader under qemu-i386 when the host kernel lacks CONFIG_X86_16BIT. The r5.01 tools are 16-bit OS/2 NE programs; such a kernel rejects their 16-bit LDT segments (modify_ldt returns EINVAL) even though lx_loader is 32-bit. build.sh probes for that and falls back to qemu's linux-user LDT, or honours LX_RUNNER=. The generated COFF is byte-identical except its header timestamp.
8df2320 — re-ran the full acceptance against a fresh MAME 0.289 build of this tree's driver at the VR319 default (the real unit's monitor, 1280x1024 active). All six tests pass; evidence PNGs regenerated at 1280x1024 (previously VR150 1024x864).
Event counts, focus=00400001, tick=887 and quit=1 are unchanged from the earlier VR150 run; only the loop counts moved with the frame cadence. The ROM md5 is b46a3e442ccb86134d2356bd70d22fd2 before and after.
Notes / limits
Scores are RAM-only; the terminal has no writable filesystem for a child.
q ends the game loop but the window stays on screen (the created process does not tear down its X connection).
Startup retries XOpenDisplay up to 40x50 ms while the X server settles.
Tetrominoes are [/] text tiles; the raster is 1-bit.
Review pass (since the first push)
A fresh review found a regression in the new runner code plus doc and hygiene issues; fixed in 4c7d94e and 58f8f44:
build.sh expanded an unset LX_RUNNER on kernels that do support 16-bit LDT (fatal under set -u) — fixed with a default and ${LX_RUNNER:-}.
Stage retries now clear the expected output and require a non-empty file; a real failure prints the loader output instead of discarding it.
run.sh --build now passes OUTNAME=termtris (it was building main.coff and then running a stale tracked binary).
Stopped tracking generated build/termtris.coff/.map and git-ignored them.
Corrected the build commands, .cinit (018B4A60), srand/0x7F/focus wording; recorded the ExposureMask occlusion limit and the spec deviation.
Full suite re-run after the fixes: verify: PASS, same acceptance numbers.
Deferred as follow-up: sharing the six copies of the loader Lua harness, and a guarded Expose repaint.
## What this is
`termtris` (John Tsiombikas' game, upstream `7c5435d`) as a **native TMS34010 program running inside the VT1200**: built with the 1990 TI r5.01 C toolchain under 2ine, loaded into main RAM by `dwt/loader`, drawing through the terminal's own ROM Xlib server, and reading real X11 `KeyPress` events. No host X client, no terminal escape output, no ROM patches.
The branch is based on `master` (17dfd24) and carries the whole port in 14 commits.
## What is in the branch
- **Toolchain**: `apps/termtris/build.sh` drives `gspac`/`gspopt`/`gspcg`/`gspa`/`gsplnk` under 2ine; handles the tools' teardown segfaults, `gspac -i.`, and the empty-`.cinit` relink.
- **Runtime**: `src/vt1200/main.c` (event loop, key map, ms clock), `x11.c` (80x24 cell grid, 6x13 cells, whole-row flush), `xgfix.c` (the per-process Xlib context fix), `scoredb.c` (RAM-only high scores), `compat.c` (stdio shims), `entry.asm`/`cinit.asm`.
- **Tests**: `apps/termtris/verify.sh` runs smoke, fmt, render, input, play and play_keys. The play tests act at frame 2500 (loader) and 3095-3190 (keys).
- **Docs**: `VT1200.md`, `HANDOFF.md`, `harness/mame/TERMTRIS_REPORT.md`, design + plan under `docs/superpowers/`.
## Latest two commits (this round)
- `ce40582` — run the 2ine loader under `qemu-i386` when the host kernel lacks `CONFIG_X86_16BIT`. The r5.01 tools are 16-bit OS/2 NE programs; such a kernel rejects their 16-bit LDT segments (`modify_ldt` returns `EINVAL`) even though `lx_loader` is 32-bit. `build.sh` probes for that and falls back to qemu's linux-user LDT, or honours `LX_RUNNER=`. The generated COFF is byte-identical except its header timestamp.
- `8df2320` — re-ran the full acceptance against a fresh MAME 0.289 build of this tree's driver at the **VR319 default** (the real unit's monitor, 1280x1024 active). All six tests pass; evidence PNGs regenerated at 1280x1024 (previously VR150 1024x864).
## Verification
```
apps/termtris/verify.sh --skip-mame
smoke PASS, fmt PASS, render PASS, input PASS, play PASS, play_keys PASS
verify: 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
```
Event counts, `focus=00400001`, `tick=887` and `quit=1` are unchanged from the earlier VR150 run; only the loop counts moved with the frame cadence. The ROM md5 is `b46a3e442ccb86134d2356bd70d22fd2` before and after.
## Notes / limits
- Scores are RAM-only; the terminal has no writable filesystem for a child.
- `q` ends the game loop but the window stays on screen (the created process does not tear down its X connection).
- Startup retries `XOpenDisplay` up to 40x50 ms while the X server settles.
- Tetrominoes are `[`/`]` text tiles; the raster is 1-bit.
## Review pass (since the first push)
A fresh review found a regression in the new runner code plus doc and hygiene issues; fixed in `4c7d94e` and `58f8f44`:
- `build.sh` expanded an unset `LX_RUNNER` on kernels that *do* support 16-bit LDT (fatal under `set -u`) — fixed with a default and `${LX_RUNNER:-}`.
- Stage retries now clear the expected output and require a non-empty file; a real failure prints the loader output instead of discarding it.
- `run.sh --build` now passes `OUTNAME=termtris` (it was building `main.coff` and then running a stale tracked binary).
- Stopped tracking generated `build/termtris.coff`/`.map` and git-ignored them.
- Corrected the build commands, `.cinit` (`018B4A60`), `srand`/`0x7F`/focus wording; recorded the `ExposureMask` occlusion limit and the spec deviation.
- Full suite re-run after the fixes: `verify: PASS`, same acceptance numbers.
Deferred as follow-up: sharing the six copies of the loader Lua harness, and a guarded `Expose` repaint.
Board, score panel, preview and help/scores toggles draw through the
ROM Xlib cell grid; keys arrive as real X KeyPress events. The backend
is now monochrome (the VT1200 raster is one bit deep) and flushes whole
rows so the in-machine server can drain the request stream.
- seed the game from the raw 0x1E93C0 counter (now_ms() returns 0 on its
first call, so the old seed was always zero)
- track pause ourselves: FocusOut pauses once, FocusIn resumes only what we
paused (game_input('p') is a toggle)
- select ExposureMask so the Expose redraw path actually fires
- play.sh/play_keys.sh rebuild the COFF (OUTNAME=termtris) and assert the
run: status=0, tick>0, loops/events/quit per mode; render.sh asserts the
renderer finished; map symbol lookup is column-order independent
- run.sh passes -lightgun -lightgun_device mouse so the pointer drives the
tablet
- docs: link map addresses, monochrome/reverse wording, honest caption and
limits text
Verified here: play.sh and input.sh pass. Still open: the MAME driver in the
tree is newer than the installed binary (Oct 30 build, VR150-era geometry),
so the acceptance suite must be re-run after a rebuild.
apps/termtris/verify.sh runs the whole acceptance: ROM md5 guard, 2ine
check, MAME build (--skip-mame to reuse one), the six tests, the
acceptance summary and the evidence list, all logged under build/.
HANDOFF.md states what is done, what is still open (the docs' figures and
screenshots predate the VR319 default because the installed MAME binary is
the 30 Oct build), the expected outputs, and the failure modes already
diagnosed. Refresh the acceptance numbers from the verify run:
348/19, 338/13, 292/11.
A map-time Expose arrives before the game has a current piece, and the
game's full-redraw path then draws a bogus piece over the top row: visible
as a blanked well border with stray X tiles (the previous commit's
screenshots showed it). The window is never obscured in this session, so
drop the mask, drop the Expose branch, and note the reason in HANDOFF.md.
Refreshed with verify.sh: move 354/18, panels 346/12, quit 300/10, all
with focus=00400001; evidence re-captured and clean.
The r5.01 tools are 16-bit OS/2 NE programs. A Linux kernel built
without CONFIG_X86_16BIT (this box's cloud kernel) rejects modify_ldt
for 16-bit segments, so lx_loader aborts at its first segment even
though it is a 32-bit binary. Probe for that failure and use
qemu-i386, whose linux-user modify_ldt supports 16-bit segments.
LX_RUNNER overrides the choice. The toolchain output is unchanged:
the build completes in ~10 s and the COFF differs only in its header
timestamp.
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.
Review findings on the port:
- run() expanded ${LX_RUNNER} while it was unset on the normal path,
which is a fatal unbound-variable error under `set -eu`; the probe
only sets it on kernels without 16-bit LDT support. Default it to
empty and expand with ${LX_RUNNER:-}.
- Clear each stage's expected output before every retry and require a
non-empty result, so a stale or partially written file cannot be
mistaken for success. Print the loader's captured output when a
stage really fails instead of discarding it.
- Widen the probe to recognise "modify_ldt" as well as the exact abort
message.
- run.sh --build passed no OUTNAME, so it built build/main.coff and
then ran the tracked build/termtris.coff. Build termtris.coff.
- Stop tracking the generated build/termtris.coff and .map: they are
rebuilt by every acceptance run, churn on the COFF timestamp, and
fed the stale-binary trap above. Add them to .gitignore.
- Correct verify.sh's header comment (it runs all tests, then fails).
Review findings on the documentation:
- The build blocks did not cd, and used paths relative to the repo root
while build.sh resolves them from the caller's cwd; VT1200.md also
omitted OUTNAME, so it produced build/main.coff. Show
`cd apps/termtris && OUTNAME=termtris ./build.sh ...`.
- The link map quoted .cinit at 018B4B10; it moved to 018B4A60 when the
Expose branch was removed, and the loader log confirms it.
- VT1200.md still credited the seed portability edit to now_ms(); the
code uses vt_rand_seed().
- Treat 0x7F as Delete and 0x08 as Backspace, and stop claiming
FocusIn/FocusOut tracks the game's own manual pause (it does not).
- Say that focus=00400001 is sampled on the click's ButtonRelease;
quit=1 is the key-delivery proof.
- Record the real ExposureMask consequence (an occluding window is not
repainted) in VT1200.md's Limits and as a deviation in the design
spec, instead of only in HANDOFF.md.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What this is
termtris(John Tsiombikas' game, upstream7c5435d) as a native TMS34010 program running inside the VT1200: built with the 1990 TI r5.01 C toolchain under 2ine, loaded into main RAM bydwt/loader, drawing through the terminal's own ROM Xlib server, and reading real X11KeyPressevents. No host X client, no terminal escape output, no ROM patches.The branch is based on
master(17dfd24) and carries the whole port in 14 commits.What is in the branch
apps/termtris/build.shdrivesgspac/gspopt/gspcg/gspa/gsplnkunder 2ine; handles the tools' teardown segfaults,gspac -i., and the empty-.cinitrelink.src/vt1200/main.c(event loop, key map, ms clock),x11.c(80x24 cell grid, 6x13 cells, whole-row flush),xgfix.c(the per-process Xlib context fix),scoredb.c(RAM-only high scores),compat.c(stdio shims),entry.asm/cinit.asm.apps/termtris/verify.shruns smoke, fmt, render, input, play and play_keys. The play tests act at frame 2500 (loader) and 3095-3190 (keys).VT1200.md,HANDOFF.md,harness/mame/TERMTRIS_REPORT.md, design + plan underdocs/superpowers/.Latest two commits (this round)
ce40582— run the 2ine loader underqemu-i386when the host kernel lacksCONFIG_X86_16BIT. The r5.01 tools are 16-bit OS/2 NE programs; such a kernel rejects their 16-bit LDT segments (modify_ldtreturnsEINVAL) even thoughlx_loaderis 32-bit.build.shprobes for that and falls back to qemu's linux-user LDT, or honoursLX_RUNNER=. The generated COFF is byte-identical except its header timestamp.8df2320— re-ran the full acceptance against a fresh MAME 0.289 build of this tree's driver at the VR319 default (the real unit's monitor, 1280x1024 active). All six tests pass; evidence PNGs regenerated at 1280x1024 (previously VR150 1024x864).Verification
Event counts,
focus=00400001,tick=887andquit=1are unchanged from the earlier VR150 run; only the loop counts moved with the frame cadence. The ROM md5 isb46a3e442ccb86134d2356bd70d22fd2before and after.Notes / limits
qends the game loop but the window stays on screen (the created process does not tear down its X connection).XOpenDisplayup to 40x50 ms while the X server settles.[/]text tiles; the raster is 1-bit.Review pass (since the first push)
A fresh review found a regression in the new runner code plus doc and hygiene issues; fixed in
4c7d94eand58f8f44:build.shexpanded an unsetLX_RUNNERon kernels that do support 16-bit LDT (fatal underset -u) — fixed with a default and${LX_RUNNER:-}.run.sh --buildnow passesOUTNAME=termtris(it was buildingmain.coffand then running a stale tracked binary).build/termtris.coff/.mapand git-ignored them..cinit(018B4A60),srand/0x7F/focus wording; recorded theExposureMaskocclusion limit and the spec deviation.verify: PASS, same acceptance numbers.Deferred as follow-up: sharing the six copies of the loader Lua harness, and a guarded
Exposerepaint.- seed the game from the raw 0x1E93C0 counter (now_ms() returns 0 on its first call, so the old seed was always zero) - track pause ourselves: FocusOut pauses once, FocusIn resumes only what we paused (game_input('p') is a toggle) - select ExposureMask so the Expose redraw path actually fires - play.sh/play_keys.sh rebuild the COFF (OUTNAME=termtris) and assert the run: status=0, tick>0, loops/events/quit per mode; render.sh asserts the renderer finished; map symbol lookup is column-order independent - run.sh passes -lightgun -lightgun_device mouse so the pointer drives the tablet - docs: link map addresses, monochrome/reverse wording, honest caption and limits text Verified here: play.sh and input.sh pass. Still open: the MAME driver in the tree is newer than the installed binary (Oct 30 build, VR150-era geometry), so the acceptance suite must be re-run after a rebuild.Review findings on the port: - run() expanded ${LX_RUNNER} while it was unset on the normal path, which is a fatal unbound-variable error under `set -eu`; the probe only sets it on kernels without 16-bit LDT support. Default it to empty and expand with ${LX_RUNNER:-}. - Clear each stage's expected output before every retry and require a non-empty result, so a stale or partially written file cannot be mistaken for success. Print the loader's captured output when a stage really fails instead of discarding it. - Widen the probe to recognise "modify_ldt" as well as the exact abort message. - run.sh --build passed no OUTNAME, so it built build/main.coff and then ran the tracked build/termtris.coff. Build termtris.coff. - Stop tracking the generated build/termtris.coff and .map: they are rebuilt by every acceptance run, churn on the COFF timestamp, and fed the stale-binary trap above. Add them to .gitignore. - Correct verify.sh's header comment (it runs all tests, then fails).