docs(xie): the VT1200 ROM has no DCT (JPEG) scheme -- measured

The question was whether this ROM answers "unsupported" for XieK_DCT.  It does,
at both layers, and it behaves exactly like an out-of-range scheme value.

SetTransport (minor 20), raw wire request, a fresh bitonal photomap per scheme:
  PCM / G31D / G32D / G42D -> accepted
  DCT (4) and 5            -> BadIDChoice(14), minor_op 20
So the ROM does not even implement the reference server's IsBitonal_ rules
(G42D is accepted on a 256-level photomap, where XieTransport.c would BadMatch),
but it does refuse 4 and above.

Export (minor 21) with the photomap's compression byte forced to each value
(the server never initialises it -- it holds the heap fill 0x55, which is why
harness/mame/xie_fix.lua pokes file 0x1B256):
  0..3 -> pixmap 4096/4096, no X error
  4    -> BadValue, nothing drawn
  5    -> BadValue, nothing drawn

And the ROM carries no JPEG machinery: no "JPEG"/"DCT" string, and the standard
Annex K quantisation table is absent in both zigzag and natural order (no flat
8x8 either).  A DCT codec carries one of those; this ROM carries none.

"DDX_RENDER: unsupported compression scheme" exists in the ROM but did not fire
in these runs, so it belongs to a path these probes do not reach -- recorded as
an open detail rather than claimed as the explanation.

Probes: xie/xiescheme.c (libXie, one scheme per connection) and
xie/xie_schemes_raw.py (raw protocol, exact error codes).
This commit is contained in:
arti committed 2026-11-01 23:55:27 +02:00
1 parent 742501be67
commit 17dfd24340
3 files changed
+317

No files matched your search

+68
View File
@@ -111,6 +111,69 @@ JPEG baseline/lossless (X11R6.1, two values) — but the 2.3 protocol that the
terminal speaks has only the single Scheme-4 DCT code, with no baseline/lossless
split and no lossless predictor field in the wire struct.
## What THIS ROM actually does (measured, not inferred)
Two probes were built for this (`xiescheme.c` via libXie, and
`xie_schemes_raw.py` which speaks the wire protocol directly):
### 1. At SetTransport (minor 20), raw request, fresh bitonal photomap each time
```
PCM SetTransport -> ACCEPTED
G31D SetTransport -> ACCEPTED
G32D SetTransport -> ACCEPTED
G42D SetTransport -> ACCEPTED
DCT SetTransport -> REFUSED ERROR code=14 (BadIDChoice) minor_op=20
5 SetTransport -> REFUSED ERROR code=14 (BadIDChoice) minor_op=20
```
So **DCT is answered exactly like an out-of-range scheme value.** Note this is
also evidence that the ROM does *not* implement the reference server's
`IsBitonal_` rules: G42D is accepted even on a 256-level photomap, where
`XieTransport.c` would answer BadMatch.
### 2. At Export (minor 21), with the photomap's compression byte forced
The client's `XieExport` to a depth-1 pixmap, for each value of the photomap's
`compression_scheme` byte (`harness/mame/xie_fix.lua` pokes it at file
`0x1B256`; the server never initialises it, so it holds the heap fill `0x55`):
```
scheme 0 PCM export: pixmap 4096/4096 no X error
scheme 1 G31D export: pixmap 4096/4096 no X error
scheme 2 G32D export: pixmap 4096/4096 no X error
scheme 3 G42D export: pixmap 4096/4096 no X error
scheme 4 DCT export: FAILED BadValue, minor 21
scheme 5 (bogus) export: FAILED BadValue, minor 21
```
**DCT and a bogus value are treated identically; 0..3 are accepted.** So in this
ROM the valid set is `0..3` and scheme 4 has no implementation — it is refused,
not silently mis-decoded.
### 3. The ROM has no JPEG machinery
Searched `vt1200_v22.bin` (md5 `b46a3e442ccb86134d2356bd70d22fd2`):
* no `JPEG` / `DCT` string (the only compression-related strings are
`DDX_RENDER: unsupported compression scheme`, `error initializing scalution`
and `5 - Scalution Test`);
* the standard JPEG Annex K quantisation table is **absent**, in both the
zigzag order code stores and the natural row order — and no flat 8x8 table
either. A DCT codec carries one of these; this ROM carries none.
`DDX_RENDER: unsupported compression scheme` exists but did **not** fire in any
of the runs above (0 occurrences in the MAME debug log for schemes 4 and 5), so
it belongs to a path these probes do not reach — reported as an open detail
rather than claimed as the explanation.
### 4. Cross-check: PCM is the only scheme that helps in practice
The working display path uses `XieK_PCM` throughout, because the transport is
`XieK_Tile` on a bitonal image and, per the reference rules above, that admits
PCM only. On a depth-1 server there is no non-bitonal visual, so `XieK_DCT` is
unreachable by protocol rule as well as unimplemented.
## Cross-reference: the DTJ article
`docs/dec/DTJ-1991-v03-04-bitonal-image-accelerators.pdf` — "Hardware
@@ -136,3 +199,8 @@ DTJ Vol. 3 No. 4, Fall 1991). Reading it against the headers above:
So the article's "image board" is the bitonal G3/G4 decompressor, and JPEG/DCT
is the scheme XIE defines for the continuous-tone data type that the same
protocol carries — the two correspond exactly to `XieK_G42D` and `XieK_DCT`.
This is consistent with the measurement above: the ROM has the G3/G4 half
(`0..3` accepted, scalution test present) and no DCT half (scheme 4 refused,
no JPEG tables). The terminal is depth 1, so the continuous-tone data type the
DCT scheme exists for has no visual to land on.
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""Ask the server, in raw XIE 2.3 protocol, one question:
for each XieK_ compression scheme, does SetTransport accept it?
It builds a fresh bitonal photomap per scheme (a photomap that already has a
transport must match it on a second SetTransport, which would turn every later
attempt into a BadMatch for the wrong reason) and then sends a real
xieSetTransportReq (minor 20, 140 bytes) and prints whatever comes back.
Usage: xie_schemes_raw.py [host]
"""
import os
import struct
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
sys.path.insert(0, os.path.join(HERE, "evidence"))
from probe23 import Cli, describe_all # noqa: E402
NAMES = {0: "PCM", 1: "G31D", 2: "G32D", 3: "G42D", 4: "DCT", 5: "(5)"}
def make_photomap(c, slot, levels=2):
"""xieCreatePhotoReq: bitonal 64x64, 1 component, 2 levels.
A distinct resource id per call -- reusing one after the first photomap owns
it is a BadIDChoice, which would mask the answer we are after.
"""
pid = c.new_id(0x0AAAA + slot * 0x100)
w = [2, pid, 64, 64, 0, 0, levels, 0, 0, 0x00000100, 0]
out = c.send(8, 12, b"".join(struct.pack("<I", x) for x in w))
return pid, describe_all(out)
def set_transport(c, pid, scheme, mode=1):
"""xieSetTransportReq, 140 bytes: 3C header + 4 + 4 + 4 + 4 + 24 + 96."""
w = [0] * 34
w[0] = pid
w[1] = 0 # compression_parameter
w[2] = scheme | (mode << 8) | (1 << 16) # scheme|mode|comp_org
w[3] = 1 # plane_mask
w[4] = 1 # pixel_stride[0] = 1
w[10] = 8 # scanline_stride[0] = 8
return c.send(20, 35, b"".join(struct.pack("<I", x) for x in w))
def main(argv):
c = Cli()
c.send(1, 2, struct.pack("<HH", 2, 3)) # init session
levels = int(argv[1]) if len(argv) > 1 else 2
schemes = [int(argv[2])] if len(argv) > 2 else (0, 1, 2, 3, 4, 5)
mode = int(argv[3]) if len(argv) > 3 else 1
print("levels=%d mode=%d (%s)" % (levels, mode,
"GetStream" if mode == 1 else "PutStream"))
for scheme in schemes:
pid, created = make_photomap(c, scheme, levels)
if "ERROR" in created:
print("%-5s create FAILED: %s" % (NAMES[scheme], created))
continue
r = set_transport(c, pid, scheme, mode)
kind = describe_all(r)
if "ERROR" in kind:
verdict = "REFUSED"
elif "NO REPLY" in kind or "NO PACKETS" in kind:
verdict = "ACCEPTED"
else:
verdict = "?"
print("%-5s SetTransport -> %-8s %s" % (NAMES[scheme], verdict, kind))
if __name__ == "__main__":
main(sys.argv)
+173
View File
@@ -0,0 +1,173 @@
/*
* What does THIS ROM answer for each XieK_ compression scheme?
*
* Built against DEC XIE 2.3 (libXie from DEC's own sources). For every scheme
* value 0..5 it issues a real SetTransport (via XieSetStream) in GetStream mode
* on a bitonal photomap, and then on a grayscale photomap if the server will
* create one -- and reports the protocol error the server returns.
*
* Xlib's default handler exits on an error, so a catching handler is installed.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XieLib.h>
/* the client never calls the DDX dispatch table; satisfy the linker */
unsigned long (*xieDdxProc[1])() = { 0 };
static int last_error;
static int last_req_minor;
static char last_text[64];
static int catch(Display *dpy, XErrorEvent *e)
{
last_error = e->error_code;
last_req_minor = e->minor_code;
XGetErrorText(dpy, e->error_code, last_text, sizeof last_text);
return 0;
}
static const char *scheme_name(int s)
{
switch (s) {
case XieK_PCM: return "PCM ";
case XieK_G31D: return "G31D ";
case XieK_G32D: return "G32D ";
case XieK_G42D: return "G42D ";
case XieK_DCT: return "DCT ";
default: return "5 ";
}
}
/* Create a photomap and push pixel data into it so it is a real image. */
static XiePhotomap make_map(Display *dpy, Window win, int bits, int ncomp,
int cls, const char *tag)
{
XieImage img;
XiePhotomap pm;
unsigned long lvl[3];
unsigned int fill;
int w = 64, h = 64, nbytes;
lvl[0] = lvl[1] = lvl[2] = (unsigned long) bits;
last_error = 0;
img = XieCreateImage((unsigned long) w, (unsigned long) h,
XieK_PCM, (unsigned char) XieK_BandByPlane,
(unsigned char) cls, ncomp, lvl,
(unsigned char) XieK_PP0, (unsigned char) XieK_LP270);
XSync(dpy, False);
if (!img || last_error) {
printf(" [%s] XieCreateImage FAILED err=%d (%s)\n", tag, last_error,
last_error ? last_text : "no error, null handle");
return 0;
}
if (!uBase_(img, 0)) {
nbytes = (int) uArSize_(img, 0);
if (nbytes <= 0) nbytes = (w + 7) / 8 * h;
uBase_(img, 0) = (unsigned char *) calloc(1, (size_t) nbytes);
uArSize_(img, 0) = nbytes;
OwnData_(img) = 1;
}
nbytes = (int) uArSize_(img, 0);
fill = ((bits + 7) / 8) == 1 ? 0xAA : 0x00;
memset(uBase_(img, 0), fill, (size_t) nbytes);
pm = XieCreatePhoto(dpy, XieK_Photomap, (unsigned long) w, (unsigned long) h,
(unsigned char) cls, ncomp, lvl,
(unsigned char) XieK_ZeroDark, 1.0,
(unsigned char) XieK_BandByPlane,
(unsigned char) XieK_BandByPlane);
XSync(dpy, False);
if (!pm || last_error) {
printf(" [%s] XieCreatePhoto FAILED err=%d\n", tag, last_error);
return 0;
}
XiePutTile(pm, img, XieK_AllPlanes, 0, 0, 0, 0,
(unsigned long) w, (unsigned long) h);
XSync(dpy, False);
if (last_error) {
/* SetTransport only needs the photomap to exist, so carry on and let the
* transport test report whatever the server says. */
printf(" [%s] XiePutTile failed err=%d (%s) -- continuing with an empty map\n",
tag, last_error, last_text);
last_error = 0;
}
printf(" [%s] photomap ready (arSize=%d)\n", tag, nbytes);
return pm;
}
static void try_scheme(Display *dpy, XiePhoto pm, int scheme, int mode,
const char *mode_name)
{
int i;
for (i = 0; i < 8; i++) last_text[i] = 0;
last_error = 0;
last_req_minor = -1;
XieSetStream(pm, 0, (unsigned char) mode, (unsigned char) scheme,
0L, (unsigned char) XieK_BandByPlane, 1L, 8L);
XSync(dpy, False);
if (last_error)
printf(" %s in %-9s -> ERROR %d %s (minor %d)\n",
scheme_name(scheme), mode_name, last_error, last_text,
last_req_minor);
else
printf(" %s in %-9s -> ACCEPTED\n", scheme_name(scheme), mode_name);
}
int main(int argc, char **argv)
{
Display *dpy;
Window win;
XSetWindowAttributes swa;
XiePhotomap bm, gm;
const char *disp = argc > 1 ? argv[1] : "192.168.99.2:0";
dpy = XOpenDisplay(disp);
if (!dpy) { fprintf(stderr, "cannot open %s\n", disp); return 1; }
XSetErrorHandler(catch);
printf("display %s, root depth %d\n", disp,
DefaultDepth(dpy, DefaultScreen(dpy)));
swa.background_pixel = 0;
win = XCreateWindow(dpy, DefaultRootWindow(dpy), 150, 200, 64, 64, 0,
CopyFromParent, InputOutput, CopyFromParent,
CWBackPixel, &swa);
XSync(dpy, False);
{
int scheme = argc > 2 ? atoi(argv[2]) : XieK_PCM;
int bits = (argc > 3 && !strcmp(argv[3], "gray")) ? 256 : 2;
int cls = (bits == 2) ? XieK_Bitonal : XieK_GrayScale;
const char *tag = (bits == 2) ? "bitonal" : "gray";
printf("\n--- %s photomap, scheme %s ---\n", tag, scheme_name(scheme));
bm = make_map(dpy, win, bits, 1, cls, tag);
if (bm) {
try_scheme(dpy, bm, scheme, XieK_GetStream, "GetStream");
/* and, if that was accepted, actually ask for the data */
if (!last_error) {
unsigned char *buf = 0;
unsigned long got = 0;
last_error = 0;
printf(" XieSetStream accepted; calling XieGetStream...\n");
XieGetStream(bm, 0, &buf, 65536L, &got);
XSync(dpy, False);
if (last_error)
printf(" XieGetStream -> ERROR %d %s\n", last_error, last_text);
else
printf(" XieGetStream -> ok, %lu bytes, first %02X %02X %02X %02X\n",
got, buf ? buf[0] : 0, buf ? buf[1] : 0,
buf ? buf[2] : 0, buf ? buf[3] : 0);
}
}
}
XCloseDisplay(dpy);
return 0;
}