11 KiB
First contact with the real VT1200
Date: 2026-10-18. Terminal at 100.103.100.103 (static IP, no BOOTP),
reached from host 100.103.255.254 on enp197s0f3u1.
Read-only throughout this note — no poke, no jump.
Results
| check | result |
|---|---|
| TCP 771 | open |
| opcode 7 | challenge (flag 0x7F) sent, computed reply accepted, Done\n\0 |
peek 0x3F200000 (8 B) |
e9 7a e2 f5 aa aa 55 55 — matches vt1200_v22.bin |
peek 0xAECE0 (1 B) |
bd — message classes 0,1,2 admitted (emulator: 05) |
peek 0x1E8BA0 (4 B) |
0x018BF1B0 bits = 3,251,638 bytes free in the heap |
screenshot over peek |
dwt/evidence/real-unit-canvas.png |
The screenshot shows the live Terminal Manager V2.2: Ethernet
08-00-2B-16-4B-5A, IP 100.103.100.103, LAT LAT_08002B164B5A, and five
Debug connection from IP host 100.103.255.254 lines in Messages — one per
connection made here. That is the read primitive working on real hardware.
Two differences from the emulator, worth knowing
- The real unit challenges opcode 7. The emulator only challenges opcodes
≥ 2. A plain liveness check therefore exercises the challenge too, and its
success is additional evidence that
dwt/challenge.pymatches the shipped image. - Message filter is
0xBD, not0x05. Levels 0, 1 and 2 are admitted on the real unit; the emulator only admitted 0 and 3.
Geometry
The raw VRAM canvas is 1024×1024, captured correctly. The visible screen is
1280×1024, with the canvas at x=192; dwt/screenshot.py --screen uses the
emulator's 1280×901/offset-192×36 defaults and will misplace the bottom on the
real unit. The raw canvas is the authoritative artifact; for a faithful screen
image use --screen-width 1280 --screen-height 1024 --offset-x 192 --offset-y 0
once the vertical origin is measured.
Next
The remaining unproven step on this unit is the write path: poke/jump and
then the COFF loader + create_process. The loader already refuses a target
outside a free-list block (status 8), so a first attempt with marker.coff
will tell us whether the linked ranges are free before anything runs.
Display geometry and background (measured)
- Framebuffer 1024×1024, 128 bytes/row, LSB-first. A 160-byte pitch shears;
128 bytes is coherent. The 160-byte figure is the total raster
(1280/8), not the memory pitch (
RENDER_REPORT.md). - Visible raster 1280×1024 (monitor OSD: 77.2 kHz / 73 Hz; Samsung
SyncMaster 770 native 1280×1024). The 1024-wide canvas is displayed 5:4, so
dwt/screenshot.py --stretch 1280x1024matches the screen.--screen's emulator 1280×901 geometry pads/crops and looks line-doubled. - The
101010background is real data, not a decode error. It is a 50% stipple: rows alternate55(01010101) andff. The root background has a setting; setting it to 100% white changed the root to solidff(verified by a fresh grab). The Terminal Manager window kept its own stipple, so its window background is a separate setting (or was taken at creation). - The bottom message box
Ethernet Port Error - 24is present in VRAM; it is a real terminal error, not a capture artifact.
The TM stipple survives the root-background change
A fresh grab after the root was set to 100% white confirms: root 0xff,
TM body still alternating 0x55/0xff rows. So the TM pattern is not the
desktop background setting and the manager has no option for it.
The likely explanation is the display fetch, not the data: if the video
mode shows only one set of rows (line doubling / interlace), the 0xff rows are
what reaches the glass and the 0x55 rows are never shown — which is why the TM
looks white while the raw framebuffer looks striped. That also explains why the
capture looked "line doubled": it is showing both row sets.
The emulator forces the VR150 monitor position (IP4-IP6 high, 0x70).
The real unit's rear switch selects the mode; if it is at another position the
firmware may use a different fetch/pattern. Test: set the switch to position 1
(VR150), power-cycle, grab again.
Line-interleaved framebuffer — the "line doubled" answer
The real framebuffer is line-interleaved: even rows carry the image, odd
rows carry a 0x55 mask in window areas (and 0xff in the root). The display
doubles the even field, so the odd rows are never shown. A linear peek
reads both fields, which is why the first captures looked striped and
"line doubled".
Evidence: in the Messages area every even row is 0xff/text and every odd row
is a uniform 0x55; the root is 0xff on both. After the owner set the
background to 100% white and rebooted, the root is 0xff everywhere and the
TM's odd rows are still 0x55 — yet the screen is solid white. That proves the
odd rows are a non-displayed mask.
Correct capture:
python3 dwt/screenshot.py --host 100.103.100.103 \
--field even --stretch 1280x1024 --out screen.png
dwt/evidence/real-unit-white2.png is that capture and matches the glass.
This is a display-fetch difference, not a CPU-emulation difference. MAME's driver models a linear 1bpp framebuffer, so the emulator never shows the interleave; the real TMS34010 display controller is programmed for the doubled even field.
Correction: the framebuffer is 1024x2048, not line-doubled
The earlier "the display doubles the even field" reading was wrong. A 256 KiB read (2048 rows x 128 bytes) shows the 1024 image lines sitting on even rows at 1:1, with the odd rows carrying the mask. The earlier 128 KiB read held only the top 512 image lines; doubling them stretched the top half and cut the bottom — which is exactly the "missing left/bottom" and "line doubled" appearance.
Correct 1:1 capture:
python3 dwt/screenshot.py --host 100.103.100.103 --real --out screen.png
--real means --rows 0:2048 --field even and outputs 1024x1024 with no
resampling. dwt/evidence/real-unit-1to1.png is that capture: Icon Box,
Terminal Manager, Memory bar, and the Ethernet Port Error - 24 box at the
bottom — the whole screen.
--stretch 1280x1024 is only the monitor's 5:4 scaling of the 1024-wide
framebuffer; it is not 1:1 and the nearest-neighbour version produces the
uneven-pixel artefacts. Use it only for an approximate visual match, not as
evidence.
Final geometry: 1280x1024, 256-byte row stride
The real unit is 1280x1024 with a 256-byte row stride. Each row's first
160 bytes (1280 px) are the image; the remaining 96 bytes are a 0x55
mask. The earlier "1024x2048, even rows" reading was an artifact of treating
that 96-byte gap as an alternate row.
This is why the 1024-wide reads lost the right quarter: the Icon Box really is 1280 px wide and its resize buttons are at the far right, outside a 1024-px crop. The mask bytes are what looked like "line doubling".
Correct 1:1 capture:
python3 dwt/screenshot.py --host 100.103.100.103 --real --out screen.png
--real = width 1280, stride 256, 1024 rows, no resampling.
dwt/evidence/real-unit-1280-1to1.png is the full 1280x1024 screen, including
the Icon Box resize buttons and the Ethernet Port Error - 24 box.
The emulator geometry is unchanged: 1024x1024 with a 128-byte stride.
Background references
Both backgrounds are captured at the correct geometry:
dwt/evidence/real-unit-1280-1to1.png (100% white root) and
dwt/evidence/real-unit-1280-50pct.png (50% 1010 root, the owner's
preference). Only the root's image bytes change; the 256-byte stride and the
1280x1024 size are identical.
X11 app on the real unit (2026-10-18)
The host-side loader ran end to end against the terminal:
python3 dwt/loader/hostload.py --host 100.103.100.103 \
--coff dwt/loader/build/xhello.coff --real
jump(malloc) -> 0x007e7bf0
COFF 774 B poked (0 differing)
loader 1220 B @ 0x01880000 (0 differing)
loader A8 = 0; entry 0x01840000; pcb 0x003f6510
xhello stage=6 strlen=4
Evidence: dwt/evidence/real-unit-xhello.png — a 400x200 WM-framed black
window over the Terminal Manager, on the real screen.
Caveat: the loader and app are linked at fixed addresses (0x01880000,
0x01840000). Re-running while that app is live overwrites the running code.
Power-cycle (or choose new addresses) before loading again.
MOP console on the real unit
dwt/mop_console.py --iface enp197s0f3u1 --mac 08:00:2b:16:4b:5a works with no
password. SHOW returned:
Firmware Revision V2.2
Option Memory Present (SIMM 1/2/3 installed)
Image Option Not Present
Display Type Monochrome
Monitor Type VR319
Monitor Type VR319 is the answer to the geometry question: the unit is in the VR319 (19", 100 dpi) mode, not the emulator's forced VR150. That is why the real framebuffer is 1280x1024 with the 256-byte stride.
TEST 9 9 8 (Ethernet port test, LANCE loopback bit 3) was refused:
?ILLEGAL COMMAND - DWT IN USE
The console refuses diagnostics while the DWT has active work — our injected xhello window is the blocker. The service guide requires no open windows before remote tests: power-cycle (or close all session windows) and retry.
Diagnostic results are not observable through rdebug
dwt/mailbox_watch.py polled the mailbox pointer (0x27C190) and the level-1
dispatcher slot (0x16F0) across a power cycle. The rdebug service was only
connectable after the OS was up, at which point:
mailbox=0x00000000 slot=0x8ac00000 (OS data)
During the boot it was unusable (timeouts / AssertionError). So the level-1 result is consumed before rdebug exists; the mailbox is a dialog/debug structure, not the boot handoff. The only durable record is the screen: the boot alert, and the Diagnostics dialog's on-screen results.
Menu-driven Ethernet Address ROM test: PASS
After a clean power cycle, selecting Ethernet Address ROM Test in the Diagnostics dialog completed, the dialog closed, and the Terminal Manager's bottom notification reads:
All Diagnostics Passed
Evidence: dwt/evidence/real-unit-romtest-result.png.
This is the ROM checksum test (service-guide error 23 would be the
failure), and it passes on the real unit, consistent with MAME. The
boot-level Ethernet Port Error - 24 is the separate AUI/transceiver
termination test, not the ROM.
The first attempt hung the Terminal Manager; this one completed. The hang is not deterministic. The mailbox watcher's reconnect storm during boot is one possible co-factor (it spawns a debug process per accepted connection), but the diagnostic reset is the other; a single success does not settle which.
NVRAM and LANCE address ROM, read over rdebug
dwt/nvram_dump.py reads 4 KiB at bit 0x6000000 and extracts the 0x800
settings bytes (low byte of each 16-bit word). Evidence:
real-unit-nvram-settings.bin, real-unit-nvram-raw.bin.
Printable settings runs:
0x26f 100.103.0.10 0x28f 172.20.51.0
0x3cf 1.1.1.1 0x3ef 8.8.8.8
0x40f retro.k-space.ee
0x50f 100.103.0.1
0x597 ROM 0x5e7 ROM
0x5eb 103.255.254:69
0x637 100.103.100.103 0x657 255.255.0.0
0x677 Messages 0x6ca / 0x6ea VTE 1
Both font-path fields persist: the selected ROM and the old TFTP
103.255.254:69.
The LANCE address ROM at bit 0x6800000 reads (low bytes of the words):
08 00 2b 16 4b 5a c1 86
byte-identical to the MAME model. The Ethernet Address ROM Test passing is
consistent with this.