#!/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)