mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-11-01 12:14:56 +02:00
fix(video): model the real unit's VR319 monitor
The harness forced the VR150 monitor switch, so the firmware wrote the OS screen at 1024 px / 128-byte rows. VT1200_VRAM_REAL then re-read that framebuffer at the VR319 pitch, which sheared the OS and wrapped the right quarter of every row. Model VR319 (switch 0x60) by default -- the real unit's mode -- so the firmware itself programs 1280x1024 with a 256-byte row pitch (CONVDP=0x14). The renderer's pitch follows VT1200_MONITOR, and the destination span now comes from HEBLNK..HSBLNK instead of x=0, placing the active 1280 px at x=336 in MAME's 1696-wide raster. Verified: clean Terminal Manager at frame 600, legible faithful diagnostics menu and level-1 error screens, tile mode PASS with tile_check 1280/1280 tiles, tablet/mouse pixel-identical. VT1200_MONITOR=VR150 reproduces the old geometry; the regression baseline moves 198 -> 199 (2 PCs) and is recorded in harness/mame/README.md.
This commit is contained in:
1 parent
eabc3051be
commit
8f831eaf33
12 files changed
+253
-128
No files matched your search
@@ -262,19 +262,23 @@ Messages, Ethernet/IP/LAT, a Memory bar, and "All Diagnostics Passed". The
|
||||
keyboard and mouse drivers initialise and speak the real protocol; the LANCE is
|
||||
initialised and the TM shows the real hardware address
|
||||
`08-00-2B-16-4B-5A`. Evidence frames: `harness/mame/skip_frame3000.png`,
|
||||
`duart2_default_tm_frame3000.png`, `tablet_cursor_*.png`.
|
||||
`duart2_default_tm_frame3000.png`, `tablet_cursor_*.png` (VR150 captures; the
|
||||
VR319 default renders the same UI at the real 1280-px placement — see
|
||||
`harness/mame/RENDER_REPORT.md` §8).
|
||||
|
||||
### Regression baseline
|
||||
|
||||
`progress.lua` at 200 frames, **no env**:
|
||||
|
||||
```
|
||||
PC=3FDCE400 samples=198 (3 distinct PCs)
|
||||
PC=3FDCE400 samples=199 (2 distinct PCs)
|
||||
```
|
||||
|
||||
It is a change detector, not a specification. It has moved twice, both
|
||||
deliberately; `harness/mame/README.md` records why, including the
|
||||
`VT1200_IO_PATH_STUB=1` value (`samples=53`, 75 PCs) as a separate baseline.
|
||||
It is a change detector, not a specification. It has moved three times, all
|
||||
deliberately; `harness/mame/README.md` records why, including the monitor-default
|
||||
move (VR319 is the real unit's mode) and the `VT1200_IO_PATH_STUB=1` values
|
||||
(`samples=97` at the VR319 default, `samples=53` with `VT1200_MONITOR=VR150`) as
|
||||
separate baselines.
|
||||
|
||||
### Deliberate divergences
|
||||
|
||||
@@ -284,8 +288,7 @@ deliberately; `harness/mame/README.md` records why, including the
|
||||
| `VT1200_SKIP_LEVEL1` | off | DUART1 IP3 high → skip all level-1 tests | leave unset |
|
||||
| `VT1200_IO_PATH_STUB` | off | 10 bytes after the level-0 `0xE1` write | leave unset |
|
||||
|
||||
The full env-knob table (21 knobs, down from 33) is in
|
||||
`harness/mame/README.md`.
|
||||
The full env-knob list is in `harness/mame/README.md`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -72,14 +72,16 @@ selects `taptun`; the tap is `tap-mess-<uid>-0` in bridge `vt1200`,
|
||||
`192.168.99.1/24`):
|
||||
|
||||
```sh
|
||||
VT1200_VRAM_REAL=1 harness/mame/run.sh net
|
||||
harness/mame/run.sh net
|
||||
```
|
||||
|
||||
`VT1200_VRAM_REAL=1` renders the framebuffer at the **real unit's** geometry
|
||||
(2048-bit / 256-byte rows, 1280 px). Without it the window uses the driver's
|
||||
derived 128-byte pitch and the tile picture is sheared — the divergence is
|
||||
documented in `harness/mame/RENDER_REPORT.md` §8, and it is a property of the
|
||||
renderer, not of the demo.
|
||||
The harness now models the real unit's **VR319** monitor, so the firmware itself
|
||||
uses the **1280-px / 2048-bit (256-byte) row layout** the tile payload targets —
|
||||
and the Terminal Manager before it renders correctly at the same geometry. No
|
||||
`VT1200_VRAM_REAL` is needed (that knob forced the pitch without changing the
|
||||
firmware's layout, which is what used to shear the OS screen). To reproduce the
|
||||
old derived 1024-px layout, run with `VT1200_MONITOR=VR150`; the tile picture
|
||||
will then be sheared, as before. See `harness/mame/RENDER_REPORT.md` §8.
|
||||
|
||||
Terminal 2 — the demo:
|
||||
|
||||
@@ -143,5 +145,5 @@ path; for a live run the screen is the evidence.
|
||||
| `demo.sh: ... did not answer within 180s` | the terminal is not on the tap, or `run.sh net` was started without the LANCE interface selected in its cfg |
|
||||
| poke reads back a mismatch | the image is being loaded over a stale link, or the payload was built for a different load address |
|
||||
| `DICT_BYTES` stuck below 32768 | datagrams lost before the payload saw them — check the tap and the sender's interface |
|
||||
| picture sheared in MAME | the window is rendering at the derived 128-byte pitch; use `VT1200_VRAM_REAL=1` |
|
||||
| picture sheared in MAME | `VT1200_MONITOR=VR150` is selected (the old derived 1024-px layout); leave the default VR319 |
|
||||
| `jump` seems to hang the service | expected: the payload never returns, so rdebug is gone until the machine is rebooted |
|
||||
@@ -3,8 +3,9 @@
|
||||
# deliver the payload over rdebug, then send the clip with the host sender.
|
||||
#
|
||||
# The terminal itself is started separately -- `harness/mame/run.sh net` for the
|
||||
# emulator (add VT1200_VRAM_REAL=1 to see the picture at the real unit's
|
||||
# framebuffer layout), or a real VT1200 on the network. This script only talks
|
||||
# emulator (its default VR319 monitor already gives the real unit's 1280-px /
|
||||
# 256-byte framebuffer layout; `VT1200_MONITOR=VR150` reproduces the old derived
|
||||
# 1024-px view), or a real VT1200 on the network. This script only talks
|
||||
# to it, so one instance serves one demo: the jump hands the payload the
|
||||
# remote-debug process and it never returns.
|
||||
#
|
||||
|
||||
@@ -92,12 +92,14 @@ Success = loader status 0, `entry=0x018A17E0`, markers `48454C4C`/`444F4E45`
|
||||
the image must not need static initializers. The font and order tables are
|
||||
filled in `build_font()` at runtime.
|
||||
|
||||
## Geometry is not portable between the real unit and MAME
|
||||
## Geometry is not portable between the real unit and MAME's VR150 mode
|
||||
|
||||
The two framebuffers differ, and the payload carries the difference as
|
||||
`ROW_BYTES`:
|
||||
`ROW_BYTES`. **The harness now defaults to the real unit's VR319 monitor**, so
|
||||
MAME's default geometry is the real one; `VT1200_MONITOR=VR150` restores the old
|
||||
MAME layout (and needs the `build.sh emu` image).
|
||||
|
||||
| | real unit (measured) | MAME |
|
||||
| | real unit (measured) = MAME VR319 (default) | MAME VR150 |
|
||||
|---|---|---|
|
||||
| base | bit `0x04000000` | bit `0x04000000` |
|
||||
| row pitch | **256 bytes** (`ROW_BYTES 256`) | 128 bytes (`VMEM_PITCH_BITS`) |
|
||||
|
||||
@@ -24,7 +24,8 @@ DOSBOX_CONF="$ROOT/harness/sdk-scratch/dosbox-bench.conf"
|
||||
|
||||
TEXT=018A0000
|
||||
BSS=01A00000
|
||||
# `build.sh emu` builds a 128-byte-stride variant for MAME's 1024x1024 canvas.
|
||||
# `build.sh emu` builds a 128-byte-stride variant for the harness's VR150 mode
|
||||
# (VT1200_MONITOR=VR150); the default VR319 layout matches the real unit.
|
||||
MODE=${1:-real}
|
||||
NAME=hello
|
||||
if [ "$MODE" = "emu" ]; then NAME=hello-emu; fi
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
* Writes the word HELLO straight into video RAM, no X11 involved:
|
||||
*
|
||||
* framebuffer bit 0x04000000, 256-byte row pitch on the real unit
|
||||
* (the emulator uses 128; this image is linked for the real one)
|
||||
* (the harness now defaults to the same VR319 layout;
|
||||
* only VT1200_MONITOR=VR150 uses 128)
|
||||
* pixels 1 bpp, LSB-first within each byte
|
||||
* text area black 256x72 box at x=40 y=800, white 5x7 glyphs scaled 8x
|
||||
*
|
||||
|
||||
@@ -16,6 +16,12 @@ short version:
|
||||
* both are observed from inside the emulator (RAM readback) and from a frame
|
||||
dump (white block at screen x 256..511, y 136..199).
|
||||
|
||||
> **Pitch note.** `hello.asm` writes 1024-bit rows (the old derived layout).
|
||||
> The harness now defaults to the real unit's **VR319** 2048-bit layout, so add
|
||||
> `VT1200_MONITOR=VR150` to the run below to see the block where this README
|
||||
> says it is; at the VR319 default the rendered block is sheared because the
|
||||
> payload, not the firmware, chose the old pitch.
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
|
||||
+59
-28
@@ -109,10 +109,13 @@ to be a six-variable incantation; it is now the default, and a bare
|
||||
|
||||
* Video RAM at bit `0x04000000` — required for the level-1 VIDEO MEMORY test.
|
||||
* The board-window handshake model (two-tick busy bit).
|
||||
* MAME's `SCN2681` DUART at **DUART1** (`0x0640000`, host/printer), with input
|
||||
port **IP4–IP6 held high** — that is how the driver reads the VR150 monitor
|
||||
switch (`0x70`). The level-1 host-port transmit is **ISR-driven**, so a real
|
||||
DUART is required here, not a convenience.
|
||||
* MAME's `SCN2681` DUART at **DUART1** (`0x0640000`, host/printer), whose input
|
||||
port **IP4–IP6** carries the monitor-selection switch. The default is
|
||||
**VR319 (`0x60`) — the real unit's mode** (see `dwt/notes/REAL_UNIT.md`), so
|
||||
the firmware itself programs a **1280×1024 display at a 256-byte (2048-bit)
|
||||
row pitch** and `CONVDP = 0x14`. `VT1200_MONITOR=VR150|VR262|VR315` selects
|
||||
another position for comparison. The level-1 host-port transmit is
|
||||
**ISR-driven**, so a real DUART is required here, not a convenience.
|
||||
* MAME's `SCN2681` at **DUART2** (`0x0620000`, keyboard/mouse). This one is a
|
||||
*faithfulness* fix, not a convenience — the real board has one. Without it the
|
||||
board write-back latch answers a read of DUART2's **write-only** IMR with its
|
||||
@@ -126,7 +129,11 @@ to be a six-variable incantation; it is now the default, and a bare
|
||||
`FD`/`T` because that is the protocol, not to satisfy a test. See
|
||||
`INPUT_REPORT.md` and `TABLET_REPORT.md`.
|
||||
* A mono palette (pens 0/1 = black/white) and an active-pixel scanline renderer
|
||||
that reads VRAM. See `RENDER_REPORT.md`.
|
||||
that reads VRAM at the pitch the modelled monitor implies: 2048-bit rows /
|
||||
1280 px for VR319 (the default), 1024-bit rows / 1024 px for VR150. The
|
||||
destination span always comes from the display controller's own
|
||||
`HEBLNK..HSBLNK`, so the active picture sits at `x = 336` in MAME's
|
||||
1696-wide VR319 raster, not at `x = 0`. See `RENDER_REPORT.md`.
|
||||
* The **LANCE Ethernet controller** — MAME's `am79c90` (AM79C90) at the
|
||||
firmware's two register windows (**bit `0x5900000` = RAP, `0x5800000` = RDP**),
|
||||
with its bus-master DMA bound (LANCE byte `B` → main-RAM word `B >> 1`) and its
|
||||
@@ -204,8 +211,10 @@ over to the terminal's own mouse cursor, and the V2.2 box appears — title bar,
|
||||
Messages, Ethernet/IP/LAT, a **Memory bar**, and "All Diagnostics Passed".
|
||||
|
||||
Confirmed in the GUI and from frame dumps: `skip_frame3000.png` (the weave) and
|
||||
`duart2_default_tm_frame3000.png` (the Terminal Manager). The real terminal shows
|
||||
the same sequence — see `reference/vt1200-boot-video/`.
|
||||
`duart2_default_tm_frame3000.png` (the Terminal Manager). Those two files are
|
||||
**VR150** captures; at the VR319 default the same Terminal Manager renders at the
|
||||
real 1280-px placement (`VT1200_DUMP_FRAMES=600`, see `RENDER_REPORT.md` §8). The
|
||||
real terminal shows the same sequence — see `reference/vt1200-boot-video/`.
|
||||
|
||||
The OS's keyboard and mouse drivers **initialise and speak the real protocol**: it
|
||||
programs DUART2 (`MR1A=0x13`, `MR2A=0x07`, `CSRA/CSRB=0x99`, `ACR=0x60`,
|
||||
@@ -269,12 +278,13 @@ Terminal Manager usable. It is a **value-aware inverse switch**: the default
|
||||
lives in the code, not in a wrapper script setting `VT1200_TABLET`, so
|
||||
`VT1200_MOUSE=1` (or `VT1200_MOUSE=0` for the tablet) always flips it either way.
|
||||
|
||||
## Environment knobs (22; down from 33)
|
||||
## Environment knobs (23; down from 33)
|
||||
|
||||
Almost all are opt-in and off by default — there is no longer a "working
|
||||
incantation" to remember, because everything the firmware needs to run is now the
|
||||
default. The one exception is that the **tablet is on by default**; `VT1200_MOUSE`
|
||||
is the switch that restores the faithful mouse (see the divergences above).
|
||||
default. The two exceptions are the **tablet** (on by default; `VT1200_MOUSE`
|
||||
restores the faithful mouse) and the **VR319 monitor** (on by default because
|
||||
that is the real unit; `VT1200_MONITOR` selects VR150 and friends).
|
||||
|
||||
Instrumentation: `VT1200_DUARTLOG`, `VT1200_LANCELOG` (every LANCE RAP/RDP and
|
||||
address-ROM access with the PC), `VT1200_LANCE_DMA_LOG` (every LANCE bus-master
|
||||
@@ -283,10 +293,13 @@ the descriptor write-back are read directly), `VT1200_BOARD_LOG`, `VT1200_BOARD_
|
||||
`VT1200_IRQLOG`, `VT1200_LOG_UNMAPPED`, `VT1200_DPYLOG` (+`_FRAMES`),
|
||||
`VT1200_DUMP_FRAME` (+`_FRAMES`), `VT1200_DUMP_RAM` (+`_FRAMES`),
|
||||
`VT1200_DUMP_VRAM` (+`_FRAMES`), `VT1200_DUMP_DIR`, `VT1200_NO_RENDER`,
|
||||
`VT1200_VRAM_REAL` (draw the framebuffer at the **real unit's** 2048-bit /
|
||||
256-byte row pitch, 1280 px wide, instead of the derived 1024-bit / 128-byte
|
||||
pitch — the view for content written for the hardware, e.g. the tile stream;
|
||||
off by default, see `RENDER_REPORT.md`),
|
||||
`VT1200_MONITOR` (monitor-selection switch: `VR319` default / `VR150` / `VR262`
|
||||
/ `VR315`, or a raw position 0–7; it also picks the renderer's row pitch.
|
||||
Positions other than 7/5/3/6 are the firmware's "Undefined monitor switch
|
||||
setting" branch),
|
||||
`VT1200_VRAM_REAL` (force the 2048-bit / 256-byte / 1280-px renderer pitch
|
||||
regardless of `VT1200_MONITOR`; only meaningful when the firmware itself is
|
||||
writing that layout, which the VR319 default does),
|
||||
`VT1200_INPUT_LOG` (per-byte keyboard/mouse/tablet TX/RX trace, with emulated
|
||||
time). Behaviour switches: `VT1200_SKIP_LEVEL1`, `VT1200_MOUSE` (put the
|
||||
VSXXX-AA mouse (relative) back on DUART2-B; the VSXXX-AB tablet is the default —
|
||||
@@ -299,10 +312,10 @@ described above — not a keyboard model, off by default).
|
||||
`progress.lua` at 200 frames, **no env**:
|
||||
|
||||
```
|
||||
PC=3FDCE400 samples=198 (3 distinct PCs)
|
||||
PC=3FDCE400 samples=199 (2 distinct PCs)
|
||||
```
|
||||
|
||||
This has moved **twice**, both deliberately:
|
||||
This has moved **three** times, all deliberately:
|
||||
|
||||
1. `samples=199` (1 PC) → `samples=53` (78 PCs): the working configuration became
|
||||
the default, so a bare run stopped wedging at the hardware-ready gate and began
|
||||
@@ -312,6 +325,15 @@ This has moved **twice**, both deliberately:
|
||||
by the write-back latch's phantom `RXRDY`, so the run parks in the diagnostics
|
||||
menu. That looks like a regression and is not one — it is the removal of a
|
||||
false pass.
|
||||
3. `samples=198` (3 PCs) → `samples=199` (2 PCs): the monitor switch became
|
||||
**VR319**, the real unit's mode, instead of the old VR150. The run still parks
|
||||
in the same level-0 diagnostics menu (verified with frame dumps at 200 and
|
||||
600); only the number of frames that land on each of the menu's two
|
||||
instructions changed, because the VR319 raster (1696×1063 total, 1280×1024
|
||||
active) runs at a different emulated frame rate than VR150 (1280×901,
|
||||
1024×864 active). `VT1200_MONITOR=VR150` reproduces the old
|
||||
`samples=198 (3 PCs)` line exactly, which is the control that shows the move
|
||||
is geometry, not a behaviour change.
|
||||
|
||||
**The tablet-default change did NOT move this baseline** (198 → 198), and that is
|
||||
expected, not an oversight. The tablet changes what DUART2-B carries on the wire
|
||||
@@ -321,17 +343,22 @@ it then frames 5-byte absolute reports instead of 3-byte relative ones — but a
|
||||
has never addressed DUART2-B at all: the `T`/`R` exchange happens only in the OS
|
||||
phase, which the no-env run does not reach. The only DUART2-B traffic in the
|
||||
window is the device's unsolicited power-up self-test, which nothing clocks in.
|
||||
The OS-phase render is unchanged too: with `VT1200_SKIP_LEVEL1=1` the frame-3000
|
||||
The OS-phase render is unchanged too: with `VT1200_SKIP_LEVEL1=1` the frame-600
|
||||
dump is **pixel-identical** for the tablet and for `VT1200_MOUSE=1`
|
||||
(`magick compare -metric AE` = 0), and both reproduce the committed
|
||||
`tablet_cursor_*.png` frames exactly.
|
||||
(`magick compare -metric AE` = 0, same md5). That was re-measured at the VR319
|
||||
default; the committed `tablet_cursor_*.png` frames are VR150 evidence and are
|
||||
reproduced with `VT1200_MONITOR=VR150`.
|
||||
|
||||
**The no-env value is unchanged by `VT1200_IO_PATH_STUB`** — stub off is the
|
||||
default, so the `samples=198 (3 PCs)` line above still holds. The stub is a separate
|
||||
observed configuration:
|
||||
default, so the `samples=199 (2 PCs)` line above still holds. The stub is a separate
|
||||
observed configuration. Its value depends on the monitor, because frame count is
|
||||
not emulated time; both are recorded here:
|
||||
|
||||
```
|
||||
# VT1200_IO_PATH_STUB=1, progress.lua at 200 frames
|
||||
# VT1200_IO_PATH_STUB=1, progress.lua at 200 frames, VR319 (default)
|
||||
PC=3FDCE400 samples=97 (69 distinct PCs)
|
||||
|
||||
# VT1200_IO_PATH_STUB=1 VT1200_MONITOR=VR150, progress.lua at 200 frames
|
||||
PC=3FDCE400 samples=53 (75 distinct PCs)
|
||||
```
|
||||
|
||||
@@ -340,10 +367,11 @@ satisfied, the run leaves the level-0 wait and executes level-0/level-1 work. It
|
||||
is **not** parked at `HOST PORT - 15`: the console transcript continues
|
||||
(`LEVEL1/2 TESTING **`, `LEVEL1/2 ERROR -> HOST PORT - 15`,
|
||||
`LEVEL1/2 ERROR -> PRINTER PORT - 16`, `TYPE 'GO' TO CONTINUE`), and the run ends
|
||||
in a DUART1 counter-ready **interrupt storm** (from frame ~2839; PC in the DUART1
|
||||
INT1 ISR, polling ISR reg 5 = `0x08`). See `LEVEL1_CONTINUE_REPORT.md`. It is
|
||||
recorded as a *different* baseline because it is opt-in; the default must stay
|
||||
`samples=198`.
|
||||
in a DUART1 counter-ready **interrupt storm** (at VR150, from frame ~2839; PC in
|
||||
the DUART1 INT1 ISR, polling ISR reg 5 = `0x08`). See
|
||||
`LEVEL1_CONTINUE_REPORT.md`. It is recorded as a *different* baseline because it
|
||||
is opt-in; the default must stay `samples=199` (`samples=53` with
|
||||
`VT1200_MONITOR=VR150`).
|
||||
|
||||
It is a change detector, not a specification. If it moves without you meaning it
|
||||
to, find out why.
|
||||
@@ -447,6 +475,7 @@ would reach a real terminal.
|
||||
|
||||
```sh
|
||||
M=/home/arti/code/k-space/mame0289/vt1200
|
||||
VT1200_MONITOR=VR150 \
|
||||
HELLO_END=150 VT1200_DUMP_FRAME=145 VT1200_DUMP_RAM=145 \
|
||||
VT1200_DUMP_DIR=harness/hello-world/evidence \
|
||||
$M vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \
|
||||
@@ -458,5 +487,7 @@ The payload writes `0xDEAD`, `0xBEEF` and a 1024-iteration counter at bit
|
||||
`0x01810000`, and fills a 256x64 block in video RAM at row 100, column 64.
|
||||
The script reads every value back and prints `ok`/`WRONG`; a frame dump shows
|
||||
the block at screen x 256..511, y 136..199. `HELLO_AT=9999` is the control run
|
||||
(nothing loads, markers stay zero). See `HELLO_WORLD_REPORT.md` and
|
||||
`harness/hello-world/README.md`.
|
||||
(nothing loads, markers stay zero). `VT1200_MONITOR=VR150` is required here:
|
||||
the payload writes 1024-bit rows and the frame numbers are the old VR150
|
||||
timeline; the VR319 default would shear the block and boot at a different frame
|
||||
cadence. See `HELLO_WORLD_REPORT.md` and `harness/hello-world/README.md`.
|
||||
@@ -4,6 +4,12 @@
|
||||
self-test **CP437 character table is visible and legible**, and the level-1
|
||||
error screen it writes later is legible too.
|
||||
|
||||
> **Note (VR319 update).** Sections 1–7 record the first, VR150-derived
|
||||
> geometry. The real unit is a **VR319** and the harness now models that by
|
||||
> default, so the firmware writes 2048-bit rows / 1280 px and the renderer
|
||||
> follows it. Section 8 is the corrected model; the only thing sections 1–7
|
||||
> still need is `VT1200_MONITOR=VR150` if you want to reproduce them.
|
||||
|
||||
---
|
||||
|
||||
## 1. The defect
|
||||
@@ -158,28 +164,62 @@ absent, so the code just blanks the line (as it did before).
|
||||
the whole `0x04000000..0x1fffffff` mapping; the framebuffer itself is the
|
||||
first ~128 KiB.
|
||||
|
||||
## 8. The other layout: `VT1200_VRAM_REAL=1`
|
||||
## 8. The real layout: VR319 is the default
|
||||
|
||||
Everything above describes the **derived** geometry: 1024-bit (128-byte) rows,
|
||||
1024 active pixels, drawn at x 192..1215. It is what the firmware's own blitter
|
||||
uses (`pc=3FDF9A00` advances the destination by `0x40` words per glyph row), and
|
||||
what makes the Level-1 error text legible.
|
||||
Sections 1–7 describe the **VR150** geometry: 1024-bit (128-byte) rows, 1024
|
||||
active pixels, drawn at x 192..1215. That was the harness's wrong default. The
|
||||
real unit reports **`Monitor Type VR319`** (`dwt/notes/REAL_UNIT.md`), and the
|
||||
firmware chooses its display format from that switch. The old
|
||||
`VT1200_VRAM_REAL=1` knob then re-read the VR150 framebuffer at the VR319 pitch,
|
||||
which is why it sheared the OS: the firmware had written 128-byte rows, not
|
||||
256-byte ones.
|
||||
|
||||
The **real unit** stores 1280-pixel rows at a 256-byte pitch — first 160 bytes
|
||||
the picture, the remaining 96 a `0x55` mask — as `harness/c-hello/README.md`
|
||||
records from measurement ("a payload writing at `row*2048` bits lands on
|
||||
scanline `row`"), and as `dwt/screenshot.py --real` (width 1280, stride 256)
|
||||
decodes from live VRAM.
|
||||
The measurements that settle it (probe of the TMS34010 display registers, same
|
||||
run, after boot):
|
||||
|
||||
A payload can target either. The tile stream targets the real unit
|
||||
(`vram_tile.asm: DPTCH = 0x800` = 2048 bits), so under the derived renderer it
|
||||
is sheared by a quarter-row per scanline and wrapped. `VT1200_VRAM_REAL=1`
|
||||
renders the same VRAM at 2048-bit pitch and 1280 px from x=0 instead. Both modes
|
||||
read the bits LSB-first, which is the TMS34010 pixel order and the order the
|
||||
tile pipeline now packs (`vidsrc.Rows.pack_row`); an MSB-first packer is
|
||||
self-consistent with an MSB-first checker and mirrors every eight pixels on a
|
||||
| monitor (switch) | HEBLNK | HSBLNK | active px | CONVDP/CONVSP | pitch |
|
||||
|---|---|---|---|---|---|
|
||||
| VR150 (`0x70`) | `0x0C` → 192 | `0x4C` → 1216 | 1024 | `0x15` | 1024 bits / 128 B |
|
||||
| VR319 (`0x60`, real unit) | `0x15` → 336 | `0x65` → 1616 | 1280 | `0x14` | 2048 bits / 256 B |
|
||||
|
||||
`CONVDP` is the chip's own destination-pitch register: the firmware sets it to
|
||||
`0x15` in VR150 mode and `0x14` in VR319 mode, so the pitch is not a renderer
|
||||
choice at all — it is what the software tells the blitter and the scanout to
|
||||
use. `VEBLNK` is `0x24` (36) in both modes; the active rows are 1024.
|
||||
|
||||
The fix is therefore in the **machine model**, not in the renderer's pitch:
|
||||
|
||||
* The monitor switch now defaults to **VR319** (`0x60`), the real unit's mode;
|
||||
`VT1200_MONITOR=VR150|VR262|VR315` restores the old geometry for comparison.
|
||||
* The renderer's source pitch follows the monitor (2048 bits for VR319, 1024 for
|
||||
the others).
|
||||
* The destination span is always the display controller's
|
||||
`HEBLNK..HSBLNK`, and the source x runs `0..(HSBLNK-HEBLNK)`. In VR319 this is
|
||||
screen x 336..1615. Drawing from x=0 (the old `VT1200_VRAM_REAL` branch) put
|
||||
the framebuffer under the device's left blanking, so the device erased the
|
||||
leftmost 336 px and the picture looked shifted/cut.
|
||||
|
||||
`VT1200_VRAM_REAL=1` is kept as a force-the-2048-pitch knob for the pre-VR319
|
||||
workflows, but it is no longer needed for the tile stream: with the default
|
||||
VR319 the OS and `vram_tile.asm` both write 2048-bit rows, and the tile checker
|
||||
passes with no environment (`VT1200_MODE=tiles harness/mame/run-vidstream.sh`,
|
||||
`tile_check: PASS`).
|
||||
|
||||
Evidence at the new default:
|
||||
|
||||
* `VT1200_SKIP_LEVEL1=1`, frame 600: the Terminal Manager renders cleanly at
|
||||
1696×1063 with the 1280-px active area at x=336. Regenerate with
|
||||
`VT1200_DUMP_FRAMES=600 VT1200_DUMP_DIR=...`.
|
||||
* No env, frame 600: the level-0 diagnostics menu (`VERSION 2.08 - SELECTED TEST
|
||||
TYPE:`) is legible and correctly placed in the active area.
|
||||
* `VT1200_IO_PATH_STUB=1`: frames 1000 and 1900 show
|
||||
`LEVEL1/2 ERROR -> HOST PORT - 15` and `LEVEL1/2 ERROR -> PRINTER PORT - 16`
|
||||
(plus "CONTINUING THE TESTS") legibly — the same screens the report used
|
||||
before, now at 2048-bit pitch.
|
||||
* `VT1200_MODE=tiles`: the 1280×1024 tile test pattern fills the active area,
|
||||
and `tile_check.py` reports 1280/1280 tiles matching.
|
||||
|
||||
Both modes still read the bits LSB-first, which is the TMS34010 pixel order and
|
||||
the order the tile pipeline packs (`vidsrc.Rows.pack_row`); an MSB-first packer
|
||||
is self-consistent with an MSB-first checker and mirrors every eight pixels on a
|
||||
display.
|
||||
|
||||
The switch changes only the bitmap, never CPU state, so the regression baseline
|
||||
(`progress.lua`, `PC=3FDCE400 samples=198`) is unaffected. Default is off: the
|
||||
real machine runs level 1, and the derived geometry is what that text needs.
|
||||
@@ -18,9 +18,9 @@ than a silently missing check.
|
||||
This script is the independent reading of the geometry, not a copy of the code
|
||||
under test. The frame is 1280 pixels wide, 1 bpp, 160 bytes a row; the screen
|
||||
row pitch is 2048 bits = 256 bytes (the real unit's, measured in
|
||||
harness/c-hello/README.md -- MAME's own renderer uses a different pitch and is
|
||||
NOT what this test checks). A source row r stretched by S must appear, byte for
|
||||
byte, on screen rows r*S .. r*S+S-1.
|
||||
harness/c-hello/README.md -- and the harness default now that it models VR319;
|
||||
`VT1200_MONITOR=VR150` would use the old 128-byte renderer). A source row r
|
||||
stretched by S must appear, byte for byte, on screen rows r*S .. r*S+S-1.
|
||||
|
||||
The comparison uses the bytes that were actually delivered to the slot, not a
|
||||
re-implementation of the pattern: whatever the sender wrote is what must land on
|
||||
|
||||
+89
-51
@@ -24,9 +24,12 @@
|
||||
* The four undecoded board windows, driving the handshake register model
|
||||
with a two-tick busy bit (required to leave early POST).
|
||||
* MAME's SCN2681 at DUART1 (0x0640000 = host/printer), with its input
|
||||
port IP4-IP6 held high -- which is how the driver reads the VR150
|
||||
monitor switch (value 0x70). The level-1 host-port transmit is
|
||||
ISR-driven, so a real DUART is required.
|
||||
port IP4-IP6 driven from the modelled monitor-selection switch. The
|
||||
default is VR319 (0x60) -- the real unit's mode (dwt/notes/REAL_UNIT.md)
|
||||
-- which makes the firmware program the 1280x1024 display with a
|
||||
256-byte (2048-bit) row pitch. VT1200_MONITOR selects VR150 (0x70),
|
||||
VR262 (0x50) or VR315 (0x30) instead. The level-1 host-port transmit
|
||||
is ISR-driven, so a real DUART is required.
|
||||
* The 1.75 MiB firmware ROM (physical byte base 0x07e40000 = bit
|
||||
0x3f200000), mirrored into the three upper 1 GiB quadrants so that
|
||||
the reset-vector page at logical bit 0xffffffe0 resolves to the last
|
||||
@@ -121,6 +124,16 @@
|
||||
VT1200_DUMP_VRAM_FRAMES.
|
||||
VT1200_DUMP_DIR=<dir> directory for the dumps (default ".")
|
||||
VT1200_NO_RENDER=1 blank the screen instead of painting it
|
||||
VT1200_MONITOR=<name|0-7> monitor-selection switch: VR319 (default,
|
||||
0x60), VR150 (0x70), VR262 (0x50) or VR315
|
||||
(0x30). Also sets the renderer's row pitch:
|
||||
VR319 is 2048 bits / 1280 px, the others
|
||||
use the derived 1024-bit / 1024 px.
|
||||
VT1200_VRAM_REAL=1 force the 2048-bit / 1280 px renderer pitch
|
||||
regardless of VT1200_MONITOR. Only
|
||||
meaningful when the emulated firmware is
|
||||
itself writing that layout (VR319 does).
|
||||
Kept for the pre-VR319 workflows.
|
||||
|
||||
Behaviour switch (deliberately diverges from the real terminal):
|
||||
|
||||
@@ -630,7 +643,6 @@ static const u32 BOARD_BASE[4] = { VT1200_WIN0, VT1200_WIN1, VT1200_WIN2, VT1200
|
||||
// sheared and unreadable. See RENDER_REPORT.md.
|
||||
static const u32 VRAM_BASE_BITS = 0x04000000; // where the framebuffer lives
|
||||
static const int VMEM_PITCH_BITS = 1024; // 0x40 words = 128 bytes
|
||||
static const int VMEM_WIDTH = 1024; // visible pixels per row
|
||||
static const int VMEM_HEIGHT = 1024; // rows in the framebuffer
|
||||
|
||||
class vt1200_state : public driver_device
|
||||
@@ -713,6 +725,13 @@ private:
|
||||
required_device<scn2681_device> m_duart2; // 0x0620000: keyboard(A)/mouse(B)
|
||||
bool m_duart1_int2 = false; // set by VT1200_SKIP_LEVEL1
|
||||
|
||||
// Monitor-selection switch position 0-7, read from DUART1 IP4-IP6. The
|
||||
// real unit reports VR319 (dwt/notes/REAL_UNIT.md), which the firmware's
|
||||
// table tests as 0x60 (position 6); 0x70=VR150, 0x50=VR262, 0x30=VR315,
|
||||
// anything else is "Undefined monitor switch setting". VT1200_MONITOR
|
||||
// selects another position for comparison.
|
||||
int m_monitor_pos = 6; // 0x60 = VR319
|
||||
|
||||
// The real devices on DUART2. Keyboard on channel A, pointing device on
|
||||
// channel B, as the board has them. Wired bit-by-bit to the SCN2681's
|
||||
// serial lines. Channel B carries the VSXXX-AB tablet BY DEFAULT, or the
|
||||
@@ -802,13 +821,12 @@ private:
|
||||
// times -- the instrument for telling "progressing" from "stuck": if two
|
||||
// samples minutes apart are byte-identical, nothing is being drawn.
|
||||
bool m_render = true;
|
||||
// VT1200_VRAM_REAL=1: render the framebuffer at the REAL unit's geometry
|
||||
// (2048-bit/256-byte row pitch, 1280 px wide) instead of the derived
|
||||
// 1024-bit/128-byte pitch MAME's own screen geometry implies. Off by
|
||||
// default: the Level-1 error text is only legible at 128 bytes (see
|
||||
// RENDER_REPORT.md), so this is for looking at content written by a payload
|
||||
// that targets the real machine -- e.g. the tile stream, whose
|
||||
// vram_tile.asm sets DPTCH=0x800 (2048 bits). The bit order is LSB-first in
|
||||
// m_render_real: render the framebuffer at the REAL unit's geometry
|
||||
// (2048-bit/256-byte row pitch) instead of the derived 1024-bit/128-byte
|
||||
// pitch. It is set automatically from the modelled monitor switch: VR319
|
||||
// (the real unit) is 1280 px/2048 bits, VR150 and the other modes use the
|
||||
// derived pitch. VT1200_VRAM_REAL=1 forces the real geometry regardless
|
||||
// (the pre-VR319 behaviour of this knob). The bit order is LSB-first in
|
||||
// both modes, which is the TMS34010 pixel order and matches
|
||||
// dwt/screenshot.py's decode of live VRAM.
|
||||
bool m_render_real = false;
|
||||
@@ -886,6 +904,20 @@ static std::vector<long> parse_frame_list(const char *s)
|
||||
|
||||
void vt1200_state::machine_start()
|
||||
{
|
||||
// Monitor-selection switch (DUART1 IP4-IP6, value bits 4-6). The switch is
|
||||
// read only during power-up and selects the display format:
|
||||
// 7 (0x70) VR150, 5 (0x50) VR262, 3 (0x30) VR315, 6 (0x60) VR319.
|
||||
// The real unit reports VR319 (dwt/notes/REAL_UNIT.md), which is the
|
||||
// default; VT1200_MONITOR accepts a name or a raw 0-7 position.
|
||||
if (const char *m = getenv("VT1200_MONITOR"))
|
||||
{
|
||||
if (!strcmp(m, "VR150")) m_monitor_pos = 7;
|
||||
else if (!strcmp(m, "VR262")) m_monitor_pos = 5;
|
||||
else if (!strcmp(m, "VR315")) m_monitor_pos = 3;
|
||||
else if (!strcmp(m, "VR319")) m_monitor_pos = 6;
|
||||
else m_monitor_pos = int(strtol(m, nullptr, 0)) & 7;
|
||||
}
|
||||
|
||||
if (const char *list = getenv("VT1200_BOARD_MAP"))
|
||||
{
|
||||
std::string s(list), item;
|
||||
@@ -922,6 +954,10 @@ void vt1200_state::machine_start()
|
||||
// Video: render by default; optionally dump a frame as PPM.
|
||||
if (getenv("VT1200_NO_RENDER"))
|
||||
m_render = false;
|
||||
// The renderer follows the firmware's own screen format. VR319 (the real
|
||||
// unit, 0x60) is 1280 px at a 256-byte/2048-bit row pitch; the other monitor
|
||||
// modes use the derived 128-byte pitch this renderer was built against.
|
||||
m_render_real = (m_monitor_pos == 6);
|
||||
if (getenv("VT1200_VRAM_REAL"))
|
||||
m_render_real = true;
|
||||
if (const char *f = getenv("VT1200_DUMP_FRAME"))
|
||||
@@ -989,12 +1025,12 @@ void vt1200_state::machine_start()
|
||||
// With the real DUART in place, board-level signals that used to be faked
|
||||
// by the window model come from the DUART's input port (read register 0x0D:
|
||||
// IP0-IP6, bit 7 always set). The monitor-selection switch is IP bits 4-6;
|
||||
// driving them high selects the VR150 CRT (0x70), matching the value the
|
||||
// firmware looks for. IP2 stays low (the value for the blit-status test at
|
||||
// bit 0x3F204AA0).
|
||||
m_duart1->ip4_w(1);
|
||||
m_duart1->ip5_w(1);
|
||||
m_duart1->ip6_w(1);
|
||||
// the real unit reports VR319, i.e. position 6 (0x60), and the firmware
|
||||
// selects its 1280x1024 / 256-byte-stride display format from that. IP2
|
||||
// stays low (the value for the blit-status test at bit 0x3F204AA0).
|
||||
m_duart1->ip4_w(m_monitor_pos & 1);
|
||||
m_duart1->ip5_w((m_monitor_pos >> 1) & 1);
|
||||
m_duart1->ip6_w((m_monitor_pos >> 2) & 1);
|
||||
|
||||
// VT1200_SKIP_LEVEL1=1 drives DUART1 IP3 high, which makes the boot gate at
|
||||
// bit 0x3F2042D0 take the OS cold-start branch (0x3F5788C0) and skip the
|
||||
@@ -1015,7 +1051,8 @@ void vt1200_state::machine_start()
|
||||
if (getenv("VT1200_IO_PATH_STUB"))
|
||||
m_iopath_stub = true;
|
||||
|
||||
logerror("vt1200: board model=handshake busy_ticks=2 switch=duart-ip4/5/6 log=%s render=%d dump=%ld\n",
|
||||
logerror("vt1200: board model=handshake busy_ticks=2 switch=duart-ip4/5/6 (monitor pos %d = 0x%02X, render pitch %d) log=%s render=%d dump=%ld\n",
|
||||
m_monitor_pos, m_monitor_pos << 4, m_render_real ? 2048 : 1024,
|
||||
m_log ? "on" : "off", m_render ? 1 : 0, m_dump_frame);
|
||||
logerror("vt1200: duart1=on (SCN2681 @0640000 host/printer), duart2=on (SCN2681 @0620000 kbd/mouse), skip_level1=%s\n",
|
||||
getenv("VT1200_SKIP_LEVEL1") ? "on (IP3 high, IRQ->INT2)" : "off (IP3 low, IRQ->INT1)");
|
||||
@@ -1474,29 +1511,27 @@ void vt1200_state::log_lance_dma(char kind, u32 addr, u16 value, u16 mask)
|
||||
// Copy one active scanline out of the video RAM framebuffer.
|
||||
//
|
||||
// The TMS34010 screen-refresh address is reconstructed by the device into
|
||||
// `params`, but the firmware's timing registers (HEBLNK/HSBLNK/VTOTAL) are not
|
||||
// initialised to the real machine's values, so the geometry MAME derives from
|
||||
// them (a 1280x901 screen, active x 192..1215) does not describe the memory
|
||||
// layout. The measured memory layout does:
|
||||
// `params`. Two memory layouts are in use, selected by the monitor switch:
|
||||
//
|
||||
// * base bit 0x04000000 (the mapped video RAM)
|
||||
// * pitch 1024 bits (0x40 words / 128 bytes) per row — taken from the
|
||||
// firmware's own blitter, which advances its destination by
|
||||
// 0x40 words per glyph row (pc=3FDF9A00 in VRAM write logs)
|
||||
// * bit order LSB-first within each 16-bit word — the TMS34010 pixel order
|
||||
// * active x the device's heblnk..hsblnk span is 1024 pixels, exactly the
|
||||
// framebuffer width, so screen x-heblnk maps to framebuffer x
|
||||
// * VR319 (the real unit, 0x60) pitch 2048 bits (0x800 / 256 bytes),
|
||||
// 1280 active px. The firmware programs
|
||||
// HEBLNK=0x15/HSBLNK=0x65 (16-px units:
|
||||
// 336..1616 in MAME's 1696-wide raster),
|
||||
// CONVSP/CONVDP=0x14 (= 2048 bits) and
|
||||
// VEBLNK=0x24 (36 -> 1024 rows).
|
||||
// * VR150 (0x70, the pre-VR319 default) pitch 1024 bits (0x40 / 128 bytes),
|
||||
// 1024 active px (HEBLNK=12/HSBLNK=76).
|
||||
//
|
||||
// The screen line maps 1:1 to a framebuffer row, offset by the vertical
|
||||
// In both modes the destination span is the display controller's own
|
||||
// heblnk..hsblnk, and the source pitch is the only thing that differs. The
|
||||
// bit order is LSB-first within each 16-bit word (the TMS34010 pixel order),
|
||||
// and the screen line maps 1:1 to a framebuffer row, offset by the vertical
|
||||
// blanking start (params->veblnk).
|
||||
//
|
||||
// VT1200_VRAM_REAL=1 switches to the REAL unit's layout instead: pitch 2048
|
||||
// bits (0x800 / 256 bytes), 1280 px wide, drawn from x=0. It exists because a
|
||||
// payload can write for the real machine and then look sheared here -- the tile
|
||||
// stream's vram_tile.asm sets DPTCH=0x800 and dwt/screenshot.py --real reads
|
||||
// stride 256 at 1280 wide. With the switch off nothing about the geometry
|
||||
// changes: this is a view of the same VRAM at the other documented pitch, not a
|
||||
// different machine model.
|
||||
// VT1200_MONITOR chooses the monitor (and therefore the pitch); VR319 is the
|
||||
// default because that is the real unit's mode (dwt/notes/REAL_UNIT.md).
|
||||
// VT1200_VRAM_REAL=1 still forces the 2048-bit layout regardless of monitor,
|
||||
// for compatibility with the old knob.
|
||||
void vt1200_state::render_active_line(bitmap_ind16 &bitmap, int scanline,
|
||||
const tms340x0_device::display_params *params)
|
||||
{
|
||||
@@ -1512,30 +1547,33 @@ void vt1200_state::render_active_line(bitmap_ind16 &bitmap, int scanline,
|
||||
return;
|
||||
|
||||
const u16 *const vram = m_vram.target();
|
||||
// Two geometries, one per documented layout (harness/c-hello/README.md,
|
||||
// Two geometries, one per monitor mode (harness/c-hello/README.md,
|
||||
// "Geometry is not portable between the real unit and MAME"):
|
||||
//
|
||||
// derived (default) pitch 1024 bits, 1024 px, x = heblnk + fx
|
||||
// real (VT1200_VRAM_REAL=1) pitch 2048 bits (0x800, 256 bytes), 1280 px,
|
||||
// x = fx -- the real raster is 1280 wide, so there is
|
||||
// no blanking offset to add
|
||||
// VR319 (default, the real unit) pitch 2048 bits (0x800, 256 bytes),
|
||||
// 1280 active px, x = heblnk + fx
|
||||
// VR150 (VT1200_MONITOR=VR150) pitch 1024 bits, 1024 active px,
|
||||
// x = heblnk + fx
|
||||
//
|
||||
// The real numbers are the ones a payload targeting the hardware writes:
|
||||
// vram_tile.asm sets DPTCH=0x800 and dwt/screenshot.py --real reads stride
|
||||
// 256 / width 1280. Revert note: delete the m_render_real branch and the
|
||||
// environment read and this file returns to the measured 128-byte renderer
|
||||
// byte for byte; nothing else depends on it.
|
||||
// 256 / width 1280.
|
||||
//
|
||||
// The pitch is the only pitch-dependent number. The destination span
|
||||
// always comes from the display controller's own blanking registers: in
|
||||
// VR319 mode the firmware programs HEBLNK=0x15/HSBLNK=0x65 (16-px units),
|
||||
// i.e. 1280 active pixels starting at screen x=336 in MAME's 1696-wide
|
||||
// raster. Drawing the framebuffer at x=0 would shift the whole OS screen
|
||||
// left by 336 px and leave the right quarter black -- which is what made
|
||||
// the "real layout" look corrupted before.
|
||||
const u32 pitch = m_render_real ? 2048u : u32(VMEM_PITCH_BITS);
|
||||
const int width = m_render_real ? 1280 : VMEM_WIDTH;
|
||||
const int x0 = m_render_real ? 0 : (int)params->heblnk;
|
||||
const int x1 = m_render_real ? bitmap.width() : (int)params->hsblnk;
|
||||
const int x0 = (int)params->heblnk;
|
||||
const int x1 = std::min<int>((int)params->hsblnk, bitmap.width());
|
||||
const u32 row_bit = (u32)fby * pitch;
|
||||
|
||||
for (int fx = 0; fx < width; fx++)
|
||||
for (int fx = 0; x0 + fx < x1; fx++)
|
||||
{
|
||||
const int x = x0 + fx;
|
||||
if (x >= x1 || x >= bitmap.width())
|
||||
break;
|
||||
const u32 bit = row_bit + (u32)fx;
|
||||
// Address-map units are bits, so a 16-bit word is 16 units.
|
||||
bitmap.pix(scanline, x) = (vram[bit >> 4] >> (bit & 15)) & 1;
|
||||
|
||||
@@ -70,10 +70,10 @@
|
||||
* fits with one descriptor to spare.
|
||||
*
|
||||
* Screen geometry. VRAM is at bit 0x04000000 and a screen row is 0x800 bits
|
||||
* (256 bytes), the real unit's pitch (harness/c-hello/README.md). MAME's own
|
||||
* renderer decodes the same memory at 128 bytes a row; that difference is a
|
||||
* property of the harness, so the draw test compares VRAM as memory, not the
|
||||
* rendered picture.
|
||||
* (256 bytes), the real unit's pitch (harness/c-hello/README.md). The harness
|
||||
* now models the real unit's VR319 monitor by default, so MAME's renderer uses
|
||||
* the same 256-byte pitch; the draw test still compares VRAM as memory, not the
|
||||
* rendered picture, because that is what the payload actually writes.
|
||||
*
|
||||
* OS repaint canary (spec 8.9). The blit covers screen rows 0..1023 and
|
||||
* columns 0..1279 only. One 16-bit sentinel word sits in the unused right
|
||||
|
||||
Reference in new issue
Block a user