Align the container to the disc, and find the decoder-free packed player fits
Two sessions, unrecorded until now, committed together because their edits share files and cannot be split cleanly after the fact. Session 28 (FINDINGS 60): the container is DLX5 -- every record sector-aligned, 120/120 starting on a boundary where 3/120 did, +0.48% on the wire and zero clocks -- and the ring's release rounds to RECALN so no pad is stranded. Two encoder levers measured and refused: `--spans all` buys +0.19 dB for +67% of the wire, and joint span/lam selection emits byte-identical containers because `lam` never leaves its floor on any of 120 frames. Session 29 (FINDINGS 61): the packed full-frame blit is 27.3% of a 12 fps frame, a channel fills GVRAM in buffer mode off the disc with the CPU halted, and it walks the 1,024 B line stride itself through array chaining. At the 9 clk/B dual-address floor the codec is 110.4% of a frame and a decoder-free packed literal player is 55.2%, at +4.89 dB -- 2.75 dB past a ceiling the codec's scene-wide palette cannot cross. Encoder work is parked; the codec is kept and not built on. check.sh is ALL GREEN before and after, plus one new stage that gates the ORDER of the measured paint costs rather than their values. Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
+87
-8
@@ -25,17 +25,24 @@ local function P(s) print("[DMA] "..s) end
|
||||
local function T() local t=M.time; return t.seconds + t.attoseconds/1e18 end
|
||||
|
||||
local DGFLAG, DGREC, DGREC_SZ = 0x18600, 0x18610, 32
|
||||
local DGWIN, DGWERR = 0x18680, 0x18684
|
||||
local DGWIN, DGWERR, DGR20, DGR20N, DGR20C = 0x18700, 0x18704, 0x18708, 0x1870C, 0x18710
|
||||
local CHROW, CHN, CHBASE = 256, 8, 0xC10000
|
||||
local R20OF -- filled in after the mailbox addresses are known
|
||||
local GV = 0xC00000
|
||||
local DGLBA, DGBLK = 1000, 4
|
||||
local DST = {0x20000, 0x24000, 0x28000}
|
||||
local DST = {0x20000, 0x24000, 0x28000, 0xC08000, 0xC0C000, 0xC10000}
|
||||
local NAME = {"PIO (the path FINDINGS 58 measured)",
|
||||
"DMA, BUS HELD (DCR $00 burst, OCR $81 max rate)",
|
||||
"DMA, STEALING (DCR $80 cycle steal, OCR $80 limited)"}
|
||||
local SHORT = {"pio", "held", "steal"}
|
||||
"DMA, STEALING (DCR $80 cycle steal, OCR $80 limited)",
|
||||
"DMA -> GVRAM (bus held, R20 bit 11 = BUFFER MODE) [47.6.2]",
|
||||
"DMA -> GVRAM (the SAME, bit 11 CLEAR -- NEGATIVE CONTROL)",
|
||||
"DMA -> GVRAM (ARRAY CHAINED, 8 rows at the 1024 B line stride)"}
|
||||
local SHORT = {"pio", "held", "steal", "gvram", "masked", "chain"}
|
||||
local ERRNAME = {[0]="OK", "SELECTION TIMEOUT -- no target answered",
|
||||
"UNEXPECTED PHASE", "POLL TIMEOUT -- a phase never arrived",
|
||||
"NON-ZERO SCSI STATUS",
|
||||
"WINDOWED READ REFUSED -- a channel cannot drop bytes"}
|
||||
R20OF = {[3]=DGR20, [4]=DGR20N, [5]=DGR20C}
|
||||
local DISK = os.getenv("DLX_SCSI_IMG") or "dlxdisk.img"
|
||||
|
||||
local code do local f=io.open("dmagate.bin","rb"); code=f:read("a"); f:close() end
|
||||
@@ -58,7 +65,8 @@ SUB = emu.add_machine_frame_notifier(function()
|
||||
cpu.state["SP"].value = 0x8000
|
||||
cpu.state["PC"].value = 0x10000
|
||||
P(string.format("dmagate.bin=%d B loaded at $10000; reading LBA %d, %d B, "
|
||||
.."three ways", #code, DGLBA, DGBLK*512))
|
||||
.."three ways, then once more into GVRAM",
|
||||
#code, DGLBA, DGBLK*512))
|
||||
st = "wait"; return
|
||||
end
|
||||
if st == "wait" then
|
||||
@@ -68,7 +76,7 @@ SUB = emu.add_machine_frame_notifier(function()
|
||||
end
|
||||
if not want then P("no "..DISK.." to check against"); P("done"); M:exit(); return end
|
||||
local LEN = DGBLK*512
|
||||
for i = 0, 2 do
|
||||
for i = 0, 5 do
|
||||
local b = DGREC + i*DGREC_SZ
|
||||
local rc = SP:read_u32(b)
|
||||
local e = SP:read_u32(b+4)
|
||||
@@ -83,14 +91,85 @@ SUB = emu.add_machine_frame_notifier(function()
|
||||
P(string.format(" FAILED: err=%d (%s)", e, ERRNAME[e] or "?"))
|
||||
else
|
||||
local bad, first = 0, nil
|
||||
-- The GVRAM run is read back a WORD at a time and split by hand.
|
||||
-- SP:read_u8 on $C00000 goes through gvram_r, which in buffer mode
|
||||
-- returns the whole word; asking for one byte of it would hand back
|
||||
-- whichever half MAME's address space happens to hand over, and the
|
||||
-- question here is precisely WHICH HALF each disc byte landed in.
|
||||
-- Even disc byte -> high half (page 1), odd -> low half (page 0),
|
||||
-- because the 68000 is big-endian and an even address is the MS byte.
|
||||
local pg1, pg0, bad_hi, bad_lo = 0, 0, 0, 0
|
||||
for k = 1, LEN do
|
||||
if SP:read_u8(DST[i+1]+k-1) ~= string.byte(want, k) then
|
||||
bad = bad + 1; first = first or (k-1)
|
||||
local got
|
||||
if i == 5 then
|
||||
-- The chained run's destination is not linear: byte k of the
|
||||
-- transfer is byte k%256 of row k//256, and the rows are a full
|
||||
-- 1024 B line stride apart. If the channel had ignored the array
|
||||
-- and run contiguously, every byte past the first row would be
|
||||
-- in the wrong place and this comparison would say so.
|
||||
local off = (k-1) % CHROW
|
||||
local a = CHBASE + ((k-1) // CHROW) * 1024 + (off & ~1)
|
||||
local w = SP:read_u16(a)
|
||||
if (off % 2) == 0 then got = (w >> 8) & 0xff; pg1 = pg1 + 1
|
||||
else got = w & 0xff; pg0 = pg0 + 1 end
|
||||
elseif i >= 3 then
|
||||
local w = SP:read_u16(DST[i+1] + ((k-1) & ~1))
|
||||
if ((k-1) % 2) == 0 then got = (w >> 8) & 0xff; pg1 = pg1 + 1
|
||||
else got = w & 0xff; pg0 = pg0 + 1 end
|
||||
else
|
||||
got = SP:read_u8(DST[i+1]+k-1)
|
||||
end
|
||||
if got ~= string.byte(want, k) then
|
||||
bad = bad + 1; first = first or (k-1)
|
||||
if ((k-1) % 2) == 0 then bad_hi = bad_hi + 1
|
||||
else bad_lo = bad_lo + 1 end
|
||||
end
|
||||
end
|
||||
if i >= 3 then
|
||||
P(string.format(" R20 during the run = $%04X (bit 11 %s); %d bytes "
|
||||
.."read back as page 1 (high half) and %d as page 0",
|
||||
SP:read_u32(R20OF[i]),
|
||||
((SP:read_u32(R20OF[i]) & 0x0800) ~= 0)
|
||||
and "SET" or "CLEAR",
|
||||
pg1, pg0))
|
||||
end
|
||||
if bad == 0 then
|
||||
P(string.format(" BYTES OK: %d B from LBA %d match %s byte for byte "
|
||||
.."[%s]", LEN, DGLBA, DISK, SHORT[i+1]))
|
||||
if i == 3 then
|
||||
P(" A CHANNEL FILLS THE PACKED LAYOUT: every disc byte landed in "
|
||||
.."its own half of a GVRAM word, with the CPU halted -- so a "
|
||||
.."stream interleaved (right<<8)|left goes from disc to screen "
|
||||
.."with no CPU in the loop (47.6.2, first half).")
|
||||
end
|
||||
if i == 5 then
|
||||
P(string.format(" THE CHANNEL WALKED THE ARRAY ITSELF: %d rows of "
|
||||
.."%d B landed at a %d B line stride from ONE start, CPU halted "
|
||||
.."throughout. A frame is %d such entries; the CPU does not "
|
||||
.."restart the channel per row.", CHN, CHROW, 1024, 192))
|
||||
end
|
||||
if i == 4 then
|
||||
P(" CONTROL DID NOT FAIL: the masked write path delivered every "
|
||||
.."byte too, so the run above is not evidence about R20 bit 11.")
|
||||
end
|
||||
elseif i == 4 then
|
||||
-- THE CLAIM IS NOT "half the bytes differ". In masked 256-colour
|
||||
-- mode gvram_w takes `data & 0x00ff` and ignores mem_mask, so a byte
|
||||
-- written to an EVEN address is never stored and the high half keeps
|
||||
-- whatever it held; some of those stale halves match the disc by
|
||||
-- coincidence, and this record is full of pad, so a lot of them do.
|
||||
-- The mechanism's signature is WHERE the damage is, not how much:
|
||||
-- every ODD byte must survive and only EVEN ones may be lost.
|
||||
P(string.format(" BYTES LOST [masked]: %d of %d differ (first at "
|
||||
.."+%d) -- %d at EVEN offsets, %d at ODD.",
|
||||
bad, LEN, first, bad_hi, bad_lo))
|
||||
if bad_lo == 0 and bad_hi > 0 then
|
||||
P(string.format(" EXACTLY THE MECHANISM: all %d survivors of the "
|
||||
.."high half are stale GVRAM that happens to match "
|
||||
.."(this record is mostly pad); not one of the %d "
|
||||
.."ODD bytes was harmed. Bit 11 is what carried the "
|
||||
.."even ones in the run above.", LEN//2 - bad_hi, LEN//2))
|
||||
end
|
||||
else
|
||||
P(string.format(" BYTES WRONG [%s]: %d of %d differ, first at +%d",
|
||||
SHORT[i+1], bad, LEN, first))
|
||||
|
||||
Reference in New Issue
Block a user