Files
vt1200/harness/mame/xie_resource.lua
T
arti d8d54bb990 feat(xie): a client-chosen XIE image is displayed on the VT1200
The XIE path now runs end to end: XieCreateImage -> XieCreatePhoto ->
XiePutTile -> XieExport(photomap -> depth-1 pixmap) -> XCopyArea(pixmap ->
window), and the client's tile appears on screen at the client's own window
position, pixel-exact.

TWO FAULTS BEHIND THE LONG DEAD END

1. Device model (harness/mame/vt1200.cpp, opt-in VT1200_ACCEL=1).  The
   accelerator's output port 0x5600200 returns the pixel stream the firmware
   pushed into the FIFO -- the firmware then stores it into the drawable itself
   (file 0x125528: MOVE *B6,*B1 ; ADDK 0x10,B1).  The FIFO read pointer was
   never reset between transfers, so consecutive exports read stale words and
   the same fill produced 759 then 516 set pixels.  Reset m_fifo_in/out on the
   data-register write; the export is now deterministic.

2. Measurement, not the device.
   - dwt/screenshot.py without --real reads 1024x1024 at a 128-byte row stride,
     but the VR319 default programs 1280x1024 at a 2048-bit (256-byte) pitch.
     That is the "shifted" picture; --real is the correct read.
   - The earlier "0 differing bytes over 128 KiB" VRAM comparison stepped peek
     by 1024 *bit* addresses, so it scanned rows 0..63 only -- never the window
     at rows 200..263.  Over the real raster the white/black diff is 576 bytes
     spanning exactly those rows.
   - New instrument: decclient does a server-side XGetImage readback of the
     pixmap and window and counts set pixels.  That separated "the server never
     drew it" from "the framebuffer view is wrong".

EVIDENCE (reproduced twice, the second time from a fresh machine)

  no export vs 0xFF : 4096 / 4096 pixels differ
  no export vs 0x00 :    0 / 4096   (the window background is black)
  0xFF      vs 0x00 : 4096 / 4096
  0xAA      vs 0x0F : 2048 / 4096   (the two stripe phases)

Tile byte 0xAA reproduces as 1-pixel vertical stripes, 0x0F as 4-pixel, with
no shear -- so the placement is right and the pixels are the client's.

Contents: xie/ holds DEC XIE 2.3 headers, 84 man pages and complete
client+server source; a libXie.a and decclient built from DEC's own code; the
XIE probes; and evidence/ with every measurement, including explicit
retractions of the earlier wrong readings (VRAM_IDENTICAL_FOR_BOTH_FILLS and
friends).  xie/evidence/XIE_IMAGE_DISPLAYED.md is the report; frames are in
xie/evidence/png/.

The accelerator model stays opt-in: it answers the status poll and relays the
pixel stream, but performs no compositing, so it is a stand-in and the verified
default configuration is unchanged (a no-env run still parks in the level-0
diagnostics menu at PC=3FDCE400).
2026-11-01 23:22:25 +02:00

178 lines
9.0 KiB
Lua

-- xie_resource.lua -- trace XIE requests and dump the created resource.
--
-- Breakpoint 1: inside XieProc after A9 = client->requestBuffer.
-- Breakpoint 2: 0x6CAC8, just after the create helper has A9 = new resource.
--
-- Run with -debuglog (printf output is discarded otherwise):
-- VT1200_SKIP_LEVEL1=1 XIE_FRAMES=2000000 \
-- /home/arti/code/k-space/mame0289/vt1200 vt1200 ... -debug -debugger none \
-- -debuglog -autoboot_script harness/mame/xie_resource.lua
local function hex(v, w) return string.format("%0" .. (w or 8) .. "X", v) end
local machine = manager.machine
local cpu = machine.devices[':maincpu']
local dbg = machine.debugger
if not dbg then print("XIE ERROR: no debugger"); machine:exit(); return end
local FRAMES = tonumber(os.getenv("XIE_FRAMES") or "2000000")
local WPFILE = "/tmp/opencode/xie_wp.txt"
local lastwp = ""
local last_elem = 0
local WORDS = tonumber(os.getenv("XIE_WORDS") or "16")
local TRACE = 0x3F525D40 -- XieProc interior, a9 = request buffer
local CREATE = 0x3F565640 -- 0x6CAC8: a9 = new resource pointer
local args = {}
for i = 0, WORDS - 1 do args[#args + 1] = string.format("w@(a9+%d)", i * 16) end
local fmt = "XIEHIT buf=%08X"
for i = 0, WORDS - 1 do fmt = fmt .. " %04X" end
fmt = fmt .. "\\n"
dbg:command("bpset " .. hex(TRACE) .. ",,{ printf \"" .. fmt .. "\", a9, " ..
table.concat(args, ", ") .. " ; g }")
dbg:command("bpset " .. hex(CREATE) ..
",,{ printf \"RES a9=%08X id=%08X class=%02X b8=%02X b36=%02X b40=%08X b44=%08X\\n\", " ..
"a9, d@(a9), b@(a9+32), b@(a9+64), b@(a9+288), d@(a9+320), d@(a9+352) ; g }")
local LOOK = 0x3F55D1C0 -- 0x6BA38: a7 = resource
local bytes = {}
for k = 0, 15 do bytes[#bytes+1] = string.format("b@(a7+%d)", k*32) end
local bfmt = string.rep(" %02X", 16)
dbg:command("bpset " .. hex(LOOK) ..
",,{ printf \"LOOK a7=%08X id=%08X cls=%02X b36=%02X b40=%08X" .. bfmt .. "\\n\", " ..
"a7, d@(a7), b@(a7+32), b@(a7+288), d@(a7+320), " .. table.concat(bytes, ", ") .. " ; g }")
-- Debug-only nudge: when a class-2 resource reaches the lookup, make it class 3
-- so PUT_TILE's hard-coded class check passes. Proves the rest of the pixel path.
-- Debug-only: skip the class-compare failure branch (0x6BAF6 -> 0x6BBCE).
local BAND = 0x3F555C40 -- 0x6AB88: a11 = band record, a9 = slot20 request
dbg:command("bpset " .. hex(BAND) ..
",,{ printf \"BAND a11=%08X f1=%02X f2=%02X f4=%08X f8=%08X f12=%08X f16=%08X\\n\", " ..
"a11, b@(a11+8), b@(a11+16), d@(a11+32), d@(a11+64), d@(a11+96), d@(a11+128) ; g }")
dbg:command("bpset 0x3F400430,,{ printf \"DDCALL a11=%08X type=%02X idx=%08X\\n\", a11, b@(a13+0xffa0), d@(a13+0x20) ; g }")
dbg:command("bpset 0x3F400040,,{ printf \"DE entry a0=%08X a1=%08X type=%02X idx=%08X\\n\", d@(a13+0xffe0), d@(a13+0xffc0), b@(a13+0xffa0), d@(a13+0x20) ; g }")
dbg:command("bpset 0x3F4000F0,,{ printf \"DE chk1 ret=%08X\\n\", a8 ; g }")
dbg:command("bpset 0x3F400210,,{ printf \"DE chk2 ret=%08X\\n\", a8 ; g }")
dbg:command("bpset 0x3F55B440,,{ printf \"HANDOFF a9=%08X\\n\", a9 ; g }")
dbg:command("bpset 0x3F55B750,,{ printf \"DEARG a9=%08X a11=%08X\\n\", a9, a11 ; g }")
dbg:command("bpset 0x3F553B70,,{ printf \"S19ENTRY\\n\" ; g }")
dbg:command("bpset 0x3F55B440,,{ printf \"H688 a9=%08X\\n\", a9 ; g }")
dbg:command("bpset 0x3F400040,,{ printf \"DEENTRY\\n\" ; g }")
dbg:command("bpset 0x3F553DA0,,{ printf \"S19LOOK a8=%08X\\n\", a8 ; g }")
dbg:command("bpset 0x3F554D10,,{ printf \"S19HANDOFF\\n\" ; g }")
dbg:command("bpset 0x3F52DE80,,{ printf \"S43OBJ a8=%08X\\n\", a8 ; g }")
dbg:command("bpset 0x3F52E230,,{ printf \"S43ID a5=%08X req8=%08X\\n\", a5, d@(a9+0x40) ; g }")
dbg:command("bpset 0x3F566070,,{ printf \"RESPTR a7=%08X a9=%08X\\n\", a7, a9 ; g }")
dbg:command("bpset 0x3F2D8A90,,{ printf \"EXPORTENTRY\\n\" ; g }")
dbg:command("bpset 0x3F2D8BC0,,{ printf \"EXPORTFLAG f=%02X\\n\", b@(0xA6220) ; g }")
dbg:command("bpset 0x3F2DC0D0,,{ printf \"EXPORTSKIPPED\\n\" ; g }")
dbg:command("bpset 0x3F2DF610,,{ printf \"ELTYPE i=%d t=%02X\n\", d@(a13+0x180), b@(a9+0x58) ; g }")
dbg:command("bpset 0x3F2DF530,,{ printf \"BADFUNC i=%d\n\", d@(a13+0x180) ; g }")
dbg:command("bpset 0x3F2DF400,,{ printf \"PIPE_T1\n\" ; g }")
dbg:command("bpset 0x3F2DEA80,,{ printf \"PIPE_T2\n\" ; g }")
dbg:command("bpset 0x3F2DE990,,{ printf \"PIPE_T3\n\" ; g }")
dbg:command("bpset 0x3F2DE3A0,,{ printf \"PIPE_T4\n\" ; g }")
dbg:command("bpset 0x3F2DF850,,{ printf \"PIPE_LOOPEND i=%d\n\", d@(a13+0x180) ; g }")
dbg:command("bpset 0x3F2D91A0,,{ printf \"EX_BAND a5=%08X elem@d8=%08X\n\", a5, d@(a5+0xd8) ; g }")
dbg:command("bpset 0x3F2D91C0,,{ printf \"EX_ELEM a5=%08X f28=%08X\n\", a5, d@(a5+0x28) ; g }")
dbg:command("bpset 0x3F2DD410,,{ printf \"EX_TIMING\n\" ; g }")
dbg:command("bpset 0x3F55D950,,{ printf \"PUTCHK n=%d obj=%08X b8=%02X b12=%02X\n\", d@(a13+0xff20), a11, b@(a11+0x40), b@(a11+0x60) ; g }")
dbg:command("bpset 0x3F55DA50,,{ printf \"PUT_BADMATCH_byte12 n=%d\n\", d@(a13+0xff20) ; g }")
dbg:command("bpset 0x3F55DF10,,{ printf \"PUT_OK_byte8 n=%d\n\", d@(a13+0xff20) ; g }")
dbg:command("bpset 0x3F2D91A0,,{ printf \"BANDFIELDS 48=%08X 80=%08X a0=%08X 160=%08X d8=%08X\n\", d@(a5+0x48), d@(a5+0x80), d@(a5+0xa0), d@(a5+0x160), d@(a5+0xd8) ; g }")
dbg:command("bpset 0x3F2D94B0,,{ printf \"RP 5a8=%08X 5d0=%08X 5f0=%08X 610=%08X 6f8=%08X 718=%08X 738=%08X 758=%08X\n\", d@(a11+0x5a8), d@(a11+0x5d0), d@(a11+0x5f0), d@(a11+0x610), d@(a11+0x6f8), d@(a11+0x718), d@(a11+0x738), d@(a11+0x758) ; g }")
dbg:command("bpset 0x3F54BFF0,,{ printf \"TRAMP minor=%02X target=%08X req=%08X\n\", b@(a10+0x8), d@(a11), a10 ; g }")
dbg:command("bpset 0x3F54C000,,{ printf \"TRAMPCALL target=%08X\n\", a11 ; g }")
dbg:command("bpset 0x3F53D820,,{ printf \"REGIDSTORE id=%08X obj=%08X\n\", d@(a9+0x40), a11 ; g }")
dbg:command("bpset 0x3F53D9A0,,{ printf \"REGISTER id=%08X\n\", d@(a9+0x40) ; g }")
-- empirical argument/return map of the slot-19 lookup helper (file 0x66FA0)
dbg:command("bpset 0x3F537DA0,,{ printf \"LKARGS a0=%08X a1=%08X a2=%08X a3=%08X a4=%08X\n\", d@(a13+0xffe0), d@(a13+0xffc0), d@(a13+0xffa0), d@(a13+0xff80), d@(a13+0xff60) ; g }")
dbg:command("bpset 0x3F537E70,,{ printf \"LKCALLED a8=%08X\n\", a8 ; g }")
dbg:command("bpset 0x3F537E90,,{ printf \"LKDISPATCH a8=%08X\n\", a8 ; g }")
dbg:command("bpset 0x3F55D630,,{ printf \"ERR_0A_badaccess\n\" ; g }")
dbg:command("bpset 0x3F55DA60,,{ printf \"ERR_08_badmatch\n\" ; g }")
dbg:command("bpset 0x3F55CD70,,{ printf \"ERR_0E_lookup0\n\" ; g }")
dbg:command("bpset 0x3F55D2C0,,{ printf \"ERR_0E_byte40_nonzero\n\" ; g }")
dbg:command("bpset 0x3F55D400,,{ printf \"ERR_0E_other\n\" ; g }")
dbg:command("bpset 0x3F55D530,,{ printf \"ERR_0E_other2\n\" ; g }")
dbg:command("bpset 0x3F2FD2F0,,{ printf \"EB -10=%08X -0c=%08X -8=%08X -4=%08X sp=%08X +4=%08X +8=%08X +c=%08X\n\", d@(sp-0x10), d@(sp-0xc), d@(sp-8), d@(sp-4), d@(sp), d@(sp+4), d@(sp+8), d@(sp+0xc) ; g }")
dbg:command("bpset 0x3F55D7A0,,{ printf \"GATE a7=%08X a11=%08X obj=%08X objb0=%02X\n\", a7, a11, d@(a13+0xffa0), b@(d@(a13+0xffa0)) ; g }")
dbg:command("bpset 0x3F55D460,,{ printf \"OBJDISP a11=%08X b0=%02X b4=%02X b8=%02X b12=%02X b16=%02X\n\", a11, b@(a11), b@(a11+0x20), b@(a11+0x40), b@(a11+0x60), b@(a11+0x80) ; g }")
dbg:command("bpset 0x3F55D7B0,,{ printf \"GATEPASS\n\" ; g }")
dbg:command("bpset 0x3F55DA50,,{ printf \"E8_select12\n\" ; g }")
dbg:command("bpset 0x3F55DC30,,{ printf \"E8_maskcheck\n\" ; g }")
dbg:command("bpset 0x3F55D7A0,,{ printf \"GATE2 a7=%08X a11=%08X\n\", a7, a11 ; g }")
dbg:command("bpset 0x3F55D7C0,,{ printf \"E8_MAIN_0BAF8\n\" ; g }")
dbg:command("bpset 0x3F55CE20,,{ printf \"CLASS1_PATH\n\" ; g }")
dbg:command("bpset 0x3F55D440,,{ printf \"SELECTOR_DISPATCH\n\" ; g }")
local frames = 0
emu.register_frame_done(function()
frames = frames + 1
if frames % 1000 == 0 then
print(string.format("XIE: frame %d pc=%s", frames, hex(cpu.state['PC'].value)))
end
local f = io.open(WPFILE, "r")
if f then
local txt = f:read("*l"); f:close()
if txt and txt ~= lastwp then
lastwp = txt
local r = tonumber(txt)
if r then
local sp = cpu.spaces['program']
local band = sp:read_u32(r + 36*8)
local p44 = sp:read_u32(r + 44*8)
local b30 = sp:read_u32(band + 48*8)
local elem = 0
if p44 ~= 0 then elem = sp:read_u32(p44 + 0xd8) end
if elem ~= 0 and elem ~= last_elem then
last_elem = elem
dbg:command(string.format("wpset %08X,32,w,1,{ printf \"WPELEM addr=%08X data=%08X\\n\", wpaddr, wpdata ; g }", elem + 0x28))
print(string.format("XIE: elem=%08X wpset elem+0x28", elem))
end
print(string.format("XIE: watch res=%08X band=%08X p44=%08X b30=%08X", r, band, p44, b30))
local function wp(a, label)
if a and a ~= 0 then
dbg:command(string.format("wpset %08X,64,w,1,{ printf \"WPHIT %s addr=%%08X data=%%08X\\n\", wpaddr, wpdata ; g }", a, label))
end
end
wp(r, "res"); wp(band, "band"); wp(p44, "p44"); wp(b30, "b30")
end
end
end
if frames >= FRAMES then machine:exit() end
end, "vt1200-xie-resource")
dbg.visible_cpu = cpu
dbg.execution_state = 'run'