Put the data phase on the DMAC, and find auto-request is charged by time
ROADMAP P4a. src/player/dma.i programs HD63450 channel 1 and takes the SCSI DATA IN phase off the CPU; src/player/dmagate.s reads the same 2,048 B at LBA 1000 three ways -- PIO, the channel with the bus held, the channel stealing cycles -- and all three are byte-exact against the host's copy of the volume. The evidence never reads $EA0015, because 57.3 established that it cannot: with the DMAC's OWN asserted MAME cannot tell a CPU-driven byte there from a DMAC-driven one. The discriminator is the CPU's own progress. MTC is sampled by the INSTRUCTION AFTER the one that starts the channel, and held it reads 0 of 2,048 -- the whole transfer happened between two instructions, because the 68000 did not execute in between -- against the full count and 426 loop trips for the stealing configuration. Put the stealing registers in the held slot and every byte still arrives and tools/bench/dma_run.sh goes RED, which is what says the counter can come out different; 58.3's vacuous "UNDERRUNS: 0/120" is the trap being avoided. tools/analysis/27_dmac_config.py decodes the four register bytes out of the player's own source, with the MC68450 field tables now in one copy (tools/analysis/mc68450.py) shared with 21_iplrom_dmac.py, so the player's configuration and the IPL ROM's 16..19 clk/B one are the same decoding. Three bounds on the apparatus, read out of MAME 0.277 rather than inferred: the CZ-6BS1 has NO request line to the DMAC (its flow control is DTACK), so external request cannot be run; single address cannot be run either, because only channel 0 has device callbacks; and only burst is modelled as held. Of the four rows of the W ladder exactly one -- dual address held -- has a code path here, and it is the one demonstrated. W did not move by one clock, for the third session running. What outlives the emulator is the currency. Every W in this project is clocks per DELIVERED byte, which presumes the device asks; an auto-requested channel spends its share of the bus whether or not a byte is there, so a record costs what it costs to ARRIVE -- halve the delivery rate and the CPU cost of the same record doubles. tools/analysis/28_autorequest_cost.py prices it from MC68450 3.8 and 5.2.3.3.2, gating its formulas against Table 5-3's sixteen rows first. At 37,405 B and an explicit 460 KB/s: max rate costs the whole 95.3% of a frame the record takes to land, and of the GCR's four bus shares only BR=00, 50%, carries the rate -- 10.61 clk/B, 47.6% of a frame, against 40.4% for the W=9 row and 391.8% measured for PIO. The GCR is a design lever nothing in this tree had named. 59.4 changes what is left. sc_in_data now REFUSES a windowed read when the data phase is the channel's (SCE_WINDOW), because a channel writes a contiguous run and cannot drop the 300 B in front of a record. 117 of 120 records need one, so sector-aligned records have gone from a preference in ROADMAP's re-encode bundle to the precondition the transport enforces -- and that bundle is now the only thing between this tree and M2. One collision, recorded because the procedure is the finding: DM_USE first sat at $18300, which is ring.i's XF_SLOT mailbox, and the P4b stage -- untouched by this work -- went red on a run that never reached its snapshot. check.sh was ALL GREEN before any of this, which is what made that red unambiguous. ALL GREEN after too, with one new stage. decode.bin is unchanged at 1,296 B and the same MD5. Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
"""What the PLAYER programs into the DMAC -- read out of the assembler source.
|
||||
|
||||
python3 tools/analysis/27_dmac_config.py [src/player/dma.i]
|
||||
|
||||
ROADMAP P4a asks for a DMAC configuration that HOLDS THE BUS, and the whole
|
||||
weight of the claim is in four register bytes. tools/analysis/21_iplrom_dmac.py
|
||||
already reads the IPL ROM's four channels the same way, out of the shipping
|
||||
image, and found Sharp's own disk channel at 16..19 clocks a byte (FINDINGS
|
||||
52.5) -- above the entire bracket this project costs P4 in. This is the other
|
||||
half of that comparison: the same MC68450 field tables (tools/analysis/
|
||||
mc68450.py, one copy) applied to the bytes src/player/dma.i actually programs.
|
||||
|
||||
IT PARSES THE SOURCE RATHER THAN RESTATING IT. A constant typed into this file
|
||||
would be a claim about the player that the player could quietly stop honouring;
|
||||
the equates are read out of src/player/dma.i, so a change there changes what is
|
||||
printed here and a mismatch between the two is not expressible.
|
||||
|
||||
IT IS A GATE. Each configuration is checked against what it is FOR -- held
|
||||
must decode as a mode that keeps the bus, stealing must decode as one that does
|
||||
not -- and a disagreement exits non-zero rather than printing a paragraph.
|
||||
|
||||
WHAT IT IS NOT: a rate. Nothing here is a measurement of anything. It says
|
||||
which mode the player asks the chip for; tools/bench/dma_run.sh shows the
|
||||
machine doing it, and `W` -- the clocks it costs on real silicon -- remains the
|
||||
project's largest open number (ROADMAP B1/B3).
|
||||
"""
|
||||
import sys, os, re
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from mc68450 import dcr, ocr, scr, XRM, DTYP, REQG
|
||||
|
||||
src = sys.argv[1] if len(sys.argv) > 1 else "src/player/dma.i"
|
||||
if not os.path.exists(src):
|
||||
sys.exit(f"missing {src} -- run from the repo root.")
|
||||
text = open(src).read()
|
||||
|
||||
def equ(name):
|
||||
m = re.search(rf"^{name}\s*=\s*\$([0-9A-Fa-f]+)", text, re.M)
|
||||
if not m:
|
||||
sys.exit(f"{src} no longer defines {name}. This script reads the "
|
||||
f"player's own equates; it does not keep a copy of them.")
|
||||
return int(m.group(1), 16)
|
||||
|
||||
# The SCR the channel is given is written inline rather than equated, because it
|
||||
# is the same for both configurations and there is nothing to choose about it.
|
||||
m = re.search(r"move\.b\s+#\$([0-9A-Fa-f]+),DM_SCR", text)
|
||||
if not m:
|
||||
sys.exit(f"{src} no longer writes DM_SCR with a literal.")
|
||||
SCR = int(m.group(1), 16)
|
||||
|
||||
CFG = [("BUS HELD", "DM_HELD_DCR", "DM_HELD_OCR"),
|
||||
("CYCLE STEALING", "DM_STEAL_DCR", "DM_STEAL_OCR")]
|
||||
|
||||
print(f"WHAT src/player/dma.i PROGRAMS -- decoded from {src}\n")
|
||||
bad = 0
|
||||
for label, dn, on in CFG:
|
||||
D, O = equ(dn), equ(on)
|
||||
print(f" {label} DCR = ${D:02X} OCR = ${O:02X} SCR = ${SCR:02X}")
|
||||
for line in dcr(D):
|
||||
print(f" {line}")
|
||||
for line in ocr(O):
|
||||
print(f" {line}")
|
||||
for line in scr(SCR):
|
||||
print(f" {line}")
|
||||
holds = (D >> 6 & 3) in (0, 3) # burst, or cycle steal WITH hold
|
||||
dual = (D >> 4 & 3) in (0, 1)
|
||||
tomem = bool(O & 0x80)
|
||||
checks = [
|
||||
(dual, "DTYP must be explicitly addressed: only channel 0 has device "
|
||||
"callbacks in this machine, so an implicit-address DTYP on "
|
||||
"channel 1 falls through to the dual-address path anyway"),
|
||||
(tomem, "OCR DIR must be device -> memory; this is a READ"),
|
||||
((O >> 4 & 3) == 0, "OCR SIZE must be byte: the SPC's port is 8 bits"),
|
||||
((O >> 2 & 3) == 0, "OCR CHAIN must be none until P5a picks a chaining "
|
||||
"scheme for the two-deep request queue (FINDINGS 55.3)"),
|
||||
((SCR & 3) == 0, "SCR DAC must not count: the device address is a "
|
||||
"REGISTER at $EA0015 and must not walk off it"),
|
||||
((SCR >> 2 & 3) == 1, "SCR MAC must increment: the record is contiguous"),
|
||||
((O & 3) in (0, 1), "OCR REQG must be an AUTO-request mode: the "
|
||||
"expansion slot has no request line to the DMAC in "
|
||||
"this machine, so external request cannot be run"),
|
||||
]
|
||||
if label == "BUS HELD":
|
||||
checks.append((holds, "the held configuration must decode as a mode "
|
||||
"that KEEPS the bus between operands"))
|
||||
checks.append(((O & 3) == 1, "and as max-rate auto-request: MAME models "
|
||||
"a held bus only for burst + REQG 01"))
|
||||
else:
|
||||
checks.append((not holds, "the stealing configuration must decode as a "
|
||||
"mode that RELEASES the bus between operands "
|
||||
"-- otherwise the two have no contrast"))
|
||||
for ok, why in checks:
|
||||
if not ok:
|
||||
print(f" FAIL: {why}")
|
||||
bad += 1
|
||||
print()
|
||||
|
||||
print("""AGAINST THE MACHINE'S OWN DISK CHANNEL (21_iplrom_dmac.py, FINDINGS 52.5)
|
||||
|
||||
IPL ROM ch1, SASI DCR $80 OCR $B2 dual address, 8-bit port, cycle steal
|
||||
WITHOUT hold, EXTERNAL request
|
||||
-> a full arbitration per byte, 16..19
|
||||
player, held DCR $00 OCR $81 dual address, 8-bit port, BURST,
|
||||
auto-request at max rate
|
||||
-> the ladder's dual-address held row, 9
|
||||
|
||||
Sharp's own configuration and the player's differ in exactly the field that
|
||||
decides the project. That is 52.5's finding read the other way round: a cheaper
|
||||
configuration IS reachable for an explicitly-addressed 8-bit port, and what it
|
||||
costs on real silicon is still ROADMAP B3's question and not this file's.""")
|
||||
sys.exit(1 if bad else 0)
|
||||
Reference in New Issue
Block a user