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:
@@ -104,6 +104,9 @@ ITER = $18008 ; iteration count, written by Lua
|
||||
SPTR = $1800C ; V5 span stream pointer, written by Lua
|
||||
SRCW = $60000 ; word-expanded frame 192*512 = 96KB
|
||||
SRCB = $80000 ; byte-per-pixel frame 192*256 = 48KB
|
||||
SRCP = $90000 ; PACKED frame 192*256 = 48KB (V8): two picture
|
||||
; bytes per word, already interleaved by the
|
||||
; encoder, so the blit is a straight copy
|
||||
DST0 = $C08000 ; GVRAM + 32*1024 (first picture row)
|
||||
DSTE = $C38000 ; GVRAM + 224*1024 (one past last)
|
||||
ROWS = 192 ; picture rows a V5 stream describes
|
||||
@@ -130,6 +133,12 @@ start:
|
||||
beq v6
|
||||
cmp.l #7,d0
|
||||
beq v7
|
||||
cmp.l #8,d0
|
||||
beq v8
|
||||
cmp.l #9,d0
|
||||
beq v9
|
||||
cmp.l #10,d0
|
||||
beq v10
|
||||
bra v3
|
||||
|
||||
; ---------------------------------------------------------------- V1
|
||||
@@ -372,5 +381,142 @@ v7fh:
|
||||
bne v7
|
||||
bra done
|
||||
|
||||
; ---------------------------------------------------------------- V8
|
||||
; THE PACKED FULL-FRAME BLIT (FINDINGS 46.6/47.2). Identical in shape to V1 --
|
||||
; a row-linear movem.l chain out of a RAM frame into GVRAM -- and different in
|
||||
; exactly one thing: a row is 128 WORDS, not 256, because R20 bit 11 lets one
|
||||
; word carry two picture bytes. 256 = 5*48 + 16, so five 12-register bursts
|
||||
; and a 4-register tail, against V1's ten and one.
|
||||
;
|
||||
; TIMING ONLY, and it does not set bit 11. MAME's gvram_w carries no timing in
|
||||
; either arm (blit.lua's header), so the bit cannot move a cycle here; what it
|
||||
; moves is the PICTURE, and the picture is what tools/bench/show_frame256_packed.lua
|
||||
; and tools/bench/gvpack already verify pixel-exactly. Setting it here would
|
||||
; make this variant's snapshot right and its measurement no different, and
|
||||
; would put a display-mode change inside a timing loop for no gain.
|
||||
;
|
||||
; The source is PRE-INTERLEAVED by the host, which is the honest half of the
|
||||
; claim: the packing is an encoder-side transform (46.3's argument for the text
|
||||
; plane, and the same one here), so the decoder-free player's per-frame work is
|
||||
; this copy and nothing else. If the interleave had to happen at run time this
|
||||
; variant would be V2, not V1.
|
||||
v8: lea SRCP,a0
|
||||
lea DST0,a1
|
||||
lea DSTE,a6
|
||||
v8row: movem.l (a0)+,d0-d7/a2-a5
|
||||
movem.l d0-d7/a2-a5,(a1)
|
||||
movem.l (a0)+,d0-d7/a2-a5
|
||||
movem.l d0-d7/a2-a5,48(a1)
|
||||
movem.l (a0)+,d0-d7/a2-a5
|
||||
movem.l d0-d7/a2-a5,96(a1)
|
||||
movem.l (a0)+,d0-d7/a2-a5
|
||||
movem.l d0-d7/a2-a5,144(a1)
|
||||
movem.l (a0)+,d0-d7/a2-a5
|
||||
movem.l d0-d7/a2-a5,192(a1)
|
||||
movem.l (a0)+,d0-d3
|
||||
movem.l d0-d3,240(a1)
|
||||
lea 1024(a1),a1
|
||||
cmpa.l a6,a1
|
||||
bne v8row
|
||||
subq.l #1,ITER.l
|
||||
bne v8
|
||||
bra done
|
||||
|
||||
; ---------------------------------------------------------------- V9
|
||||
; WHAT THE PACKED LAYOUT COSTS A BLOCK DECODER (FINDINGS 47.6.4, open).
|
||||
;
|
||||
; V4 is the access pattern of a decoder that writes 4x4 codewords straight into
|
||||
; GVRAM: 4 rows of 8 contiguous bytes at a 1024-byte stride, so each row is one
|
||||
; `movem.l` of two registers. Under the packed layout that pattern is GONE.
|
||||
; A block at columns x..x+3 owns the LOW bytes of four consecutive words -- four
|
||||
; bytes at STRIDE 2 -- and the high bytes of those same words belong to the
|
||||
; block 128 columns away. There is no burst that writes every other byte, so
|
||||
; the block is sixteen `move.b`s.
|
||||
;
|
||||
; V9 does the pair together, low block then high block off one base, so it
|
||||
; writes every byte it touches and covers the same 49,152 pixels V1/V4/V8 do.
|
||||
; It is the HONEST version of "keep the codec and pack the screen": the mode
|
||||
; map is unchanged, SKIP still works per block, and the writes go byte at a
|
||||
; time. V10 below is the other option, and the comparison is the point.
|
||||
v9: lea SRCB,a0
|
||||
lea DST0,a3
|
||||
lea DSTE,a4
|
||||
v9brow: move.l a3,a1
|
||||
lea 256(a3),a5 ; 32 block PAIRS * 8 bytes
|
||||
v9blk:
|
||||
move.b (a0)+,(a1)
|
||||
move.b (a0)+,2(a1)
|
||||
move.b (a0)+,4(a1)
|
||||
move.b (a0)+,6(a1)
|
||||
move.b (a0)+,1024(a1)
|
||||
move.b (a0)+,1026(a1)
|
||||
move.b (a0)+,1028(a1)
|
||||
move.b (a0)+,1030(a1)
|
||||
move.b (a0)+,2048(a1)
|
||||
move.b (a0)+,2050(a1)
|
||||
move.b (a0)+,2052(a1)
|
||||
move.b (a0)+,2054(a1)
|
||||
move.b (a0)+,3072(a1)
|
||||
move.b (a0)+,3074(a1)
|
||||
move.b (a0)+,3076(a1)
|
||||
move.b (a0)+,3078(a1)
|
||||
move.b (a0)+,1(a1)
|
||||
move.b (a0)+,3(a1)
|
||||
move.b (a0)+,5(a1)
|
||||
move.b (a0)+,7(a1)
|
||||
move.b (a0)+,1025(a1)
|
||||
move.b (a0)+,1027(a1)
|
||||
move.b (a0)+,1029(a1)
|
||||
move.b (a0)+,1031(a1)
|
||||
move.b (a0)+,2049(a1)
|
||||
move.b (a0)+,2051(a1)
|
||||
move.b (a0)+,2053(a1)
|
||||
move.b (a0)+,2055(a1)
|
||||
move.b (a0)+,3073(a1)
|
||||
move.b (a0)+,3075(a1)
|
||||
move.b (a0)+,3077(a1)
|
||||
move.b (a0)+,3079(a1)
|
||||
addq.l #8,a1
|
||||
cmpa.l a5,a1
|
||||
bne v9blk
|
||||
lea 4096(a3),a3
|
||||
cmpa.l a4,a3
|
||||
bne v9brow
|
||||
subq.l #1,ITER.l
|
||||
bne v9
|
||||
bra done
|
||||
|
||||
; ---------------------------------------------------------------- V10
|
||||
; THE OTHER OPTION: PAIR THE BLOCKS IN THE ENCODER. If the codec codes the
|
||||
; block at x and the block at x+128 as ONE unit, the destination is whole words
|
||||
; again and V4's `movem.l` shape comes straight back -- the same instructions,
|
||||
; the same 32 bytes of source per unit, and TWICE the pixels, because a word now
|
||||
; carries two of them. So V10 is V4's inner loop run half as many times.
|
||||
;
|
||||
; WHAT IT COSTS IS NOT IN THIS MEASUREMENT. A pair skips only if BOTH of its
|
||||
; blocks skip, and the two are 128 columns apart with nothing in the picture
|
||||
; relating them. That is a CONTAINER question -- what fraction of the mode map
|
||||
; survives pairing -- and 08_mode_map.py has the data to answer it. V10 prices
|
||||
; the paint; it does not price the SKIPs the pairing loses.
|
||||
v10: lea SRCP,a0
|
||||
lea DST0,a3
|
||||
lea DSTE,a4
|
||||
v10brow: move.l a3,a1
|
||||
lea 256(a3),a5 ; 32 block PAIRS * 8 bytes
|
||||
v10blk: movem.l (a0)+,d0-d7 ; 32 bytes = one PAIR of 4x4 blocks
|
||||
movem.l d0-d1,(a1)
|
||||
movem.l d2-d3,1024(a1)
|
||||
movem.l d4-d5,2048(a1)
|
||||
movem.l d6-d7,3072(a1)
|
||||
addq.l #8,a1
|
||||
cmpa.l a5,a1
|
||||
bne.s v10blk
|
||||
lea 4096(a3),a3
|
||||
cmpa.l a4,a3
|
||||
bne v10brow
|
||||
subq.l #1,ITER.l
|
||||
bne v10
|
||||
bra done
|
||||
|
||||
done: move.l #$FF,FLAG.l ; timer stops here
|
||||
halt: bra.s halt
|
||||
|
||||
Reference in New Issue
Block a user