Price the transport against the frame, and find dual address cannot fit

FINDINGS 59.7. tools/analysis/15_bus_occupancy.py has always answered "what does
each W cost" and never "what can the frame afford", and after 59.2 those are not
the same question. It now answers both, and takes an optional --kbps for the
auto-request rows -- the only rows whose cost depends on how long the record
takes to arrive.

On the gate container at 12 fps, decode term MEASURED: decode 68.5%, audio 1.25%,
HEADROOM 30.2% = 6.74 clocks per byte at a 37,403 B record. Against that, P4a
cut the transport from 391.7% of a frame to 40..95% -- four to ten times, the
largest movement in this project's cost model since the decoder was written --
and it still does not fit. A dual-address byte is a 4-clock read of the device
plus a 5-clock write to memory, so 9 clk/B is a FLOOR and the frame affords 6.74.
No GCR share goes under it and no delivery rate goes under it: a share decides
whether the channel sits at the floor or above it. At 460 KB/s max-rate totals
165.1% and LRAR at 50% totals 117.4%, and a 50% share tops out at 543 KB/s, above
which the channel is the bottleneck and the rate falls back to exactly that floor.

So 59.2's three bounds arrive in the budget as one sentence: the configurations
this machine can run are the ones the frame cannot afford, and the one it can
afford -- single address, 5 clk/B, 92.2% total, 7.8% spare -- needs the device to
ACK the DMAC, which needs a request line MAME does not connect and the slot
pinout does have at B36/B37.

ROADMAP re-ranks accordingly. B3 stops being a constant to look up and becomes
DOES THE CARD DRIVE #EXREQ, ahead of B1: B1 sets how much headroom the player
has, B3 decides whether there is any. New E7 carries the other branch -- if the
answer is no, the container must reach 27,995 B a frame, 328 KB/s of payload,
against 438 now. The dependency diagram is redrawn around that fork.

The scope is stated rather than buried: this is the GATE container, deliberately
the heaviest thing the encoder emits, and the lighter cpufit family was NOT
priced -- 15_bus_occupancy.py refuses it, correctly, because the C68K measurement
it cross-checks against belongs to the gate container. E7 therefore begins with a
harness re-run, and until then "34% too big" is a statement about the fixture and
not about the project.

check.sh ALL GREEN before and after.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
prosolis
2026-08-25 00:17:29 -07:00
parent 621a5bb457
commit 8800d8f8c0
5 changed files with 281 additions and 20 deletions
+17
View File
@@ -172,6 +172,23 @@ to land, and of the four bus shares the GCR can be programmed for — 50, 25,
a frame. The GCR is a design lever nothing in this tree had named (FINDINGS a frame. The GCR is a design lever nothing in this tree had named (FINDINGS
59.3). 59.3).
**And what it all costs: the frame affords 6.74 clocks a byte, and a
dual-address byte is 9.** Putting the transport on the channel cuts it from
**391.7% of a 12 fps frame to 40..95%** — four to ten times, the largest
movement in this project's cost model since the decoder was written — and **it
still does not fit.** After the measured decode (68.5%) and the audio DMA
(1.25%), 30.2% of the frame is left, which at this container's 37,403 B record
is 6.74 clocks a byte; a dual-address byte is a 4-clock read of the device plus
a 5-clock write to memory, so **9 is a floor no bus share and no delivery rate
goes under**. Single address is 5 and fits at 92.2% with room to spare — and it
needs the device to ACK the DMAC, which needs a request line MAME does not
connect and the slot pinout does have. So the project's live question is now a
fact about a board: **does a real CZ-6BS1 drive `#EXREQ`?** If it does, the
design fits. If it does not, the container has to come down from 438 KB/s of
payload to **328** — which is an encoder target, entirely inside this project,
and measured against the heaviest container the encoder emits rather than
against a shipping one (FINDINGS 59.7).
**A record is not a sector, and the cheapest fix is a re-encode.** 117 of 120 **A record is not a sector, and the cheapest fix is a re-encode.** 117 of 120
records start part way into a 512 B block, and reading whole blocks into the records start part way into a 512 B block, and reading whole blocks into the
ring corrupts the neighbouring records rather than merely wasting bytes — the ring corrupts the neighbouring records rather than merely wasting bytes — the
+72
View File
@@ -5731,3 +5731,75 @@ any of this work, so the red was unambiguously new, and the failure was in a
stage nobody would have re-run on suspicion. Both halves of "green before and stage nobody would have re-run on suspicion. Both halves of "green before and
green after" earned their place; the map is now `$18500`, clear of everything green after" earned their place; the map is now `$18500`, clear of everything
the streaming rig owns. the streaming rig owns.
### 59.7 What it all costs: the frame affords 6.74 clocks a byte, and a dual-address byte is 9
The sweep in `15_bus_occupancy.py` has always answered *"what does each `W`
cost"*. It never answered *"what can the frame afford"*, and after 59.2 those
stopped being the same question. The tool now answers both; every figure below
is it, on the gate container at 12 fps, and the decode term is **measured**
(C68K) while the transport terms are datasheet arithmetic — except PIO, which
58.2 measured.
frame slot 833,333 clk
decoder, MEASURED 570,958 clk 68.5% (worst frame 110.8%)
audio DMA 10,417 clk 1.25%
------------------------------------------
HEADROOM 251,958 clk 30.2% = 6.74 clk/B at a 37,403 B record
| transport | clk/B | video | CPU+audio+video |
|---|---|---|---|
| PIO — **measured**, 58.2 | 87.28 | 391.7% | **461.5%** |
| dual address, arbitrated — the IPL ROM's own (52.5) | 16..19 | 71.8..85.3% | 141.6..155.0% |
| single address, arbitrated | 12 | 53.9% | 123.6% |
| **dual address, held — and the FLOOR of every dual-address configuration** | **9** | 40.4% | **110.2%** |
| single address, held | 5 | 22.4% | **92.2%** |
**WHAT P4a BOUGHT: the transport falls from 391.7% of a frame to 40..95%**, four
to ten times. That is the whole of the gain and it is the largest single
movement in the project's cost model since the decoder was written.
**WHAT IT DID NOT BUY IS A FIT, and the reason is one line of arithmetic.** A
dual-address byte is a 4-clock read of the device and a 5-clock write to memory
`buscost.DMA_DUAL_BYTE_CLK`, Fig 4-25 — so **9 clocks is a floor, and the
frame affords 6.74.** No GCR share goes under it and no delivery rate goes under
it: a share decides whether the channel sits *at* the floor or *above* it. So
**59.2's three bounds arrive in the budget as one sentence — the configurations
this machine can run are exactly the ones the frame cannot afford**, and the one
it can afford is single address, which needs the device to ACK, which needs the
request line ROADMAP B3 asks about.
**The rate sweep says the same thing from the other side.** At 460 KB/s
max-rate totals **165.1%** and LRAR at 50% totals **117.4%**; the smaller shares
total less but cannot carry the rate at all. A faster disc makes auto-request
cheaper — which no `W` does, and it is the first cost in this project that
improves when the medium does — but it cannot reach the floor: **a 50% share
tops out at 543 KB/s**, above which the channel is the bottleneck and the
delivered rate falls back to it, at which point the cost is exactly 9 clk/B
again.
**SO THE FIT NOW TURNS ON ONE OF TWO THINGS, AND ONE OF THEM IS OURS.**
1. **Does a real CZ-6BS1 drive `#EXREQ`/`#EXACK`?** It is the only route to
single address, 22.4%, 92.2% total, fitting with 7.8% to spare. MAME cannot
answer it — it does not connect the pin (59.2) — and the slot pinout has it
at B36/B37. **This is now the sharpest form ROADMAP B3 has ever had**, and it
is worth more than the throughput half of B1: B1 sets how much headroom the
player has, B3 decides whether there is any.
2. **Bytes.** At the 9 clk/B floor this container must come down to **27,995 B a
frame — 328 KB/s of payload**, against the 37,403 B and 438 KB/s it is now:
**34% too big.** That is an encoder target, it is entirely inside this
project, and **it is the pessimistic reading of the lever**, because a
lighter container also decodes cheaper and the decode term falls with the
byte term.
**WHAT THIS IS NOT.** It is the **gate container**, which is deliberately the
heaviest thing the encoder emits — the span-heavy scsi container encoded to the
488 recipe, chosen so that every block mode and the newest span path are
exercised (check.sh's own note). The lighter `cpufit` family exists and was NOT
measured here: `15_bus_occupancy.py` refuses it, correctly, because the C68K
measurement in `tmp/c68k_frames.csv` belongs to the gate container and the
cross-check at the top of the tool is what licenses every number under it.
**Re-deriving 59.7 against a lighter container needs that harness re-run first**,
and until it is, "34% too big" is a statement about the heaviest container and
not about the project.
+70 -13
View File
@@ -11,7 +11,9 @@ Amended end of session 26: P4b done, P4a is the last open item before M2
Amended end of session 27: P4a done at the transport level; THE RE-ENCODE Amended end of session 27: P4a done at the transport level; THE RE-ENCODE
BUNDLE under P2 is now the only thing between this tree and M2, because 59.4 BUNDLE under P2 is now the only thing between this tree and M2, because 59.4
made sector-aligned records a precondition the transport enforces rather than a made sector-aligned records a precondition the transport enforces rather than a
preference (FINDINGS 59). preference (FINDINGS 59). **And 59.7 re-ranks what is left: the frame affords
6.74 clocks a byte, a dual-address byte costs 9, so B3 stopped being a constant
to look up and became the question of whether the design fits at all.**
**THE COMPLETION TARGET IS M3, THE VERTICAL SLICE** (USER DECISION): one scene **THE COMPLETION TARGET IS M3, THE VERTICAL SLICE** (USER DECISION): one scene
tree — a decision point, two outcomes, a death clip — with audio, streaming from tree — a decision point, two outcomes, a death clip — with audio, streaming from
@@ -39,6 +41,7 @@ these units:
| **Delivery rate** | **no working figure, deliberately** (FINDINGS 50, USER DECISION). Every tool REQUIRES an explicit rate. | | **Delivery rate** | **no working figure, deliberately** (FINDINGS 50, USER DECISION). Every tool REQUIRES an explicit rate. |
| **Seek time** | **no figure at all, and never had one.** 51.3/51.4 made it matter. | | **Seek time** | **no figure at all, and never had one.** 51.3/51.4 made it matter. |
| **W, clocks stolen per delivered byte** | 5 single-address held, 9 dual held, 12 single arbitrated; the IPL ROM's own disk channel is **16..19** (52.5). **The largest open number in the project.** Session 27 added the row underneath it: with **no external request line** on the card (59.2) the channel is auto-requested and is charged **by time rather than by byte**, so at 460 KB/s a 50% bus share costs **10.61 clk/B** and a smaller share cannot carry the rate at all (59.3). | | **W, clocks stolen per delivered byte** | 5 single-address held, 9 dual held, 12 single arbitrated; the IPL ROM's own disk channel is **16..19** (52.5). **The largest open number in the project.** Session 27 added the row underneath it: with **no external request line** on the card (59.2) the channel is auto-requested and is charged **by time rather than by byte**, so at 460 KB/s a 50% bus share costs **10.61 clk/B** and a smaller share cannot carry the rate at all (59.3). |
| **The frame's headroom for a transport** | **6.74 clk/B** — 30.2% of a 12 fps frame, after the MEASURED decode (68.5%) and best-case audio (1.25%), at the gate container's 37,403 B record. **New in session 27, 59.7, and it is the number every row above is now read against.** A dual-address byte's floor is 9. |
--- ---
@@ -80,12 +83,34 @@ and settles it in minutes on a real board. FINDINGS 48 shifted the prior toward
MAME and toward "unusable" — **do not pre-build on 1.0 B/pixel**. Same sitting: MAME and toward "unusable" — **do not pre-build on 1.0 B/pixel**. Same sitting:
the priority register `0xE82500` at `0x0000` (47.3). the priority register `0xE82500` at `0x0000` (47.3).
**B3. Single-address vs dual-address DMA.** 242 KB/s and 0.69 dB. Needs **B3. Single-address vs dual-address DMA — and now, first, DOES THE CARD DRIVE
`scsiexrom.bin` (8 KB, CRC `7be488de`) sourced, then its DMAC init disassembled `#EXREQ`?** 242 KB/s and 0.69 dB was the old framing. **Session 27 promoted this
for DCR's DTYP: `10`/`11` = single (5.0 clk/B), `00`/`01` = dual (9.0). to the item that decides whether the design fits at all (59.7).** The frame
affords **6.74 clocks a byte**; a dual-address byte is **9** — a 4-clock read of
the device plus a 5-clock write to memory — so **no dual-address configuration
fits this container at 12 fps, at any delivery rate and any GCR share.** Single
address is 5 clk/B and fits at 92.2% of the frame with 7.8% to spare, and it
needs the device to ACK the DMAC directly, which needs the request line.
**MAME cannot answer it**: `x68k_scsiext.cpp`'s `drq_w` only stores a flag and
the expansion slot has no request path to the HD63450 at all (59.2). The slot
PINOUT has `#EXREQ` at B36 and `#EXACK` at B37, so the provision exists on the
real board. **What is wanted from hardware is therefore narrower and sharper
than "disassemble the ROM": does a CZ-6BS1 assert `#EXREQ` during a data phase,
and will the HD63450 run a single-address channel against it?** A scope or a
logic analyser answers the first; the second is a program the player can run.
Sourcing `scsiexrom.bin` (8 KB, CRC `7be488de`) and disassembling its DMAC init
is still the cheapest paper route to the same answer, because whatever Sharp's
own driver programs into DCR's DTYP is a statement about what the card supports.
FINDINGS 48.4. Not on this machine (checked, session 18). FINDINGS 48.4. Not on this machine (checked, session 18).
**This is also P4's input** — the handshake the player drives is the same
question from the software side. **Ranking, stated because B1 used to outrank this:** B1 (throughput and seek)
sets how much headroom the finished player has. **B3 decides whether there is
any.** If the card drives `#EXREQ`, the ladder applies and the design fits with
room. If it does not, the fallback is limited-rate auto-request at a share the
player picks (P4c), and the container has to come down to **328 KB/s of payload**
to fit at the 9 clk/B floor — 34% below where the gate container sits (59.7).
> **Session 20 moved the prior hard, and it moved the wrong way (FINDINGS 52.5).** > **Session 20 moved the prior hard, and it moved the wrong way (FINDINGS 52.5).**
> The IPL ROM *is* on this machine, and `tools/analysis/21_iplrom_dmac.py` reads > The IPL ROM *is* on this machine, and `tools/analysis/21_iplrom_dmac.py` reads
@@ -382,6 +407,30 @@ interaction to price next, and it is E2's question with a second consumer in it.
**E6. Container v2** — audio interleave, per-record index, scene table. Depends **E6. Container v2** — audio interleave, per-record index, scene table. Depends
on P6's answer and on P5's index. on P6's answer and on P5's index.
**E7. A BYTE TARGET, AND IT COMES OUT OF THE BUS BUDGET RATHER THAN OUT OF
TASTE (new, session 27, FINDINGS 59.7).** The frame affords **6.74 clocks a
byte** after the measured decode and the audio, and a dual-address byte costs
**9**. So *if* B3 comes back saying the card cannot drive `#EXREQ`, the container
has to reach **27,995 B a frame — 328 KB/s of payload** to fit at 12 fps, where
the gate container sits at 37,403 B and 438 KB/s: **34% too big.**
Three things make this less alarming than the number looks, and one makes it
worse:
- The gate container is **deliberately the heaviest thing the encoder emits**
(span-heavy, the 488 recipe, every block mode exercised). It is a test
fixture, not a shipping target.
- A lighter container **also decodes cheaper**, so the 68.5% decode term falls
with the byte term. 328 KB/s is the pessimistic reading of the lever.
- `rc_fr_singe_scsi_cpufit.dlx` already exists — the encoder has had a
CPU-fitting mode since session 11.
- **Worse:** `15_bus_occupancy.py` REFUSES to price the cpufit container,
correctly, because the C68K measurement it cross-checks against belongs to
the gate container. **So E7 starts with a harness re-run**
(`tools/bench/c68k/run.sh`) against whichever container is to be the target,
and until that is done "34% too big" is a statement about the fixture rather
than about the project.
~~**G1. Import the scene graph — early, because it is a measurement input.**~~ ~~**G1. Import the scene graph — early, because it is a measurement input.**~~
**DONE, session 24 — FINDINGS 56.** It was pulled ahead for exactly the reason **DONE, session 24 — FINDINGS 56.** It was pulled ahead for exactly the reason
given, and it paid: **the worst gap between two consecutive decision points is given, and it paid: **the worst gap between two consecutive decision points is
@@ -435,16 +484,24 @@ Listed for completeness; past M3 these are scope, not risk.
## Dependency summary ## Dependency summary
``` ```
B1 seek+rate ─┐ P4a DONE (59): the channel drives the data phase and
B3 DTYP ──────┴─> P4a DMA HOLDS THE BUS ──┐ (P4b DONE, 58: the ring is holds the bus -- 391.7% of a frame becomes 40..95%
│ filled off a real volume, and
P1 P2(half) P3 P4b P5 P7 ─────────────────┤ PIO costs 87 clk/B) B3 #EXREQ? ──┬─ YES ─> single address, 5 clk/B, 92.2% ── FITS ──┐
├─> M2 ─> M3 (TARGET) ─> M4 │ │
P6 (bus cost DONE, 52) ───────────────────┤ └─ NO ──> auto-request, 9 clk/B FLOOR, 110.2% ──> E7 byte target
G1 scene graph (DONE, 56) ────────────────┘ (the frame affords 6.74; 59.7) 328 KB/s
P2 re-encode bundle (sector alignment is now a PRECONDITION, 59.4) ┤
P1 P3 P4b P5 P7, P6 bus cost (52), G1 scene graph (56) ────────────┼─> M2 ─>
B1 seek+rate (sets HEADROOM, not fit) ─────────────────────────────┘ M3 ─> M4
B2 blanking ─> (page 1; do not pre-build on it) B2 blanking ─> (page 1; do not pre-build on it)
``` ```
**Read that top-left branch as the project's live question.** Everything else
on the diagram is work; `#EXREQ` is a fact about a board nobody here has, and it
decides which of the two lower paths the player is on.
## Standing rules that apply to all of it ## Standing rules that apply to all of it
- **Green light first and last.** `./tools/bench/check.sh`, ALL GREEN, before and - **Green light first and last.** `./tools/bench/check.sh`, ALL GREEN, before and
+27
View File
@@ -57,6 +57,33 @@ writes a contiguous run and cannot drop the 300 B in front of a record, so
"sector-aligned records should join the bundle" has become "the transport will "sector-aligned records should join the bundle" has become "the transport will
not run without them". not run without them".
**6. WHAT IT ALL COSTS, and this is the part that re-ranks the roadmap.**
`15_bus_occupancy.py` now answers "what can the frame afford" as well as "what
does each `W` cost". On the gate container at 12 fps, with the decode term
MEASURED: decode 68.5%, audio 1.25%, **headroom 30.2% = 6.74 clocks per byte**
at a 37,403 B record.
| transport | clk/B | video | total |
|---|---|---|---|
| PIO, measured (58.2) | 87.28 | 391.7% | **461.5%** |
| dual address, held — **the floor** | 9 | 40.4% | **110.2%** |
| single address, held | 5 | 22.4% | **92.2%** |
**P4a cut the transport from 391.7% to 40..95%, four to ten times — and it still
does not fit.** A dual-address byte is 4 clocks of device read plus 5 of memory
write, so **9 is a floor and the frame affords 6.74**; no GCR share and no
delivery rate goes under it. At 460 KB/s max-rate totals 165.1% and LRAR at 50%
totals 117.4%. So the fit turns on **B3, which has stopped being a constant to
look up: does a real CZ-6BS1 drive `#EXREQ`?** If yes, single address fits at
92.2% with 7.8% spare. If no, the container must reach **328 KB/s of payload**
(new ROADMAP E7), against 438 now.
**The scope matters and is stated in 59.7:** this is the GATE container, the
heaviest thing the encoder emits. The lighter `cpufit` family was NOT priced —
the tool refuses it, correctly, because the C68K measurement belongs to the gate
container — so E7 begins with a harness re-run, and until then "34% too big" is
about the fixture, not about the project.
**ONE COLLISION, AND THE BEFORE-HALF OF `check.sh` CAUGHT IT.** `DM_USE` was **ONE COLLISION, AND THE BEFORE-HALF OF `check.sh` CAUGHT IT.** `DM_USE` was
first placed at `$18300`, which is `ring.i`'s `XF_SLOT` mailbox — `scsi.i`'s first placed at `$18300`, which is `ring.i`'s `XF_SLOT` mailbox — `scsi.i`'s
trace ends at `$182FF` and the next 160 bytes are the ring's. The ring rig's trace ends at `$182FF` and the next 160 bytes are the ring's. The ring rig's
+95 -7
View File
@@ -62,6 +62,11 @@ ap.add_argument("container", nargs="?", default="tmp/rc_fr_singe_scsi_cpufit.dlx
ap.add_argument("--csv", default="tmp/c68k_frames.csv", ap.add_argument("--csv", default="tmp/c68k_frames.csv",
help="per-frame output of tools/bench/c68k/run.sh") help="per-frame output of tools/bench/c68k/run.sh")
ap.add_argument("--nframes", type=int, default=None) ap.add_argument("--nframes", type=int, default=None)
ap.add_argument("--kbps", type=float, default=None,
help="delivery rate in KB/s. OPTIONAL and there is no default "
"(FINDINGS 50): supply it and the AUTO-REQUEST rows are "
"added, which are the only rows whose cost depends on how "
"long the record takes to arrive (59.3).")
a = ap.parse_args() a = ap.parse_args()
if not os.path.exists(a.container): if not os.path.exists(a.container):
sys.exit(f"missing {a.container}") sys.exit(f"missing {a.container}")
@@ -186,19 +191,102 @@ print(f" {'W (clk/byte)':<16}{'clk/frame':>12}{'% of frame':>12} "
f"{'CPU+audio+video':>18}") f"{'CPU+audio+video':>18}")
for W, note in ((5.0, "single address, bus held (11_cpu_budget.py default)"), for W, note in ((5.0, "single address, bus held (11_cpu_budget.py default)"),
(8.0, "FINDINGS 5's long-standing per-word ESTIMATE"), (8.0, "FINDINGS 5's long-standing per-word ESTIMATE"),
(9.0, "DUAL address, bus held -- and the FLOOR of every "
"dual-address\n "
" configuration, auto-request included (59.3)"),
(12.0, "single address, arbitrated per byte"), (12.0, "single address, arbitrated per byte"),
(16.0, "what the ROM programs for SASI (best case)"), (16.0, "what the ROM programs for SASI (best case)"),
(19.0, "what the ROM programs for SASI (worst case)")): (19.0, "what the ROM programs for SASI (worst case)"),
(87.28, "PIO -- MEASURED, FINDINGS 58.2, the CPU doing it itself")):
v = vid_bpf * W v = vid_bpf * W
tot_clk = (cpu_clk if cyc_t.any() else 0) + a_lo + v tot_clk = (cpu_clk if cyc_t.any() else 0) + a_lo + v
print(f" {W:<16.0f}{v:>12,.0f}{100*v/FRAME_CLK:>11.1f}% " print(f" {W:<16.6g}{v:>12,.0f}{100*v/FRAME_CLK:>11.1f}% "
f"{100*tot_clk/FRAME_CLK:>17.1f}% {note}") f"{100*tot_clk/FRAME_CLK:>17.1f}% {note}")
print(f"\n (the last column adds the MEASURED mean decode and the BEST-CASE " print(f"\n (the last column adds the MEASURED mean decode and the BEST-CASE "
f"audio, so it is\n the optimistic end of every row. 100% is the frame " f"audio, so it is\n the optimistic end of every row. 100% is the frame "
f"deadline at {FPS:g} fps.)") f"deadline at {FPS:g} fps.)")
print(f""" print(f"""
Audio is {100*a_lo/FRAME_CLK:.2f}%..{100*a_hi/FRAME_CLK:.2f}% of the frame and video is {vid_bpf*5/FRAME_CLK*100:.0f}%..{vid_bpf*19/FRAME_CLK*100:.0f}%. The unpriced audio Audio is {100*a_lo/FRAME_CLK:.2f}%..{100*a_hi/FRAME_CLK:.2f}% of the frame and video is {vid_bpf*5/FRAME_CLK*100:.0f}%..{vid_bpf*19/FRAME_CLK*100:.0f}% over the ladder, against
stream was never the risk P6 called it -- ON THE BUS. What the same reading of {vid_bpf*87.28/FRAME_CLK*100:.0f}% for the PIO transport FINDINGS 58.2 measured. The unpriced audio stream
the ROM found is that the DISK's per-byte cost has a worked example on this was never the risk P6 called it -- ON THE BUS.""")
machine, it is 16..19 clocks, and at that price this design does not fit at any
container size. W is the number to attack, and it is a PLAYER decision.""") # --- HEADROOM, AND THE FLOOR UNDER THE LADDER -----------------------------
# Added session 27. The sweep above answers "what does each W cost"; it never
# answered "what can this frame afford", and the two are not the same question.
# FINDINGS 59.2 is why it matters now: with no external request line the only
# configurations that can be run are dual-address, and a dual-address byte has
# a FLOOR -- one 4-clock read of the device plus one 5-clock write to memory,
# buscost.DMA_DUAL_BYTE_CLK. No GCR share and no delivery rate goes under it.
print("\n" + "=" * 72)
print("WHAT THE FRAME CAN AFFORD, AND THE FLOOR UNDER THE LADDER\n")
head_clk = FRAME_CLK - (cpu_clk if cyc_t.any() else 0) - a_lo
head_wb = head_clk / vid_bpf
print(f" headroom after the MEASURED decode and best-case audio: "
f"{head_clk:,.0f} clk = {100*head_clk/FRAME_CLK:.1f}%")
print(f" at {vid_bpf:,.0f} B a frame that is {head_wb:.2f} CLOCKS PER BYTE, and "
f"that is the number\n a transport has to come in under.\n")
floor = B.DMA_DUAL_BYTE_CLK
print(f" dual-address floor {floor} clk/B ({B.DMA_READ_CLK} read of the "
f"device + {B.DMA_WRITE_CLK} write to memory, Fig 4-25)")
print(f" single-address held {B.DMA_DISK_CLK_WORD_HELD} clk/B (one memory "
f"write; needs the device to ACK, i.e. a REQUEST LINE)")
if head_wb < floor:
print(f"""
SO DUAL ADDRESS DOES NOT FIT THIS CONTAINER AT {FPS:g} fps -- not at any
delivery rate and not at any GCR share, because {head_wb:.2f} < {floor}. A share
decides whether the channel sits AT the floor or above it; it cannot
go under it. That is FINDINGS 59.2's three bounds arriving in the
budget: the configurations this machine can run are exactly the ones
the frame cannot afford, and the one it can afford -- single address,
{B.DMA_DISK_CLK_WORD_HELD} clk/B, {100*vid_bpf*B.DMA_DISK_CLK_WORD_HELD/FRAME_CLK:.1f}% -- needs the request line ROADMAP B3 asks about.""")
for w, what in ((floor, "dual address"), (B.DMA_DISK_CLK_WORD_HELD, "single address")):
tgt = head_clk / w
print(f"\n TO FIT AT {w} clk/B ({what}) THIS CONTAINER MUST COME DOWN TO")
print(f" {tgt:,.0f} B a frame = {tgt*FPS/1024:,.0f} KB/s of payload "
f"(it is {vid_bpf:,.0f} B, {vid_bpf*FPS/1024:,.0f} KB/s)"
+ (" -- already met" if vid_bpf <= tgt else
f" -- {100*(vid_bpf/tgt-1):.0f}% too big"))
print(f"""
AND THAT IS THE PESSIMISTIC READING OF THE ENCODER LEVER: a lighter
container also DECODES cheaper, so the decode term above falls with
the byte term. The figure to re-derive it against is this tool run on
the lighter container -- with its OWN C68K measurement, because the
cross-check at the top is what licenses every number below it.""")
else:
print(f"\n The frame affords {head_wb:.2f} clk/B, which is at or above the "
f"{floor} clk/B dual-address floor.")
# --- AUTO-REQUEST, and only when a rate is supplied ------------------------
# These are the rows 59.3 added and they are the only ones here whose cost is
# not a property of the transfer: an auto-requested channel spends its share of
# the bus whether or not a byte is there, so what a record costs depends on how
# long it takes to ARRIVE. No default rate, deliberately (FINDINGS 50).
if a.kbps:
RATE = a.kbps * 1024.0
wire_clk = vid_bpf / RATE * CPUHZ
cap = 0.5 * CPUHZ / B.DMA_DUAL_BYTE_CLK # the 50% share's ceiling
print("\n" + "=" * 72)
print(f"AUTO-REQUEST AT {a.kbps:g} KB/s -- charged by TIME, not by byte "
f"(59.3)\n")
print(f" the record takes {wire_clk:,.0f} clk to arrive = "
f"{100*wire_clk/FRAME_CLK:.1f}% of a frame\n")
print(f" {'configuration':<34}{'clk/B':>8}{'% of frame':>12}"
f"{'CPU+audio+video':>18}")
rows = [("REQG 01, max rate (100% of the bus)", 1.0, None)]
for br, share in ((0, .5), (1, .25), (2, .125), (3, .0625)):
rows.append((f"REQG 00, LRAR BR={br:02b}, {share*100:g}% share", share,
share * CPUHZ / B.DMA_DUAL_BYTE_CLK))
for name, share, sustains in rows:
v = share * wire_clk
tot = (cpu_clk if cyc_t.any() else 0) + a_lo + v
flag = ""
if sustains is not None and sustains < RATE:
flag = f" cannot carry the rate ({sustains/1024:.0f} KB/s max)"
print(f" {name:<34}{v/vid_bpf:>8.2f}{100*v/FRAME_CLK:>11.1f}%"
f"{100*tot/FRAME_CLK:>17.1f}%{flag}")
print(f"""
A FASTER DISC MAKES AUTO-REQUEST CHEAPER, which no W does -- the share is
spent over a shorter wire time. But it cannot reach the floor: a 50% share
tops out at {cap/1024:,.0f} KB/s, above which the CHANNEL is the bottleneck and the
delivered rate falls back to it. At that ceiling the cost is exactly the
{B.DMA_DUAL_BYTE_CLK} clk/B floor, which is where the section above already put it.""")