# Addresses that moved, and what that means for the older reports

The probe-hardening pass needed room inside the `$E6F2-$E732` block, which had only three spare
bytes, so six internal helpers were relocated.  Nothing the game, the ABI or the frozen regions name
by address moved - `checkAbi.py` still passes - and every one of these is reached only by a `JSR` or
`JMP` from code that changed in the same pass.

| routine | was | now |
|---|---|---|
| `aciaSetCommandIdle` | `$E72A` | `$E408` |
| `baudEntryTable` | `$E6FD` | `$E706` |
| `queueAndKickTx` | `$E712` | `$E71B` |
| `silenceCia2` | `$E71E` | `$E727` |
| `countLinkError` | `$E403` | `$E73F` |
| `aciaPageLatch` | (new) | `$E5BF` |

`queueByteForTransmit` `$E73C` kept its address and became a tail `JMP` into `queueAndKickTx`.

## The adopt-mode and re-entrancy-guard pass (2026-08-22)

The NMI re-entrancy guard and the probe that reads the user's own line settings before overwriting
them needed 50-odd bytes inside `$E644-$E732`, a block that had six spare bytes in it.  Three
routines left the block, one routine was deleted, one was folded into another, and the table grew an
eighth entry.  Again, nothing the game, the ABI or the frozen regions name by address moved, and
`checkAbi.py` still passes.

| routine | was | now |
|---|---|---|
| `aciaResumeReceiver` | `$E658` | `$E5B1` |
| `silenceCia2` | `$E727` | `$E56F` |
| `dropDtrLine` | `$E574` | `$E408` |
| `aciaSetPage` | `$E663` | `$E6FA` |
| `aciaProbe` | `$E6F2` | `$E655`, renamed `aciaProbeAdopted` - it reads the control register before it writes one |
| `baudEntryTable` | `$E706` | `$E710`, and 23 bytes wide: eight entries, not seven (the last entry's spare byte is not needed) |
| `queueAndKickTx` | `$E71B` | `$E727` |
| the NMI handler's chain exit | `$E679` | `$E67B` |
| `aciaDetectDone` | `$E654` (as `setBitPeriodFull`) | `$E654`, unchanged, and now the shared 'nothing to do' RTS of the transmit path as well |

Deleted outright: `aciaSetCommandIdle` `$E408` (the re-entrancy guard makes its second half provably
unnecessary at the one site that called it, so the NMI's disarm is a plain `aciaSetCommand` again),
`clearUartState` `$E5B6` (its whole inline block was the single byte `txCharActive`, which
`clearTxCharActive` `$E04F` already clears in the same three bytes at the one call site), `setBitPeriod`
`$E657` and the shared transmit-path RTS `L_E662` (its callers now branch to `aciaDetectDone` `$E654`,
which is the same instruction).  `aciaRxByte` `$E5BD` became `nmiHandlerState`, the guard's own byte:
the receive half tests the error bits before it takes the character out of the data register, so it no
longer needs anywhere to park it.

`dropDtrLine` moving is the only one of these that is visible outside the driver's own block: the
`JSR` operand at `$E38E` inside `hangUpModem` had to follow it, which is why `checkAbi.py` now lists
`$E38E-$E38F` as changed.
The dead `ignoreLinkError` stub that used to sit at `$E408-$E40E` was deleted to make the space; it
was provably unreachable, and both halves of the error-vector pair at `$E3C7`/`$E3C9` were repointed
at `countLinkError` so that even the never-selected configuration lands on a real hook.

## Why the older reports were not rewritten

`realtimeReport.md`, `strapReport.md`, `rate1200Report.md`, `highRateReport.md`, `hotkeyReport.md`,
`testReport.md` and `spec_uart.md` cite several of the old addresses - `$E403` most often, because
`countLinkError` is where the error tracepoint was set in every measurement.

Those documents are records of what was observed on the build that existed when the measurement was
taken.  Editing the addresses inside them would make them describe a run that never happened, so they
have been left alone.  Read an address in a test report as "the address that routine had at the time
of that run", and use the table above to map it onto the current source.

`README.md` is the exception: it documents the driver as it stands now, and it was updated.
`wedgeDiagnosis.md` is a second exception in the other direction: it is a record of measurements made
against the build that still had the fault, so its addresses are the ones that build had.

## Conclusions that were overturned

The same rule applies to a report's *conclusions*, and for the same reason.  Where a later measurement
has overturned one, the older file keeps its numbers and gains a note at the top pointing at the newer
result - the way this file points at moved addresses.  Three exist so far:

| File | What was overturned | By |
|---|---|---|
| `highRateReport.md` | "Must not be selected: 38400", and 9600/19200 as degraded modes | `wedgeDiagnosis.md` found the cause; `portCompatReport.md` sections 3-4 measured the guarded build |
| `hardenReport.md` | the 38400 retirement (its hot-key code blanked) | the same, plus `testAdopt.py`: C= + 8 selects 38400 again |
| `strapReport.md` | the two probe weaknesses in its section 8 | `hardenReport.md` (two patterns, and the page latched once per cold start) and `portCompatReport.md` section 9.1 (the `$DF00` fallback now reads that page before writing it) |

`spec_uart.md` carries a note of the same kind on its hazards checklist: hazard 2's character-time
arithmetic is three times out, and hazard 8's "same 8N1 framing" now describes only the data bits,
since the driver adopts the port's own stop bits and clock source.
