dwt/screenshot: --stretch for the real unit's 1024->1280 display; record geometry and the background stipple

This commit is contained in:
SDD committed 2026-10-18 16:11:24 +03:00
1 parent 4a0e1235c7
commit f696a2e8f6
4 files changed
+44 -1

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

+17
View File
@@ -45,3 +45,20 @@ 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 1280x1024` matches the screen. `--screen`'s
emulator 1280×901 geometry pads/crops and looks line-doubled.
* The `101010` background is **real data**, not a decode error. It is a 50%
stipple: rows alternate `55` (`01010101`) and `ff`. The root background has a
setting; setting it to **100% white** changed the root to solid `ff` (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 - 24` is present in VRAM; it is a
real terminal error, not a capture artifact.
+27 -1
View File
@@ -57,6 +57,11 @@ OFFSET_X = 192
OFFSET_Y = 36
def parse_size(text: str) -> tuple[int, int]:
w, h = text.lower().split("x", 1)
return int(w, 0), int(h, 0)
def parse_rows(text: str) -> tuple[int, int]:
if ":" in text:
a, b = text.split(":", 1)
@@ -82,6 +87,19 @@ def decode(data: bytes, width: int = WIDTH) -> bytearray:
return out
def scale_nearest(gray: bytearray, sw: int, sh: int,
dw: int, dh: int) -> bytearray:
"""Nearest-neighbour scale; used for the real unit's 1024->1280 display."""
out = bytearray(dw * dh)
for y in range(dh):
sy = y * sh // dh
src = sy * sw
dst = y * dw
for x in range(dw):
out[dst + x] = gray[src + x * sw // dw]
return out
def compose_screen(gray: bytearray, rows: int, sw: int, sh: int,
ox: int, oy: int) -> bytearray:
canvas = bytearray(sw * sh)
@@ -147,7 +165,12 @@ def one(rd: exploit.RDebug, a: argparse.Namespace, tag: str) -> None:
if a.repeat > 1:
root, dot, ext = out.rpartition(".")
out = f"{root}-{tag}.{ext}" if dot else f"{out}-{tag}"
if a.screen:
if a.stretch:
dw, dh = a.stretch
scaled = scale_nearest(gray, WIDTH, rows, dw, dh)
write_png(out, dw, dh, scaled)
print(f"[+] {out}: {dw}x{dh} (nearest-neighbour from {WIDTH}x{rows})")
elif a.screen:
canvas = compose_screen(gray, rows, a.screen_width, a.screen_height,
a.offset_x, a.offset_y)
write_png(out, a.screen_width, a.screen_height, canvas)
@@ -179,6 +202,9 @@ def main(argv: list[str]) -> int:
metavar="A:B", help=f"row range to read (default 0:{HEIGHT})")
ap.add_argument("--screen", action="store_true",
help="compose the visible screen instead of the raw canvas")
ap.add_argument("--stretch", type=parse_size, metavar="WxH", default=None,
help="nearest-neighbour scale the raw canvas to WxH "
"(the real unit displays 1024x1024 as 1280x1024)")
ap.add_argument("--screen-width", type=int, default=SCREEN_W)
ap.add_argument("--screen-height", type=int, default=SCREEN_H)
ap.add_argument("--offset-x", type=int, default=OFFSET_X)