# Magikarp Topper Serial Key and Emulator ## Scope Target: - `/games/pokemon_pro/game` Generated artifacts: - `analysis/topper-emulator/topper_emulator.py` - `analysis/topper-emulator/README.md` Static caveat: this page is based on static rootfs files and targeted Ghidra decompilation. It does not include live cabinet or logic-analyzer validation. ## Transport Finding The Magikarp topper path uses the generic SPIKE node-board bus. Evidence: - `/games/pokemon_pro/spike3/netbridge/netbridge.elf` contains debug/source strings including `uart_node.c`, `NODEBUS_Poll`, `NODEUART_Write`, `NODEUART_WritePoll`, and `NODEUART_WriteAddress`. - `/games/pokemon_pro/game` function `FUN_00a21a20` constructs addressed node frames and calls lower transport helper `FUN_00a4c4c0`. - `/games/pokemon_pro/game` functions `FUN_0090aff0`, `FUN_00a23450`, `FUN_00a26690`, and `FUN_00a26700` implement discovery/address-management around node addresses, not USB or cloud registration. Inference: the MicroPython emulator should target a UART-style nodebus interface first. The rootfs does load `i2c-dev`, but the topper-specific evidence reviewed here points to nodebus UART. Exact pins, voltage, baud, and half-duplex behavior still need hardware validation. ## Frame Format Recovered From `FUN_00a21a20` For addressed commands, higher-level helpers pass an application frame shaped like: ```text byte 0: node_address | 0x80 byte 1: application argument count byte 2..N: command and arguments last: response payload length ``` `FUN_00a21a20` converts that to this wire frame: ```text byte 0: node_address | 0x80 byte 1: application argument count + 1 byte 2..N: command and arguments next: subtractive checksum over bytes before the checksum last: response payload length + 2 ``` The response is expected as: ```text bytes 0..payload_len-1: payload next: subtractive checksum over payload last: status bits; status bits 0x0c cause the game to reject the response ``` Discovery is special: `FUN_00a23450` sends a single zero byte and expects one raw response byte containing a discovered node address. ## Serial Number Key Algorithm The strongest recovered key path is: - `FUN_006a9620` gets logical node device `10` with `FUN_007cbc80(10)`. - It verifies the node is available with `FUN_007cbfb0(10, 1)`. - It passes the configured node address byte from device entry offset `0x1a` to `FUN_00a28a00(address, out)`. - `FUN_00a28a00` sends command `0xfc` with expected response length `0x10`. - `FUN_006a9620` treats the 16 returned bytes as four little-endian 32-bit words and computes: ```c key = (word0 ^ word1 ^ word2 ^ word3) & 0x7fffffff; ``` This is the recovered algorithm for the Magikarp topper serial-number key material read from the control board. Important namespace note: - The public adjustment name table places `AD_MAGIKARP_TOPPER_SERIAL_NUMBER_KEY` at index `0x1a3`. Nearby entries are: - `0x19b`: `AD_MAGIKARP_MECH_DISABLED` - `0x19c`: `AD_MAGIKARP_HEAD_HI_LIMIT` - `0x19d`: `AD_MAGIKARP_HEAD_LOW_LIMIT` - `0x19e`: `AD_MAGIKARP_TAIL_HI_LIMIT` - `0x19f`: `AD_MAGIKARP_TAIL_LOW_LIMIT` - `0x1a0`: `AD_MAGIKARP_WATER_CENTER` - `0x1a1`: `AD_MAGIKARP_ATTRACT_BEHAVIOR` - `0x1a2`: `AD_MAGIKARP_ATTRACT_FREQUENCY` - `0x1a3`: `AD_MAGIKARP_TOPPER_SERIAL_NUMBER_KEY` - The Magikarp runtime functions use constants such as `0xa6..0xaa` and `0xad` with `FUN_007d3330` / `FUN_007d39c0`. Static table dumping showed those are not direct indexes into the public `AD_*` name-pointer table, so they should be treated as a separate adjustment-access namespace or translated runtime IDs. ## Persistence and Calibration Path `FUN_006a9570` compares the recovered key from object offset `+0x0c` against `FUN_007d3330(0xad)`. If the stored value is zero or mismatched, and the calibration/config payload is present and valid, it stores the recovered key with `FUN_007d39c0(0xad, key)` and marks the local state as synchronized. Calibration validity is checked by `FUN_006a8f50`: ```text byte 0: 0x01 byte 1..3: ASCII "-15" byte 4..8: five Magikarp calibration bytes byte 9..10: little-endian ((sum(bytes 4..8) ^ 0x93d3) & 0xffff) ``` `FUN_006a9920` constructs the same payload from five calibration adjustment reads. Static dump of the pointer used as the three-byte magic resolves to target address `0x0268ab40`, whose first bytes are ASCII `-15`. `FUN_0090c290` and `FUN_0090c310` copy a 64-byte node config/cache block from the generic node record. They treat bytes `0x3c..0x3f` equal to `0x3254789a` as a stored-valid marker. ## Emulator The generated emulator is `analysis/topper-emulator/topper_emulator.py`. It implements: - Discovery request `0x00`. - Address-management command `0xf0` as a no-op ACK path. - Config read command `0xf2` in 8-byte chunks. - Serial-key command `0xfc`, returning the 16-byte key material. - Adjacent serial/cache helper command `0xfd`. - Identity command `0xfe`. - Status command `0xff`. - Selected provisioning/runtime commands as ACK/no-op paths: `0x14`, `0x40`, `0x41`, `0x42`, `0x43`, `0x44`, `0x45`, `0x46`, `0x48`, `0x4d`, `0x72`, `0xf4`, `0xf8`. Host-side validation performed: ```text python3 -m py_compile analysis/topper-emulator/topper_emulator.py ``` Frame self-test: ```text request: 8a02fc7812 response length: 18 response payload matched serial material: True response status: 0 ``` The sample request is command `0xfc` for node address `10`, using the recovered wire-frame checksum and expected response length. ## Open Hardware Variables These values must be matched against a real board or a known-good capture: - `NODE_ADDRESS`: logical device `10` resolves to a configured node address at runtime via `FUN_007cbc80(10)`, device-entry offset `0x1a`. This value was not recovered as a fixed static literal. - UART ID, pins, baud, electrical level, and bus direction control. - `IDENTITY_BOARD_PART_ID`: command `0xfe` returns an 11-byte identity payload. The game compares its part ID against the expected node-device table. This part ID was not confidently recovered from static analysis. - Exact serial/version fields expected by diagnostics. - Whether live netbridge behavior requires breaks, XON/XOFF, timing gaps, or address arbitration before the game accepts a MicroPython device on the physical bus. ## Confidence High confidence: - Command `0xfc` supplies 16 bytes used for the key calculation. - The key calculation is XOR of four little-endian dwords masked with `0x7fffffff`. - Logical topper node device ID is `10`. - Calibration block predicate and checksum are recovered. - The implemented node response checksum/status shape matches `FUN_00a21a20`. Moderate confidence: - The implemented no-op handling is enough for startup/provisioning if identity fields match. - Returning zero in the second status word for command `0xff` avoids the generic unsigned/problem bit paths. Low confidence until hardware-tested: - Physical-layer UART settings and direction handling. - Correct `IDENTITY_BOARD_PART_ID` for a live Magikarp topper board.