Put the palette on the channel, and find one start paints a whole frame
ROADMAP K1, the packed player's one open structural item. A frame is a picture
AND a palette, and no run in this tree had pointed a DMA channel at the palette
registers. dmagate.s runs 7-9, gated by dma_run.sh and check.sh:
7. 512 B off the disc into $E82000, bus held -- byte-exact in 256 register
words, read back OUT OF the registers by the 68000;
8. the SAME transfer aimed at RAM -- byte-exact at $2C000, and 256 of 256
palette words still read the poison the CPU wrote, which is what attributes
run 7 to the channel's MAR rather than to the readback path;
9. ONE array-chained start across two kinds of destination -- the palette and
six picture rows at the 1,024 B line stride, 2,048 B byte-exact.
So a packed frame is one channel start: a 193-entry array, palette first, CPU
halted from the first byte to the last. The array is scene-constant, because
the packed layout spends both 256-colour pages and there is no page to flip.
What is left on the CPU per frame in the video path is the channel start and the
READ(10) -- no per-frame PAINT, which is not the same claim as no per-frame CPU.
The destination is POISONED first (62.1). Runs 4-6 wrote into RAM that was zero
and GVRAM that was stale against a record that is mostly pad; "it matches the
disc" was weaker than it read as. The host counts whether the poison actually
discriminates instead of assuming it: 511 of 512, and the gate refuses under 500.
And it opened a hardware item (62.4, ROADMAP B4). MAME maps the palette to
palette_device over memory_array, whose write16 is a plain COMBINE_DATA -- RAM
that honours mem_mask, with no handler that could refuse a byte write. Unlike
GVRAM's 256-colour arm there is nothing here to be wrong about, so the run
bounds the model and not the board. What a real palette register does with a
byte write is unmeasured. A negative costs 0.28% of a frame and nothing else.
29_packed_player.py now also prints the two rows with the per-frame palette
charged -- 55.7% of a frame on the chain, 582 KB/s -- alongside the picture-only
figures the codec comparison is quoted against.
check.sh ALL GREEN before (tmp/check_s30_start.log) and after
(tmp/check_s30_end.log).
Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
@@ -330,10 +330,14 @@ print(f""" PSNR against the 24-bit source, 18_text_plane_16col.py over the same
|
||||
one entry. The tree has already measured a reserved entry at
|
||||
0.04 dB (60.3), so this is noise against {PSNR_FRAME_256-PSNR_SHIPPED:+.2f}.
|
||||
|
||||
NOT SETTLED, and it is the obvious next probe: whether a DMA CHANNEL can write
|
||||
the palette registers at $E82000, which would make the palette a 193rd chain
|
||||
entry and cost the CPU nothing at all. Untested; 61.2 only ever pointed a
|
||||
channel at GVRAM.
|
||||
SETTLED IN SESSION 30, AND THE ANSWER IS YES (FINDINGS 62): a channel writes
|
||||
the palette registers at $E82000 byte-exact, and ONE array-chained start
|
||||
crosses from those registers into GVRAM -- so the palette IS a 193rd chain
|
||||
entry and the clocks row above is what the CPU pays only if it does the write
|
||||
itself. dmagate.s runs 7-9. What that does NOT settle is the board: MAME maps
|
||||
the palette to palette_device over memory_array, whose write16 is a plain
|
||||
COMBINE_DATA, so there is no handler that could refuse a byte write and the
|
||||
model cannot discriminate. ROADMAP B4.
|
||||
|
||||
AND THE PSNR FIGURES ARE PIL's MEDIANCUT, not this project's own palette
|
||||
builder (vq.scene_palette / H.build). The DIRECTION is measured and the
|
||||
@@ -344,6 +348,14 @@ print(f""" PSNR against the 24-bit source, 18_text_plane_16col.py over the same
|
||||
w9 = 9.0
|
||||
free_packed_dma = PACKED_BPF * w9 + CHAIN_CLK + AUD_CLK
|
||||
free_packed_cpu = PACKED_BPF * w9 + blit[8] + AUD_CLK
|
||||
# ... and the same two rows with the PER-FRAME PALETTE actually charged, which
|
||||
# is what a player ships. The picture rows above are the comparison against the
|
||||
# codec and are left alone so the published 55.2% / 81.6% do not drift; these
|
||||
# are the shipping figures. Session 30 (FINDINGS 62) made the DMAC row's
|
||||
# version legal: the palette is a 193rd chain ENTRY, so it costs 512 more
|
||||
# delivered bytes and one more entry rather than 256 CPU word writes.
|
||||
pal_dma = (PACKED_BPF + PAL_BYTES) * w9 + CHAIN_CLK + B.DMA_CHAIN_CLK + AUD_CLK
|
||||
pal_cpu = (PACKED_BPF + PAL_BYTES) * w9 + blit[8] + pal_clk + AUD_CLK
|
||||
codec_9 = codec_bpf * w9 + codec_decode + AUD_CLK
|
||||
print("\n" + "=" * 78)
|
||||
print(f"""THE ANSWER, AT THE ONE RUNG THIS MACHINE CAN BE SHOWN TO RUN (W=9)
|
||||
@@ -352,6 +364,22 @@ print(f"""THE ANSWER, AT THE ONE RUNG THIS MACHINE CAN BE SHOWN TO RUN (W=9)
|
||||
free / DMAC->GVRAM / PACKED {100*free_packed_dma/FRAME_CLK:6.1f}% -- FITS, with {100-100*free_packed_dma/FRAME_CLK:.0f}% to spare
|
||||
free / CPU-painted / PACKED {100*free_packed_cpu/FRAME_CLK:6.1f}% -- FITS, with {100-100*free_packed_cpu/FRAME_CLK:.0f}% to spare
|
||||
|
||||
WITH THE PER-FRAME PALETTE CHARGED, which is what would ship:
|
||||
|
||||
DMAC-direct, palette on the CHAIN (62) {100*pal_dma/FRAME_CLK:6.1f}% of the frame, {(PACKED_BPF+PAL_BYTES)*FPS/1024:.0f} KB/s
|
||||
CPU-painted, palette written by the CPU {100*pal_cpu/FRAME_CLK:6.1f}% of the frame, {(PACKED_BPF+PAL_BYTES)*FPS/1024:.0f} KB/s
|
||||
|
||||
The palette costs the same on the WIRE either way -- {PAL_BYTES} B a frame,
|
||||
+{100*PAL_BYTES/PACKED_BPF:.1f}% -- and the wire is where this design is expensive. The gap
|
||||
between the two rows is the PAINT, not the palette.
|
||||
|
||||
What session 30 bought is smaller than either and is worth stating exactly:
|
||||
{pal_clk:,.0f} CPU clocks of palette writing replaced by one more chain entry at
|
||||
{B.DMA_CHAIN_CLK} clocks, a net {100*(pal_clk-B.DMA_CHAIN_CLK)/FRAME_CLK:.2f}% of a frame -- plus the structural half,
|
||||
which is that the video path then contains no per-frame PAINT at all. The
|
||||
CPU still issues the READ(10) and starts the channel, and neither of those
|
||||
is priced anywhere in this tree.
|
||||
|
||||
THE DECODER-FREE PACKED PLAYER FITS THE CLOCK BUDGET THAT THE CODEC MISSES.
|
||||
That is not a small correction to 47.5, it is the reverse of the reason the
|
||||
codec exists. 44.7 said it in advance and on a different cost model: "the
|
||||
|
||||
+12
-1
@@ -550,6 +550,16 @@ echo "--- session 27: the DMAC drives the data phase, and holds the bus (FINDING
|
||||
# again, so the run asserts the contrast and not just the held value;
|
||||
# * the channel's own CSR/CER/MTC/MAR, which must say it moved every byte
|
||||
# without error;
|
||||
# * THE PALETTE REGISTERS AT $E82000 (session 30, ROADMAP K1): the same
|
||||
# transfer aimed at the palette, byte-exact into 256 register words read
|
||||
# back by the 68000; the SAME transfer aimed 20 KB away leaving the palette
|
||||
# as the CPU poisoned it, which is what attributes the first run to the
|
||||
# channel's MAR; and ONE array-chained start crossing from the registers
|
||||
# into GVRAM, which is the shape of a whole frame -- a palette entry and
|
||||
# 192 row entries, started once. What this does NOT settle is the board:
|
||||
# MAME models the palette as a generic palette_device over memory_array,
|
||||
# whose write16 is a plain COMBINE_DATA, so it cannot tell a register file
|
||||
# that takes byte writes from one that does not (FINDINGS 62.4);
|
||||
# * and a WINDOWED read through the channel REFUSED. 117 of 120 records start
|
||||
# part way into a sector (58.3); a channel writes a contiguous run and cannot
|
||||
# drop the bytes in front of one, so it would write the neighbouring records
|
||||
@@ -565,7 +575,8 @@ if command -v chdman > /dev/null; then
|
||||
bash tools/bench/dma_run.sh "$DLX" > tmp/dma_gate.log 2>&1 || {
|
||||
echo "FAIL: the DMAC did not drive the SCSI data phase."
|
||||
tail -16 tmp/dma_gate.log; exit 1; }
|
||||
grep -aE "BYTES OK|MTC one instruction|trips round|REFUSED" tmp/dma_gate.log \
|
||||
grep -aE "BYTES OK|MTC one instruction|trips round|REFUSED|PALETTE|ONE START" \
|
||||
tmp/dma_gate.log \
|
||||
| sed 's/^ *//;s/^/ /'
|
||||
else
|
||||
echo " SKIPPED: no chdman (ships with mame-tools) -- cannot build the volume"
|
||||
|
||||
+117
-15
@@ -24,25 +24,40 @@ local SP = M.devices[":maincpu"].spaces["program"]
|
||||
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 DGFLAG, DGREC, DGREC_SZ = 0x18600, 0x18800, 32
|
||||
local DGWIN, DGWERR, DGR20, DGR20N, DGR20C = 0x18700, 0x18704, 0x18708, 0x1870C, 0x18710
|
||||
local DGR20P = 0x18714
|
||||
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, 0xC08000, 0xC0C000, 0xC10000}
|
||||
-- the palette runs (ROADMAP K1). PS7/PS8/PS9 are the SNAPSHOTS dmagate.s takes
|
||||
-- by reading $E82000 back with the 68000 after each run; the registers
|
||||
-- themselves hold only the last of the three by the time the host looks.
|
||||
local PAL, PALN, PALB, POIS = 0xE82000, 256, 512, 0xA500
|
||||
local PS7, PS8, PS9 = 0x1A000, 0x1A200, 0x1A400
|
||||
local CHROW2, CH2BASE, CHN2ROWS = 256, 0xC14000, 6
|
||||
local DST = {0x20000, 0x24000, 0x28000, 0xC08000, 0xC0C000, 0xC10000,
|
||||
PAL, 0x2C000, CH2BASE}
|
||||
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)",
|
||||
"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"}
|
||||
"DMA -> GVRAM (ARRAY CHAINED, 8 rows at the 1024 B line stride)",
|
||||
"DMA -> PALETTE (bus held, 512 B into $E82000) [K1, 61.9]",
|
||||
"DMA -> RAM (the SAME read aimed elsewhere -- NEGATIVE CONTROL:"
|
||||
.." the palette must still read poison)",
|
||||
"DMA -> PALETTE + SIX ROWS (ONE array-chained start across two"
|
||||
.." kinds of destination)"}
|
||||
local SHORT = {"pio", "held", "steal", "gvram", "masked", "chain",
|
||||
"pal", "palctl", "palchain"}
|
||||
local LENOF = {[6]=PALB, [7]=PALB} -- everything else is DGBLK*512
|
||||
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}
|
||||
R20OF = {[3]=DGR20, [4]=DGR20N, [5]=DGR20C, [8]=DGR20P}
|
||||
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
|
||||
@@ -75,8 +90,8 @@ SUB = emu.add_machine_frame_notifier(function()
|
||||
return
|
||||
end
|
||||
if not want then P("no "..DISK.." to check against"); P("done"); M:exit(); return end
|
||||
local LEN = DGBLK*512
|
||||
for i = 0, 5 do
|
||||
for i = 0, 8 do
|
||||
local LEN = LENOF[i] or DGBLK*512
|
||||
local b = DGREC + i*DGREC_SZ
|
||||
local rc = SP:read_u32(b)
|
||||
local e = SP:read_u32(b+4)
|
||||
@@ -101,21 +116,43 @@ SUB = emu.add_machine_frame_notifier(function()
|
||||
local pg1, pg0, bad_hi, bad_lo = 0, 0, 0, 0
|
||||
for k = 1, LEN do
|
||||
local got
|
||||
-- `a` is set for every destination that has to be read a WORD at a
|
||||
-- time and split by hand -- GVRAM in buffer mode, and the palette
|
||||
-- snapshots, whose words are what the 68000 read back out of
|
||||
-- $E82000. Where it stays nil the destination is plain RAM.
|
||||
local off, a
|
||||
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)
|
||||
off = (k-1) % CHROW
|
||||
a = CHBASE + ((k-1) // CHROW) * 1024 + (off & ~1)
|
||||
elseif i == 6 then
|
||||
off = (k-1) % 2
|
||||
a = PS7 + ((k-1) & ~1)
|
||||
elseif i == 8 then
|
||||
-- ONE transfer across two kinds of destination: the first sector
|
||||
-- is the palette, the rest is six picture rows at the line
|
||||
-- stride. The split is the array's, and this walks it the same
|
||||
-- way the channel was told to.
|
||||
if k <= PALB then
|
||||
off = (k-1) % 2
|
||||
a = PS9 + ((k-1) & ~1)
|
||||
else
|
||||
local idx = k - PALB - 1
|
||||
off = idx % 2
|
||||
a = CH2BASE + (idx // CHROW2) * 1024 + ((idx % CHROW2) & ~1)
|
||||
end
|
||||
elseif i >= 3 and i ~= 7 then
|
||||
off = (k-1) % 2
|
||||
a = DST[i+1] + ((k-1) & ~1)
|
||||
end
|
||||
if a then
|
||||
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
|
||||
@@ -125,14 +162,35 @@ SUB = emu.add_machine_frame_notifier(function()
|
||||
else bad_lo = bad_lo + 1 end
|
||||
end
|
||||
end
|
||||
if i >= 3 then
|
||||
if R20OF[i] 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",
|
||||
.."read back out of the HIGH half of a destination "
|
||||
.."word and %d out of the LOW half",
|
||||
SP:read_u32(R20OF[i]),
|
||||
((SP:read_u32(R20OF[i]) & 0x0800) ~= 0)
|
||||
and "SET" or "CLEAR",
|
||||
pg1, pg0))
|
||||
end
|
||||
-- THE PALETTE RUNS' OWN VACUITY CHECK. Run 7's destination was
|
||||
-- poisoned by the 68000 first, so "it matches the disc" cannot be
|
||||
-- satisfied by a channel that did nothing -- but only if the poison
|
||||
-- and the disc actually differ everywhere they are compared. That is
|
||||
-- a property of THIS record and is counted rather than assumed.
|
||||
if i == 6 then
|
||||
local diff = 0
|
||||
for j = 0, PALN-1 do
|
||||
local w = (POIS | j) & 0xffff
|
||||
if ((w >> 8) & 0xff) ~= string.byte(want, 2*j+1) then diff = diff + 1 end
|
||||
if (w & 0xff) ~= string.byte(want, 2*j+2) then diff = diff + 1 end
|
||||
end
|
||||
P(string.format(" PALETTE POISON IS A DISCRIMINATOR: %d of %d "
|
||||
.."positions differ from the disc's bytes -- a "
|
||||
.."channel that wrote nothing could not have passed "
|
||||
.."in those.", diff, PALB))
|
||||
P(string.format(" %d bytes read back out of the HIGH half of a "
|
||||
.."palette word (G and the top of R) and %d out of "
|
||||
.."the LOW half", 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]))
|
||||
@@ -152,6 +210,26 @@ SUB = emu.add_machine_frame_notifier(function()
|
||||
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
|
||||
if i == 6 then
|
||||
P(" A CHANNEL WRITES THE PALETTE REGISTERS: 512 B off the disc "
|
||||
.."became 256 palette words, read back OUT OF $E82000 by the "
|
||||
.."68000 itself, with the CPU halted for the transfer. Each "
|
||||
.."disc byte landed in its own half of a register word, so a "
|
||||
.."per-frame palette needs no CPU (61.9, ROADMAP K1).")
|
||||
end
|
||||
if i == 8 then
|
||||
P(string.format(" ONE START PAINTED THE PALETTE AND %d ROWS: a "
|
||||
.."single array-chained transfer crossed from device registers "
|
||||
.."at $%06X into GVRAM at $%06X, %d B in %d entries, CPU halted "
|
||||
.."throughout. A frame is that shape with %d row entries "
|
||||
.."instead of %d.", CHN2ROWS, PAL, CH2BASE, LEN, CHN2ROWS+1,
|
||||
192, CHN2ROWS))
|
||||
end
|
||||
elseif i == 6 or i == 8 then
|
||||
P(string.format(" PALETTE WRONG [%s]: %d of %d differ, first at "
|
||||
.."+%d -- %d at EVEN offsets (the HIGH half of a "
|
||||
.."word), %d at ODD.",
|
||||
SHORT[i+1], bad, LEN, first, bad_hi, bad_lo))
|
||||
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
|
||||
@@ -175,6 +253,30 @@ SUB = emu.add_machine_frame_notifier(function()
|
||||
SHORT[i+1], bad, LEN, first))
|
||||
end
|
||||
end
|
||||
if i == 7 then
|
||||
-- THE ATTRIBUTION CONTROL'S SECOND CLAIM, and the one that makes run
|
||||
-- 7 mean something: the same transfer aimed 20 KB away leaves the
|
||||
-- palette exactly as the 68000 poisoned it. If this comes back with
|
||||
-- the disc's bytes in it, something other than the channel's MAR
|
||||
-- decides what reaches $E82000 and run 7 measured that instead.
|
||||
local stale, first_s = 0, nil
|
||||
for j = 0, PALN-1 do
|
||||
if SP:read_u16(PS8 + 2*j) ~= ((POIS | j) & 0xffff) then
|
||||
stale = stale + 1; first_s = first_s or j
|
||||
end
|
||||
end
|
||||
if stale == 0 then
|
||||
P(string.format(" PALETTE UNTOUCHED BY THE CONTROL: %d of %d words "
|
||||
.."still read the poison the 68000 wrote, so the "
|
||||
.."bytes in run 7 got there because the channel's "
|
||||
.."MAR pointed at $%06X.", PALN, PALN, PAL))
|
||||
else
|
||||
P(string.format(" CONTROL DID NOT FAIL [palctl]: %d of %d palette "
|
||||
.."words are no longer poison (first at entry %d) "
|
||||
.."-- the palette changed without a channel aimed "
|
||||
.."at it.", stale, PALN, first_s))
|
||||
end
|
||||
end
|
||||
if i > 0 then
|
||||
-- THE DISCRIMINATOR. MTC as the instruction after START saw it, and
|
||||
-- the number of times the CPU went round its own wait loop.
|
||||
|
||||
@@ -108,6 +108,43 @@ grep -aq "BYTES OK: 2048 B from LBA 1000 .*\[chain\]" tmp/dma_run.log || \
|
||||
grep -aq "THE CHANNEL WALKED THE ARRAY ITSELF" tmp/dma_run.log || \
|
||||
fail "the chained run did not report walking its own array."
|
||||
|
||||
# ---- THE PALETTE (ROADMAP K1, FINDINGS 61.9). If the registers at $E82000 take
|
||||
# a byte-wide DMA the way GVRAM does in buffer mode, a per-frame palette is a
|
||||
# 193rd array-chain entry and ONE channel start paints a whole frame; if they do
|
||||
# not, the CPU writes 256 words a frame and the architecture still stands. The
|
||||
# run is poisoned first and controlled twice -- once by aiming the same transfer
|
||||
# elsewhere, once by counting how many of the 512 positions the poison and the
|
||||
# disc actually differ in.
|
||||
grep -aq "BYTES OK: 512 B from LBA 1000 .*\[pal\]" tmp/dma_run.log || \
|
||||
fail "the channel did not write the palette registers at \$E82000 -- so a
|
||||
per-frame palette costs the CPU 256 word writes and cannot ride the
|
||||
frame's array chain (61.9). That is a RESULT, not a broken run: check the
|
||||
PALETTE WRONG line above for whether the bytes were dropped or misplaced."
|
||||
DIFF=$(sed -n 's/.*PALETTE POISON IS A DISCRIMINATOR: \([0-9]*\) of 512.*/\1/p' \
|
||||
tmp/dma_run.log)
|
||||
[ -n "$DIFF" ] && [ "$DIFF" -ge 500 ] || \
|
||||
fail "the poison and the disc's bytes agree in ${DIFF:-?} of 512 positions, so
|
||||
the palette run could have passed without a channel writing anything --
|
||||
this is run 4's could-not-fail trap in a new place. Change DGPOIS."
|
||||
grep -aq "BYTES OK: 512 B from LBA 1000 .*\[palctl\]" tmp/dma_run.log || \
|
||||
fail "the ATTRIBUTION control's read did not land in RAM, so its palette claim
|
||||
is about a transfer that did not happen."
|
||||
grep -aq "PALETTE UNTOUCHED BY THE CONTROL: 256 of 256 words" tmp/dma_run.log || \
|
||||
fail "the palette changed during a transfer aimed 20 KB away from it. Then
|
||||
what reached \$E82000 in the run above was not decided by the channel's
|
||||
MAR, and that run measured something else."
|
||||
if grep -aq "CONTROL DID NOT FAIL \[palctl\]" tmp/dma_run.log
|
||||
then
|
||||
fail "the control reported its own failure -- see the line above it."
|
||||
fi
|
||||
grep -aq "BYTES OK: 2048 B from LBA 1000 .*\[palchain\]" tmp/dma_run.log || \
|
||||
fail "ONE array-chained start could not cross from the palette registers into
|
||||
GVRAM. A frame is one palette entry and 192 row entries; if the two kinds
|
||||
of destination cannot share a chain, the CPU is back in the video path
|
||||
once a frame to start the second half of it."
|
||||
grep -aq "ONE START PAINTED THE PALETTE AND 6 ROWS" tmp/dma_run.log || \
|
||||
fail "the palette+rows run did not report the crossing it exists to show."
|
||||
|
||||
grep -aq "WINDOWED DMA READ REFUSED" tmp/dma_run.log || \
|
||||
fail "a WINDOWED read through the channel was not refused. 117 of 120 records
|
||||
start part way into a sector (58.3), and a channel cannot drop the bytes
|
||||
|
||||
Reference in New Issue
Block a user