6.9 KiB
Magikarp Topper Serial Key and Emulator
Scope
Target:
/games/pokemon_pro/game
Generated artifacts:
analysis/topper-emulator/topper_emulator.pyanalysis/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.elfcontains debug/source strings includinguart_node.c,NODEBUS_Poll,NODEUART_Write,NODEUART_WritePoll, andNODEUART_WriteAddress./games/pokemon_pro/gamefunctionFUN_00a21a20constructs addressed node frames and calls lower transport helperFUN_00a4c4c0./games/pokemon_pro/gamefunctionsFUN_0090aff0,FUN_00a23450,FUN_00a26690, andFUN_00a26700implement 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:
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:
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:
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_006a9620gets logical node device10withFUN_007cbc80(10).- It verifies the node is available with
FUN_007cbfb0(10, 1). - It passes the configured node address byte from device entry offset
0x1atoFUN_00a28a00(address, out). FUN_00a28a00sends command0xfcwith expected response length0x10.FUN_006a9620treats the 16 returned bytes as four little-endian 32-bit words and computes:
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_KEYat index0x1a3. Nearby entries are:0x19b:AD_MAGIKARP_MECH_DISABLED0x19c:AD_MAGIKARP_HEAD_HI_LIMIT0x19d:AD_MAGIKARP_HEAD_LOW_LIMIT0x19e:AD_MAGIKARP_TAIL_HI_LIMIT0x19f:AD_MAGIKARP_TAIL_LOW_LIMIT0x1a0:AD_MAGIKARP_WATER_CENTER0x1a1:AD_MAGIKARP_ATTRACT_BEHAVIOR0x1a2:AD_MAGIKARP_ATTRACT_FREQUENCY0x1a3:AD_MAGIKARP_TOPPER_SERIAL_NUMBER_KEY
- The Magikarp runtime functions use constants such as
0xa6..0xaaand0xadwithFUN_007d3330/FUN_007d39c0. Static table dumping showed those are not direct indexes into the publicAD_*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:
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
0xf0as a no-op ACK path. - Config read command
0xf2in 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:
python3 -m py_compile analysis/topper-emulator/topper_emulator.py
Frame self-test:
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 device10resolves to a configured node address at runtime viaFUN_007cbc80(10), device-entry offset0x1a. This value was not recovered as a fixed static literal.- UART ID, pins, baud, electrical level, and bus direction control.
IDENTITY_BOARD_PART_ID: command0xfereturns 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
0xfcsupplies 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
0xffavoids the generic unsigned/problem bit paths.
Low confidence until hardware-tested:
- Physical-layer UART settings and direction handling.
- Correct
IDENTITY_BOARD_PART_IDfor a live Magikarp topper board.