Raw '30 04' is a READ(4), so it now earns the same region line as the function-call form —
the annotation follows the bytes, not the syntax:
30 04 -> 04-07 → user memory
3A 00 06 -> 00-02 → UID/serial · 03 → CC · 04-06 → user memory
30 E3 -> E3 → CFG0 · E4 → CFG1 · E5 → PWD · E6 → PACK
catalog.pages_for_payload() reverse-maps a raw payload to the pages it touches by matching the
opcode (payload[0]) to a page/block command and reading its page argument from payload[1:].
dispatch's raw path prints region_annotation() after the exchange, mirroring _handle_call.
Works from either entry mode (it operates on the decoded bytes).
Along the way: TagCommand.opcode() centralises opcode extraction, tolerating data args (valid
hex placeholder) and two-phase (multi-frame) builds — fixing a latent bug where COMPAT_WRITE's
opcode came back as bytes/None. The completer now keys landmark suggestions on the first
parameter being a page/block/start address, so FAST_READ( gets the memory map too.
Hardware-verified on a real NTAG216 (raw 30 04 / 3A 00 06 / 30 E3, and the binary form
00110000 00000100). Tests for the reverse-map, the two-phase opcode, and the raw dispatch
annotation. 1274 green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>