General Info
This wiki aims to document some details about the Pokewalker in a more organised way than Dmitry's original article. Also included will be more information and an expanded scope than the article also.
The wiki is divided into different sections, mostly aimed at people developing things for the Pokewalker.
- The behaviour section details the inner workings of certain modules and sub-modules of the walker.
- The manipulation section details things that can be done to manipulate things on the walker, such as RCE and re-flashing the firmware on the walker.
There are also sections for the EEPROM map, data structures used in the walker as well as all of the IR commands used in comms between the walker and either the DS or another walker.
This wiki aims to be actively updated whenever new discoveries are made for the walker.
Resources
- Dmitry.gr's Pokewalker writeup
- H8/38602R Group Hardware Manual
- H8/39606F Group Addition Notes
- H8/300H Series Software Manual
- H8/300L Series Software Manual
Tools
Pokewalker Hardware
Definitions
Pokemon
POKEMONis just a generic name. Should only be used for sizes, not addresses.CURRENT_POKEMONis the current pokemon on a walk. Unknown if "joined" pokemon are treated the same.EXTRA_POKEMONis the pokemon gifted as an event byCMD_C2or caught on a special route.ROUTE_POKEMONare the three regular route-available pokemon. Stored inroute_info_t.route_pokemon[].GIFTED_POKEMONexclusively refers to the pokemon gifted byCMD_C2.SPECIAL_ROUTE_POKEMONis the extra pokemon on the special route. Stored inspecial_route_info_t.special_pokemon.
Items
ITEMis a generic name, only used to refer touint16_t le_item.DOWSED_ITEMis an item currently in the inventory that was obtained through normal dowsing.EXTRA_ITEMis an item currently in the inventory that was either obtained though aCMD_C2gift or dowsed on a special route.ROUTE_ITEMis an item not currently in the inventory, that is available to be dowsed thorough normal dowsing.GIFTED_ITEMexclusively refers to the item gifted byCMD_C2PEER_PLAY_ITEMis an item received after engaging in peer play.SPECIAL_ROUTE_ITEMis an item not currently in the inventory, that is only available to be dowsed though special route dowsing.
Behaviour
This section outlines the detailed behaviour of the Pokewalker's apps ans subsystems. They may be in a bit of an unorganised state since I have been writing it as I've been reverse engineering the Pokewalker. Some information may be wrong, so take with a grain of salt.
Steps and Watts
This is more "health data" focussed, which contains steps and watts.
Function 0x24ac:process_step_taken
- Compares two bytes, if they're equal then exit function
- Probably how many steps are left to process
- Increment byte
0xf7b4 - If its less than 64, exit function
- Increment
0xf7a4:watts_gained_since_last_sync, clamping to 9999 - Increment 4-byte
0xf79c:current_steps, clamping to 99999 - Increment 4-byte
ram_health_data.total_steps, clamping to 9999999 - Increment
ram_health_data.steps_this_watt - If >= 20, subtract 20 and increment
ram_health_data.current_watts, clamping to 9999 - increment byte
0xf7b3and compare to0xf7b2- If
0xf7b3<=0xf7b2then skip next step
- If
- set
0xf7b3=0xf7b2 - Decrement byte
0xf7b4by 63
Writing ram_health_data to eeprom
- In
add_watts - On receive command
0x20_IDENTITY_DATA_REQ - On receive commands
0x32,0x40,0x52 - On receive command
0x66 - On receive commands
0xc6,0xd6 - On walk start
- On walk end
- On battle lost
- On log event
- On pokemon join route
- On update settings
- Before entering dowsing or radar (to subtract watts)
- On initialising eeprom
Periodic events
Function 0xa34a:regular_processing
Checks if each of the RTC_EVENTS bits are set, and if they are, perform that task.
Only does anything for minute, hour and day.
Minute just increments walk_minute_counter.
Hour and day have separate functions, seen below.
Function 0xa682:rtc_sec interrupt routine
- Sets
current_second - Increments
ram_health_data.last_syncseconds counter - increments
seconds_counter, clamping to 3600 seconds - Decrements
sleep_mode_countdown - Decrements
deep_sleep_countdown - Clears RTC interrupt bit
Function 0xa3aa:perform_every_hour_tasks
- sets bit 2 in
common_bit_flags - checks if total steps < 9999999 (7 9s)
- If not, skip today steps checking
- Checks if
ram_health_data.today_steps< 9999999 (7 9s)- If not, skip increment
- Increment
ram_health_data.today_steps - Write
ram_health_datato eeprom - Check if we have a pokemon (
walker_status_flags.2is set)- If we don't, skip to end, setting watts to 0
- Read
route_infofrom eeprom - Check if we're on a special route
- Log event with type
0x1B_EVENT_TYPE_FELL_ASLEEP - Zero
0xf7a0:current_watts- This is watts gained since last write? Not actual current held watts
- Check if time if at end of day hour in BCD
- If so, set
RTC_EVENTSbit 2 (every day?)
- If so, set
Function 0xa45e:perform_every_day_tasks
- Increment
ram_health_data.total_days, clamping to9999 - Write
ram_health_datato eeprom - Read eeprom
0xcef0:historic_step_count - Shift the step count array, deleting 7th day and making room for today
- Write today's steps at index 0
- Write back to
0xcef0:historic_step_count - Fills 10 slots in
0xde24:MET_PEER_DATAwith 40 bytes of 0xff
Pokewalker Sleep
Device has two sleep modes, "normal" sleep and "deep" sleep.
"Normal" sleep is triggered after 60 seconds of inactivity. "Deep" sleep is triggered after 90 seconds of inactivity.
Walker keeps track of what sleep state its in with bits [3..4] of 0xf7b6:walker_status_flags.
When putting the walker to sleep, the main event loop changes to sleep_mode_event_loop.
On wake-up, it resets these counters, and checks what sleep state its in.
If it's in "deep" sleep mode, it resets the accel sample index to 0 then does the same as normal sleep.
In "normal" sleep mode (and deep sleep) it sets the walker_status_flags to "awake" and tells the LCD to exit power save mode.
On "deep" sleep, it stops TIMERW, clears bit 2 of CKSTPR1, sets walker_status_flags to "deep sleep" and clears bit 0 of RTC_RTCCR2.
On "normal" sleep, it sets bit 2 of CKSTPR1, sets walker status flags to "normal sleep", sets bit 2 of RTC_RTCCR2, sets the "deep sleep" countdown to 30 and clears bit 7 of 0xf7b5:common_bit_flags, likely to indicate that we aren't walking any more.
Function 0x7882:sleep_mode_event_loop
TODO
Walk Start
Procedure
- Normal handshake, DS asserts master
What the walker does
On rx 0x5a:
- runs
start_walk - zeroes 0x6c8 bytes at 0xb800
- go to animation
On rx 0x38:
- runs
start_walk - go to animation
Function start_walk
- Write 0xa5 to eeprom reliable data area copy markers
- (start of function
eeprom_unfinished_copy) - copy 0x2900 bytes from eeprom 0xd700 to 0x8f00
- copy 0x280 bytes ftin eeprom 0xd480 to 0xcc00 (my team data)
- clears copy marker in eeprom reliable data area
- (end of function
eeprom_unfinished_copy) - zero event log (same as walk start)
- zero peer team data, 0x1568 bytes of eeprom at 0xde24
- zero
current watts - zero
health_data.walk_minute_counter - zero
health_data.event_log_idx - zero
health_data.current_watts - write ram cache health data to eeprom
- reads walker_info_t from eeprom
- set walker status flag bits [0..1], clears bit [2]
- copy
unk{0..3}inwalker_infofrompeer_walk_info - copy
walker_info.{tid,sid}frompeer_walk_info - copy
trainer_name,protocol_ver,protocol_subver,unk5frompeer_walk_info - sets
walker_info.unk8to2 - reliable write
walker_info_tto eeprom - sets up walk start log event
- writes walk start log event
- clears obtained items/pokemon/peer items (zeros 0x64 bytes at 0xce8c)
Log
- calls logEvent
- e1 = 0x0000
- [sp] = settings byte u8 and settings byte&0x01 u8
- event type 0x19
- isOnSpecialRoute = settings byte&0x01
Walk End
Procedure
- Normal handshake, DS asserts master.
- DS sends
CMD_IDENTITY_REQ 0x20, no data - Walker sends
CMD_IDENTITY_RSP 0x22, containswalker_info_t (IdentityData) - DS sends
0x40, containswalker_info_t (IdentityData)idk what to do with it - Walker sends
0x42, no data. Strange since this is apparentlyCMD_IDENTITY_SEND_ALIAS1 - DS does a bunch of reads.
- DS ping
- Walker pong
- DS sends
CMD_WALK_END_REQ 0x4e - Walker sends
CMD_WALK_END_RSP 0x50 - End of connection
What the walker does
- End walk action (after 0x4e)
- clear special route bit in
RamCache_settingsByte - clear walker has poke bit in
walker_status_flags - set log write pointer to top of stack
- set current watts to zero
- copy ram health data to reliable
HEALTH_DATA_1/2 - reads
IDENTITY_DATA_1/2 - zeros
walker_info_t.unk1, unk3, flags:[0..1] - writes to
IDENTITY_DATA_1/2 - zeros 0x64 bytes of eeprom at 0xcf0c (obtained items/pokemon/peer items)
- 'zeros' 24 elements, each size 0x88 bytes, in eeprom starting at 0xcf0c (see eventLogInvalidate:0x188c)
- zeros 0x6c8 bytes of eeprom at 0xb800 (event data)
- zeros 0x1568 bytes of eeprom at 0xde24 (met peer data)
- zeros 0x10 bytes of eeprom at 0x8f00 (current pokemon summary)
- clear special route bit in
DS checks
In order for the DS to accept the walk end, the eeprom needs to contain:
- valid
walker_info_t (IdentityData)protocol_ver = 2,protocol_subver=0le_unk1=1,le_unk3=7- correct
unique_identity_data_t
- The correct species in
route_info_t.pomeon_summary.le_species
Walker Initialisation
Checks on startup
- Checks for nintendo string
- if not found:
- run
eeprom_init(true, true) - write nintendo string to eeprom
- check for unfinished copy
- run
- if found:
- Reads
health_data_tfrom eeprom. - Reads
walker_info_tfrom eeprom - sets global init and has_pokemon flags based on
walker_info_t.flags[0..1] - check for unfinished copy
- Reads
- if not found:
- go to sleep mode (??)
- (In sleep mode now)
- process splash normally
- if we aren't inited, go to connect loop
Resetting via IR
Relevant commands are:
| CMD | Description |
|---|---|
| 0xe0 | Clear events and lifetime data |
| 0x2a | Clear events only |
| 0x2c | Don't clear events or lifetime data |
"clear events" means:
- zero
walker_info_t.event_bitmap - zero eeprom
received_bitfield -> text_event_item_name
"clear lifetime data" means:
- Zero
walker_info_t.be_step_count - Zero
health_data_t.total_steps - Zero
health_data_t.total_days health_data_t.last_sync = date2nd Jan 2008- Zero
health_data-t.today_steps - Zero eeprom
caught_pokemon_summary-0x08 -> current_peer_team_data-0x34
All commands call eeprom_init with their respective flags set.
The one that's actually used by the game is 0x2a - only clear event data.
Function eeprom_init
- Zero
current_steps - Zero
current_watts - Clear
status_flagsbits 1 and 2 (walker_inited,walker_has_pokemon) - Reliable read
walker_info_t- Zero
unk0, unk1, unk2, unk3 - Zero tid/sid
- Zero trainer name
- If
clear_eventszeroevent_bitmap_t - Zero flags
- Zero
unk4[2] - Zero
protocol_subver unk8 = 2- Reliable read
unique_identity_data_tintowalker_t.unique_identity_data - if
clear_stepszerobe_step_count - Reliable write
walker_info_t
- Zero
- Clear and write
health_data_t- if
clear_lifetime_data- Zero
total_steps - Zero
total_days last_sync = 2209248002nd Jan 2008- Zero
today_steps
- Zero
- Zero
walk_minute_counter - Zero
current_watts - Zero
steps_this_watt settings = 0x24- Reliable write
health_data_t
- if
- If
clear_stepszero eeprom0xce80 - 0xdbcc(caught_pokemon_summary-8 -> current_peer_team_data-0x34) - Else only zero inventory, event_log, step_log (note: this only skips clearing the staging area (?))
- If
clear_eventszero eeprom0xb800 - 0xbec8(received_bitfield -> text_event_item_name) - Zero met peer data
Function check_and_validate_eeprom
- Check for "nintendo" string at first 8 bytes of eeprom.
- if not found:
- run
eeprom_init(true, true) - reset sound and contrast from ram health_data
- write "nintendo" to start of eeprom
- run
- if found:
- check settings byte and apply a minimum contrast?
- update status flags from eeprom
- if not found:
- Check for unfinished copy by reliable reading both copy markers
- if not ok, continue the copy
Peer Play
IR Sequence
Taken from dmitry's writeup.
- Normal keyex
- Master sends command
0x10containing itsidentity_data_t - Slave checks version compatability and if already played
- What if these checks fail?
- Slave replies with command
0x12containing itsidentity_data_t - Master checks version compatability and if already played
- What if these checks fail? These should pass if slave replies?
- Master sends small animated sprite to slave's
0xf400 - Master sends team data to slave's
0xdc00 - Master reads slave's small animated sprite and writes to
0xf400 - Master reads slave's team data and writes to
0xdc00 - Master sends command
0x14withpeer_play_data_t - Slave writes this to
0xf6c0 - Slave replies with command
0x14and its ownpeer_play_data_t - Master writes this to
0xf6c0 - Master sends command
0x16 - Slave replies with command
0x16 - Both play animation
- Both calculate gift as
seed = max(20000, 10*((remote.watts + self.watts) + (remote.steps + self.steps)) - Checks if there's space in peer play gifts
- If fails, gift
watts = max(99, seed/200) - If passes, gift
item = TBD
- If fails, gift
Animation
- Self sits on middle right side, other walks in over 8 fast frames to middle-left, both animated
- show message "{other} has arrived" for 8 fast frames (2-high), both animated
- Show message "played a bit" with music note static in top middle, 8 fast frames, 2 high, both animated
- Draw large present in middle, 8 px from top, show message "here's a gift", 4 fast frames, 1 high
- Draw large message "{gift} received!", 4 fast frames, 2 high
- Send to splash
Event logs
Notes
- dmitry's struct is 0x5e bytes, missing route_name
walker function sig
r0 = TeamAndrouteInfo in ram ptr (0xBE bytes from 0x8f00) e0 = tmpBufPtr (0x88 bytes big) can be pre-initialized with data r1l = eventTYpe r1h = boolean isOnSpecialEventRoute e1 = extraInfo (eg: item type) [sp] = u8 pushed as u16, availablePokeIdx(1..3 are route availables, 4 is event poke, 0 for N/A)
notes on the decomp of logEvent
- we have a log set up to write (apparently)
- reads written event
- if 0x1b, drop this log
- if 0x19, increment index
idx = (idx+1)%23 - if our event is <0x0a, skip the zero part (this will be peer play events)
- zero 0x88 bytes of our log buf
- (buf+0x84) = our event type (u8)
- (buf+0x0e) = extra data (u16)
- (buf+0x00) = current time (u32, be)
- (buf+0x78) = current watts (u16, be)
- (buf+0x7c) = current steps (u32, be)
- (buf+0x0a) = (route_info) (current species) (u16)
- (buf+0x20) = (route_info+0x10) (current nickname) 22 bytes
- (buf+0x77) = (route_info+0x26) pokemon happiness u8
- (buf+0x85) = (pokemon_flags_1&0x1f) | (buf+0x85)&0xe0, u8
- (buf+0x85):7 = are we on special route, u8
- on normal route: (buf+0x76) = route_image_index u8
- on normal route: (buf+0x4c) = route_name 0x2a bytes (21*u16)
- on special route: (buf+0x76) = special route_image_index (eeprom:0xbf06) (u8)
- on special route: (buf+0x4c) = special route_name 0x2a bytes (eeprom:0xbf50)
- check if we have 1-3:normal_pokemon, 4:special_pokemon, else:no_pokemon (idk)
- probably for caught pokemon
- if we were normal pokemeon: (buf+0x0c) = caught species
- normal pokemon: (buf+0x86) = caught gender/form &0x1f | (buf+0x86)&0xe0 + funky bitfield insert
- event pokemon:
- if log type is 0x0f or 0x10: (buf+0x0c) = (eeprom:bf08) event caught species (u16)
- else: (buf+0x0c) = (eeprom:ba44) gifted pokemon species (u16)
- event pokemon: (buf+0x86) = 1f&(eeprom:bf0d) (should be bf15) special pokemon flags (u8)
- also orred with (buf+0x86)&0xe0 + funky bitfield insert
- all routes: (no pokemon jumps straight here)
- write 0x88 bytes to eeprom at
0xcf0c+0x88*idx - increment global index
idx = (idx+1)%23 - write total steps to reliable area
notes on decomp of logPeerPlay
- Also grabs 0x88 bytes for log item
- (buf+0x04) = peer_play+0x08 u32
- (buf+0x08) = peer_play+0x0c u16
- (buf+0x0c) = peer_play+0x0e u16
- (buf+0x86) = peer_play+0x36 u8 + bitfield inserts again | peer_play:7
- (buf+0x7a) = peer_play+0x04 (peer watts) u16
- (buf+0x80) = peer_play+0x00 (peer steps) u32
- (buf+0x36) = peer_play.nickname 22 bytes (walker copies 24)
- (buf+0x10) = peer_play.trainer_name 16 bytes (walker copies 18)
- pushes
(2*number_of_peers_met)&0xff00u16 to the stack?? - event type is number of peers met so far
- extra data = route_info.items[n_peers_met] u16:w
Radar Bush Game
Walker behaviour
- Choose mon
- check for event
- check if we already have mon
- check steps
- check percent
- exclamation level = 3+rand()%2
- check if we already have mon
- check option a > b > c
- check steps
- check percent (same rand() val)
- exclamation level = 3-idx+rand()%2
- always have at least option c
- check for event
- mark active timer = 5 ticks
- timeout timer = 0x40
- active bush = rand()%4
- substate user choose bush
- dec mark timer
- if mark timer up, dec timeout timer
- on enter, if cursur is on active bush, move to substate handle ok bush
- else move to failed global state
- set message timeout to 10 ticks
- substate radar handle ok bush
- dec message timeout
- if exclamation level == chosen level
- set show message
- reset bar animation frame
- switch to battle start state
- mark active timer = 16+rand()/stuff
- inc exclamation level
- active bush = rand()%4
- switch to choosing state
- substate user failure (I don't know if this ever gets called, on failure we go to separate global state)
- show received message (??)
- wait for input
- send to splash
- substate battle start
- 4 ticks to black out screen
- send to battle state
PokeRadar
Choosing pokemon
- Checks for event pokemon first
- if rand() < chance, choose event mon
- else choose route 3 mon
- get new rand()
- Check for normal pokemon
- start at option A (rarest)
- check for steps
- check if same rand() < chance
- if both pass, choose that option mon
- else continue
- substate_y = index+1 (probably found pokemon id)
- substate_b = randomly add 1 to index+1
- This means that even if it chose eg 0, there's a 50% chance it'll actually be 1 instead
- Same applies for event mon? Skips event check?
Wild Pokemon Join
Notes
Walker stuff
In pokeJoinEmptyWalkerWalk
- set global status has_walking_poke [2]
- read reliable identity data
- if walker_info_t.flags.1 (have pokemon) then break
- set walker_info_t.flags.1
- set walker_info_t.unk0 and walker_info_t.unk2
- set walker_info_t.flags.2 (pokemon joined walk)
- write back to reliable area
- read small image for pokemon option c
- write to current small pokemon sprite
- read large image for pokemon option c
- write to current large pokemon sprite
- read pokemon option c name
- write to current pokemon name
- read route_info_t
- copy route_info_t.route_pokemon[2] to route_info_t.current_pokemon
- clear pokemon_summary_t.flags[7]
- write 0x46 to route_info_t.current_pokemon.happiness
- zero 0x16 bytes of route_info_t.pokemon_nickname
- write route_info_t
- reliable write global health data
- clear event logs (write zero in each entry type)
In addRandomGift
- (skipped some stuff)
- standard log event, with event_type=(0x10+ addRandomGift param)
- should be event type 0x17
Battle
General notes
- Frames are twice as fast.
- Does not show message during animation
- Updates health at the end of each animation
- Opponents sprite is flipped
Catching
- "threw a poke ball" and ball anim
- cloud "anim" (shows cloud for 2 frames)
- wobble anim n times
- outcome
- on lose
- cloud "anim"
- show mon "almost had it"
- fled
- on win
- stars anim ~5 frames
- "was caught"
- on lose
Animations
- wobble has x=20 +/- 1, y=12, 4 frames each
- throw has 6 frames
- always wobbles once (? pretty sure)
- can wobble 3 times and break out
RNG stuff
Seems like there's a percent chance each wobble based on its HP for it to break out. If it reaches 3 wobbles without breaking out, it's caught.
Audio
Random gift
Overview of the process
In sleep mode (and only in sleep mode), calls choose_random_event if device is awake.
choose_random_event does this exact check again before doing anything.
If we're in the splash state and walker_status_flags bit 0 is set then we can have a gift.
There's a 40% chance for an event to happen each time.
If an event is chosen, first check if we have a pokemon.
If we don't have a pokemon and current watts are more than 300, then set substate_y=EVENT_TYPE_JOIN_WALK.
If we do have a pokemon then move on to gifts.
There's something about waiting an hour, not sure about this.
First read route_info from eeprom and find if we have an item slot free.
- If we have a free slow, pokemon happiness > 90 and we have >=500 watts then set
substate_y=EVENT_TYPE_ITEM. - Else if pokemon happiness > 80 and we have >=250 watts then set
susbtate_y=EVENT_TYPE_50_WATTS. - Else if we have >=200 watts then set
substate_y=EVENT_TYPE_20_WATTS. - Else if we have >=100 watts then set
substate_y=EVENT_TYPE_10_WATTS. - Else if we have >=50 watts and
health_data.walk_minute_counter>=60 thensubstate_y=EVENT_TYPE_NO_GIFT. - Else no event
Next in the spash state in normal event mode (and for one(?) cycle of sleep mode), check if substate_z is nonzero.
If it is, it means we have a random event, so we run handle_random_splash_event on button press.
This uses event_type parameter to index into a pointer table random_event_table and sets substate_b.
substate_b now points to an array of data/instructions corresponding to the event type chosen.
The data/instructions just outline whether or not to draw a pokemon sprite, item sprite, which message to display,
what to say we found, which emote/face to display, which sound to play and whether we should go back to the splash screen.
Each "step" is 4 bytes and substate_b moves along 4 bytes at a time until dialogue is over.
Next handle_random_splash_event sets the current substate to 0x0c random_gift_state.
It zeroes the seconds_counter and then proceeds to perform the action the event_type describes.
- If event is for an item, item index is calculated by
max(0,9-watts/500), where 0 is rarest item. Then finds a free slot and adds the item. if there are no free slots then don't add an item. - If event is for watts, call
add_wattsfunction with the right number of watts and writes it tosubstate_z. - Otherwise, zero
substate_zNext write a log event for the random event. Log event index is0x10+EVENT_TYPE, extra info isitem_id(nothing for watts).
If the event type was either for watts or no gift, then set substate_a = (rand()>>3)%3, else zero it.
This is to display a "random" mood message on the screen.
Now in random_gift_state event loop, on button press check if bit 0 of the data pointed to by substate_b is set.
If it is, go to splash screen, else play sound id stored in substate_b[1] and move substate_b to point to next
set of 4 bytes.
Finally, draw the state depending on what flags are set in the substate_b data array.
Use draw_large_message with an index of substate_b[3] (+substate_a if told) to display the "found" and "is happy" messages etc.
Eventually, go back to splash screen once dialogue is done.
Analysis of 0x5d52 handle_random_splash_event
handle_random_splash_event takes parameter event_type in r0l.
interaction_handler_splash zeroes substate_z, keeps substate_y as event_type and passes event_type into function.
- Sets
substate_btorandom_event_table[event_type*2]- related to drawing? - Sets current state to
0x0c(random gift) - Sets
seconds_counterto 0
enum event_type {
// type 0 doesn't exist
EVENT_TYPE_ITEM = 1,
EVENT_TYPE_50_WATTS = 2,
EVENT_TYPE_20_WATTS = 3,
EVENT_TYPE_10_WATTS = 4,
EVENT_TYPE_NO_GIFT = 5, // smiled/is happy etc
// type 6 doesn't exist
EVENT_TYPE_JOIN_WALK = 7,
};
If event type adds watts, just call 0x1f3e add_watts().
If event type was join walk, call 0x5c0a pokemon_join_walk()
If event type was add item then:
item_index= 9 - floor(current_watts/ 500)item_id=route_item_ids[item_index]- if we have slots free add item to slot
- if we don't have slots free, don't bother
common to all event types:
- alloc and read route info from eeprom
- check if we're on a special map
- malloc enough for log struct
- log event
- log type is (16 +
event_type) - say if we're on a special route
- log type is (16 +
- if event type was add watts,
g_substate_a=rand()>>3 % 3- this leads to a "random" message displayed on screen
- else
g_substate_a= 0
Analysis of 0x5c0a pokemon_join_walk()
- clear current watts
- set
have_pokemonflag (bit 2) inwalker_status_flagsram cache - malloc and read in
walker_infofrom eeprom - set flags in
walker_info- set walker inited if it wasn't already
- copy unk0 to unk1
- copy unk2 to unk3
- set have pokemon in flags
- reliable write walker info
- reset and malloc 0x180 bytes for images
- copy option c small sprite to current pokemon small sprite
- copy option c large sprite to current pokemon large sprite
- copy option c pokemon name to current pokemon name
- reset, malloc and read route info
- copy option c pokemon summary to route info
- set bit 7 in pokemon flags (idk what this bit does)
- set pokemon happiness to 70
- copy pokemon nickname to route info
- write route info again
- reliable write ram health data to eeprom
- clear event log
Analysis of 0x5fc2 choose_random_event
checks walker flags, if set on startup and not poke joined then do the following:
- if we aren't in splash state, go to default
- if walker flags bit zero is not set, go to default
- clear walker flags bit 0
- clear substate y, z
- put current watts on stack
- 40% rand check, if failed go to default
- check walker flags, if we dont have pokemon:
- if watts > 300 go to default
- if not, move
7to substate y (probablyEVENT_TYPE_JOIN_WALK) - move
0x30into substate z - exit
- check walker flags, if we do have pokemon
- if seconds counter > 3600, go to default
- reset heap, malloc and read
route_info - put happiness inro
r5l - reset heap, malloc and read 12 bytes for obtained items
- if we have free slot, happiness > 90 and watts > 500, substate_y =
EVENT_TYPE_ITEM - else if no free slot, happiness > 80 and watts > 250, substate_y =
EVENT_TYPE_50_WATTS - else if watts > 200
EVENT_TYPE_20_WATTS - else if watts > 100
EVENT_TYPE_10_WATTS - else if watts > 50 and
ran_health_data.walk_minute_counter> 60EVENT_TYPE_5 - else exit (with y=z=0)
- y is event type, set z to 0x30, then exit
Analysis of 0x5edc draw_random_gift_state
substate_bseems to be a pointer to an array/struct of data, outlined below. 12-bytes widesubstate_zseems to be what was found, depending on the flags insubstate_bsubstate_agets added tosubstate_b[3]to make a "random" large message index
struct random_event_data {
uint8_t flags; // [0]=go to splash, [1]=draw pokemon, [2]=draw item, [3:4]=apply range(?), [5:7]=face index
uint8_t sound_id;
uint8_t draw_id; // fc=draw pokemon name, fd = draw item name, fe = draw watts, ff = do nothing, anything else = use as message index and draw it (never used)
uint8_t message_id; // index into large message id table (see eeprom 0x2530)
}
- reset heap pointer and malloc
0xc0bytes - load
substate_b[0]bit 1- if bit 1 is set, draw pokemon small animated at (32, 4)
- converge, grab
substate_b[0], treat top 3 bits like au3 idx(face index)- if
idx != 7then calldraw_talk_faces(idx)
- if
- converge, grab
substate_b[0]and load bit 2- if set, then call
draw_item_symbol(x=20, y=20)
- if set, then call
- converge, grab
substate_b[2]- if val == 0xfc then draw pokemon name
- if val == 0xfd then draw
substate_zas item name index - if val == 0xfe then draw
substate_zas watts - if val != 0xff then draw large message
substate_zis index
- get first byte of
substate_barray - if
val&0x18 <= 8(ie bit 4 == 0) then- if
substate_b[2] == 0xffthen movesubstate_b[3]intor0hand draw large message with full border, blinking cursor - else move
substate_b[3]intor0hand draw large message with partial border, blinking cursor
- if
- else move
substate_b[3] + substate_aintor0hand draw large message with partial border, blinking cursor
Analysis of 0x5e9e random_gift_state()
Essentially steps through the 12-byte array on each button press. Does this 4 bytes at a time, so there's max 3 message states.
- check if enter key pressed, if not then exit
- load
substate_b[0]bit 0 - if set then call
some_statevar_shuffling(sets y=z=0, sets a) then send to splash - else
substate_b+= 4 (moves 4 bytes across the 12-byte array), then grabsubstate_b[1]- if val != 0x10 then call
play_sound(substate_b[1])
- if val != 0x10 then call
Decoding the event table
- item event
- draw pokemon, face_idx=0, [4:3] = 0b01, sound id 0x10, draw name, message id 0x32 "cheered!"
- draw pokemon, face idx=7, [4:3] = 0b01, sound id 0x10, message id 0x3f "found something"
- draw pokemon, draw item, go to splash, face idx=7, [4:3] = 0b01, sound id 5, draw item, message id 0x18 "found!"
- 50 watts event
- draw pokemon, face idx=1, [4:3] = 0b11, sound id 0x10, draw name, message id 0x33 "is very happy!" (+
substate_a) - draw pokemon, face idx=7, [4:3] = 0b01, sound id 0x10, message id 0x3f "found something"
- draw pokemon, go to splash, face idx=7, [4:3] = 0b01, sound id 5, draw watts, message id 0x18 "found!"
- draw pokemon, face idx=1, [4:3] = 0b11, sound id 0x10, draw name, message id 0x33 "is very happy!" (+
- joined event
- face idx 7, [4:3] = 0b01, sound id 0x10, message id 0x40 "what?"
- go to splash, face idx 6, [4:3] = 0b01, sound id 7, draw name, message id 0x41 "joined!"
- nothing event
- go to splash, face idx 4, draw pokemon, [4:3]=0b11, sound id 0x10, draw name, message id 0x39 "is looking around" (+
substate_a)
- go to splash, face idx 4, draw pokemon, [4:3]=0b11, sound id 0x10, draw name, message id 0x39 "is looking around" (+
Structures
Walker
/*
* size: 0x68 = 104 bytes
* Dmitry: struct IdentityData
*/
typedef struct {
/* +0x00 */ uint32_t le_unk0;
/* +0x04 */ uint32_t le_unk1;
/* +0x08 */ uint16_t le_unk2;
/* +0x0a */ uint16_t le_unk3;
/* +0x0c */ uint16_t le_tid;
/* +0x0e */ uint16_t le_sid;
/* +0x10 */ unique_identity_data_t identity_data;
/* +0x38 */ event_bitmap_t event_bitmap;
/* +0x48 */ uint16_t le_trainer_name[8];
/* +0x58 */ uint8_t unk4[2];
/* +0x5a */ uint8_t event_index;
/* +0x5b */ uint8_t flags; // [0]=init, [1]=has_pokemon, [2]=pokemon_joined, [3..7]=end_of_day_hour
/* +0x5c */ uint8_t protocol_ver;
/* +0x5d */ uint8_t unk5;
/* +0x5e */ uint8_t protocol_subver;
/* +0x5f */ uint8_t unk8;
/* +0x60 */ uint32_t be_last_sync;
/* +0x64 */ uint32_t be_step_count;
} walker_info_t;
/*
* size: 0x18 = 24 bytes
* dmitry: struct HealthData
*/
typedef struct { // strut HealthData
/* +0x00 */ uint32_t be_total_steps;
/* +0x04 */ uint32_t be_today_steps;
/* +0x08 */ uint32_t be_last_sync; // seconds since 1 Jan 2001
/* +0x0c */ uint16_t be_total_days;
/* +0x0e */ uint16_t be_current_watts;
/* +0x10 */ uint16_t le_walk_minute_counter; // only used internally
/* +0x12 */ uint8_t steps_this_watt; // only used internally
/* +0x13 */ uint8_t event_log_write_idx; // only used internally
/* +0x14 */ uint8_t padding[3];
/* +0x17 */ uint8_t settings; // [0]=special_map, [1..2]=volume, [3..6]=contrast
} health_data_t;
/*
* size: 64 bytes
* dmitry: struct LcdConfigCmds
*/
typedef struct {
uint8_t flags;
uint8_t commands[0x3f];
} lcd_config_t;
/*
* size: 256 bytes
* dmitry: RELIABLE_DATA
*
* Warning: gcc will add padding by default
* since members aren't aligned
*/
typedef struct {
/* +0x00 */ uint8_t factory_data[2];
/* +0x02 */ uint8_t factory_data_checksum;
/* +0x03 */ unique_identity_data_t unique_data;
/* +0x2b */ uint8_t unique_data_checksum;
/* +0x2c */ lcd_config_t lcd_config;
/* +0x6c */ uint8_t lcd_config_checksum;
/* +0x6d */ walker_info_t walker_info;
/* +0xd5 */ uint8_t walker_info_checksum;
/* +0xd6 */ health_data_t health_data;
/* +0xee */ uint8_t health_data_checksum;
/* +0xef */ uint8_t copy_marker;
/* +0xf0 */ uint8_t padding[16];
} reliable_data_t;
/*
* size: 1 byte
*
*/
typedef struct {
uint8_t stamp_heart: 1;
uint8_t stamp_space: 1;
uint8_t stamp_diamond: 1;
uint8_t stamp_club: 1;
uint8_t special_map: 1;
uint8_t event_pokemon: 1;
uint8_t evet_item: 1;
uint8_t special_route: 1;
} special_inventory_t;
Notes:
- Walker refuses walk end when walker_info_t.le_unk{1,3} are not {1,7} respectively, stating "not the correct pokewalker"
Pokemon
/*
* size: 0x38 = 56 bytes
* dmitry: struct PeerPlayData
*/
typedef struct { // Dmitry struct PeerPlayData
/* +0x00 */ uint32_t le_current_steps;
/* +0x04 */ uint16_t le_current_watts;
/* +0x06 */ uint8_t padding[2];
/* +0x08 */ uint32_t le_unk0;
/* +0x0c */ uint16_t le_unk2;
/* +0x0e */ uint16_t le_species;
/* +0x10 */ uint16_t pokemon_name[11];
/* +0x26 */ uint16_t trainer_name[8];
/* +0x36 */ uint8_t pokemon_flags_1;
/* +0x37 */ uint8_t pokemon_flags_2;
} peer_play_data_t;
/*
* size: 16 bytes
* dmitry: struct PokemonSummary
*/
typedef struct {
/* +0x00 */ uint16_t le_species;
/* +0x02 */ uint16_t le_held_item;
/* +0x04 */ uint16_t le_moves[4];
/* +0x0c */ uint8_t level;
/* +0x0d */ uint8_t pokemon_flags_1; // [0..5] = variant (spinda, arceus, unown, etc.) [6] = female
/* +0x0e */ uint8_t pokemon_flags_2; // [0] = has form, [1] = shiny
/* +0x0f */ uint8_t padding;
} pokemon_summary_t;
/*
* size: 0x38 = 56 bytes
* dmitry: struct TeamPokeData
*/
typedef struct {
/* +0x00 */ uint16_t le_species;
/* +0x02 */ uint16_t le_held_item;
/* +0x04 */ uint16_t le_moves[4];
/* +0x0c */ uint16_t le_ot_tid;
/* +0x0e */ uint16_t le_ot_sid;
/* +0x10 */ uint32_t le_pid;
/* +0x14 */ uint32_t ivs; // packed to 5-bits each
/* +0x18 */ uint8_t evs[6];
/* +0x1e */ uint8_t pokemon_flags_1;
/* +0x1f */ uint8_t source_game;
/* +0x20 */ uint8_t ability;
/* +0x21 */ uint8_t happiness;
/* +0x22 */ uint8_t level;
/* +0x23 */ uint8_t padding;
/* +0x24 */ uint16_t nickname[10];
} pokemon_info_t;
/*
* size: 0x2c = 44 bytes
* dmitry: struct EventPokeExtraData
*/
typedef struct {
/* +0x00 */ uint32_t le_unk0;
/* +0x04 */ uint16_t le_ot_tid;
/* +0x06 */ uint16_t le_ot_sid;
/* +0x08 */ uint16_t le_unk1;
/* +0x0a */ uint16_t le_location_met;
/* +0x0c */ uint16_t le_unk2;
/* +0x0e */ uint16_t ot_name[8];
/* +0x1e */ uint8_t encounter_type;
/* +0x1f */ uint8_t ability;
/* +0x20 */ uint16_t le_pokeball_item;
/* +0x22 */ uint8_t unk3[10];
} special_pokemon_info_t;
Note: special_pokemon_info_t cannot contain PID because ability, gender, shininess, spinda spots all depend on PID.
So PID must be generated/inferred from the data given.
Unsure if it's random each time, or if the same gifted pokemon gives the same PID.
The le_unk1 and le_unk2 change PV somehow, since I can see nature and characteristics changes.
Nothing seems to grant ribbons, marks or pokerus.
Routes
typedef enum {
/* 0x00 */ ROUTE_IMAGE_FIELD_AND_TREES,
/* 0x01 */ ROUTE_IMAGE_FOREST_AND_TREES,
/* 0x02 */ ROUTE_IMAGE_SUBURBS,
/* 0x03 */ ROUTE_IMAGE_URBAN,
/* 0x04 */ ROUTE_IMAGE_MOUNTAIN,
/* 0x05 */ ROUTE_IMAGE_CAVE,
/* 0x06 */ ROUTE_IMAGE_LAKE,
/* 0x07 */ ROUTE_IMAGE_BEACH,
} route_image_index_t;
/*
* size: 0xbe = 190 bytes
* dmitry: struct RouteInfo
*/
typedef struct {
/* +0x00 */ pokemon_summary_t pokemon_summary;
/* +0x10 */ uint16_t pokemon_nickname[11];
/* +0x26 */ uint8_t pokemon_happiness;
/* +0x27 */ uint8_t route_image_index;
/* +0x28 */ uint16_t route_name[21];
/* +0x52 */ pokemon_summary_t route_pokemon[3];
/* +0x82 */ uint16_t le_route_pokemon_steps[3];
/* +0x88 */ uint8_t route_pokemon_percent[3];
/* +0x8b */ uint8_t padding;
/* +0x8c */ uint16_t le_route_items[10];
/* +0xa0 */ uint16_t le_route_item_steps[10];
/* +0xb4 */ uint8_t route_item_percent[10];
} route_info_t;
/*
* size: 0x6ac = 1708 bytes
* dmitry: struct SpecialRoute
*/
typedef struct {
/* +0x0000 */ uint8_t item_info[6];
/* +0x0006 */ uint8_t route_image_index;
/* +0x0007 */ uint8_t padding_1;
/* +0x0008 */ pokemon_summary_t special_pokemon;
/* +0x0018 */ special_pokemon_info_t special_pokemon_extra;
/* +0x0044 */ uint16_t le_special_pokemon_steps;
/* +0x0046 */ uint8_t special_pokemon_percent;
/* +0x0047 */ uint8_t padding_2;
/* +0x0048 */ uint16_t le_special_item;
/* +0x004a */ uint16_t le_special_item_steps;
/* +0x004c */ uint8_t special_item_percent;
/* +0x004d */ uint8_t padding_3[3];
/* +0x0050 */ uint16_t route_name[21];
/* +0x007a */ uint8_t pokemon_event_number;
/* +0x007b */ uint8_t item_event_number;
/* +0x007c */ uint8_t special_pokemon_sprite_data[0x170]; // should be 0x180 bytes, truncated
/* +0x01ec */ uint8_t special_pokemon_name_image[0x140];
/* +0x032c */ uint8_t special_area_icon[0xc0];
/* +0x03ec */ uint8_t special_area_name_image[0x140];
/* +0x052c */ uint8_t special_item_name_image[0x180];
} special_route_info_t;
Logs
/*
* size: 0x88 = 136 bytes
* dmitry: struct EventLogItem
* Modified from dmitry's version
*/
typedef struct {
/* 0x00 */ uint32_t be_time;
/* 0x04 */ uint32_t le_unk0;
/* 0x08 */ uint16_t le_unk2;
/* 0x0a */ uint16_t le_our_species;
/* 0x0c */ uint16_t le_other_species;
/* 0x0e */ uint16_t le_extra;
/* 0x10 */ uint16_t other_trainer_name[8];
/* 0x20 */ uint16_t our_pokemon_name[11];
/* 0x36 */ uint16_t other_pokemon_name[11];
/* 0x4c */ uint16_t route_name[21];
/* 0x76 */ uint8_t route_image_index;
/* 0x77 */ uint8_t pokemon_friendship;
/* 0x78 */ uint16_t be_our_watts;
/* 0x7a */ uint16_t be_other_watts;
/* 0x7c */ uint32_t be_steps;
/* 0x80 */ uint32_t be_other_steps;
/* 0x84 */ uint8_t event_type;
/* 0x85 */ uint8_t our_pokemon_flags;
/* 0x86 */ uint8_t other_pokemon_flags;
/* 0x87 */ uint8_t padding;
} event_log_item_t;
typedef enum {
/* 0x00 */ EVENT_TYPE_EMPTY_ENTRY,
/* 0x01 */ EVENT_TYPE_PEER_PLAY_1,
/* 0x02 */ EVENT_TYPE_PEER_PLAY_2,
/* 0x03 */ EVENT_TYPE_PEER_PLAY_3,
/* 0x04 */ EVENT_TYPE_PEER_PLAY_4,
/* 0x05 */ EVENT_TYPE_PEER_PLAY_5,
/* 0x06 */ EVENT_TYPE_PEER_PLAY_6,
/* 0x07 */ EVENT_TYPE_PEER_PLAY_7,
/* 0x08 */ EVENT_TYPE_PEER_PLAY_8,
/* 0x09 */ EVENT_TYPE_PEER_PLAY_9,
/* 0x0a */ EVENT_TYPE_PEER_PLAY_10,
/* 0x0b */ EVENT_TYPE_ITEM_DOWSED,
/* 0x0c */ EVENT_TYPE_SPECIAL_ITEM_DOWSED,
/* 0x0d */ EVENT_TYPE_POKEMON_CAUGHT,
/* 0x0e */ EVENT_TYPE_SPECIAL_POKEMON_CAUGHT,
/* 0x0f */ EVENT_TYPE_POKEMON_RAN,
/* 0x10 */ EVENT_TYPE_POKEMON_LOST,
/* 0x11 */ EVENT_TYPE_POKEMON_FOUND_ITEM,
/* 0x12 */ EVENT_TYPE_MOOD_HAPPY,
/* 0x13 */ EVENT_TYPE_MOOD_RUNNING,
/* 0x14 */ EVENT_TYPE_MOOD_LOOKING_AWAY,
/* 0x15 */ EVENT_TYPE_MOOD_BORED,
/* 0x16 */ EVENT_TYPE_MOOD_GO_HOME,
/* 0x17 */ EVENT_TYPE_POKEMON_JOINED,
/* 0x18 */ EVENT_TYPE_WALK_ENDED,
/* 0x19 */ EVENT_TYPE_WALK_STARTED,
/* 0x1a */ EVENT_TYPE_PLAYED_ALOT,
/* 0x1b */ EVENT_TYPE_FELL_ASLEEP,
/* 0x1c */ EVENT_TYPE_ITEM_GIFTED,
} event_log_type_t;
IR
/*
* size: 0x88 = 136 bytes
*/
typedef struct {
/* +0x00 */ uint8_t cmd;
/* +0x01 */ uint8_t extra;
/* +0x02 */ uint16_t le_checksum;
/* +0x04 */ uint32_t le_session_id;
/* +0x08 */ uint8_t payload[128];
} pw_packet_t;
Audio
/*
* size: 0x02 = 2 bytes
*/
typedef struct {
/* +0x00 */ uint8_t info; // ? used in calculating note duration
/* +0x01 */ uint8_t period_idx; // [7] = negative duration?, [6:0] = index into period table
} pw_sound_frame_t;
/*
* size: 0x03 = 3 bytes
*/
typedef struct {
/* 0x00 */ uint16_t offset; // byte offset into SOUND_DATA for start of sound
/* 0x02 */ uint8_t length; // number of bytes this sound effect uses
/* 0x03 */ uint8_t pad;
} pw_sound_info_t;
enum sound_type {
SOUND_NAVIGATE_MENU = 0,
SOUND_NAVIGATE_BACK = 1,
SOUND_CURSOR_MOVE = 2,
SOUND_POKERADAR_FOUND_SOMETHING = 3,
SOUND_SELECTION_MISS = 4,
SOUND_DOWSING_FOUND_ITEM = 5,
SOUND_POKEMON_CAUGHT = 7,
SOUND_POKEMON_ENCOUNTER = 10,
SOUND_MINIGAME_FAIL = 14,
SOUND_POKEBALL_THROW = 15,
}
Notes:
- What does
pw_sound_frame_tstore exactly? what'sinfoand why doesperiod_idxseem to be used to calculate a negative(?) duration? - It would be interesting to see what the missing sounds are. m8 has a wav file, maybe they're mystery gift/ir stuff.
EEPROM Map
This map has been taken from Dmitry's article and has been modified/corrected as more
information has been found.
For things that have been changed since dmitry's article, see the changed column.
| name | addr | size | comments | changed? |
|---|---|---|---|---|
| NINTENDO | 0x0000 | 8 | "nintendo" string as a magic marker. if rom does not find this at boot, it will consider the walker empty and uninitialized | |
| PERSONALISATION | 0x0008 | 8 | some value written during personalization. never read. | |
| 0x0010 | 98 | ??? | ||
| WATCHDOG_RESETS | 0x0072 | 1 | number of watchdog resets | |
| 0x0073 | 13 | ??? | ||
| FACTORY_DATA_1 | 0x0080 | 2 | factory-provided adc calibration data. (reliable data format, copy at 0x0180) | Y |
| UNIQUE_IDENTITY_DATA_1 | 0x0083 | 40 | struct uniqueidentitydata. provisioned at game pairing time (reliable data format, copy at 0x0183) | Y |
| LCD_COMMANDS_1 | 0x00ac | 64 | struct lcdconfigcmds. provisioned at game pairing time (reliable data format, copy at 0x01ac) | Y |
| IDENTITY_DATA_1 | 0x00ed | 104 | struct identitydata. provisioned at walk start time (reliable data format, copy at 0x01ed) | Y |
| HEALTH_DATA_1 | 0x0156 | 24 | struct healthdata. provisioned at walk start time (reliable data format, copy at 0x0256) | Y |
| COPY_MARKER_1 | 0x016f | 1 | struct copymarker. used at walk init time (reliable data format, copy at 0x26f) | Y |
| 0x0170 | 16 | unused | Y | |
| FACTORY_DATA_2 | 0x0180 | 2 | factory-provided adc calibration data. (reliable data format, copy at 0x0080) | Y |
| UNIQUE_IDENTITY_DATA_2 | 0x0183 | 40 | struct uniqueidentitydata. provisioned at game pairing time (reliable data format, copy at 0x0083) | Y |
| LCD_COMMANDS_2 | 0x01ac | 64 | struct lcdconfigcmds. provisioned at game pairing time (reliable data format, copy at 0x00ac) | Y |
| IDENTITY_DATA_2 | 0x01ed | 104 | struct identitydata. provisioned at walk start time (reliable data format, copy at 0x00ed) | Y |
| HEALTH_DATA_2 | 0x0256 | 24 | struct healthdata. provisioned at walk start time (reliable data format, copy at 0x0156) | Y |
| COPY_MARKER_2 | 0x026f | 1 | struct copymarker. used at walk init time (reliable data format, copy at 0x16f) | Y |
| 0x0270 | 16 | unused | Y | |
| IMG_DIGITS | 0x0280 | 416 | numeric character images: "0123456789:-/", 8x16 each, in this order | |
| IMG_WATTS | 0x0420 | 64 | watt symbol image 16x16 | |
| IMG_BALL | 0x0460 | 16 | pokeball 8x8 | |
| IMG_BALL_LIGHT | 0x0470 | 16 | pokeball light grey 8x8 (used for event pokemon) | |
| 0x0480 | 8 | unused | ||
| IMG_ITEM | 0x0488 | 16 | item symbol 8x8 | |
| IMG_ITEM_LIGHT | 0x0498 | 16 | item symbol light grey 8x8 (used for event items) | |
| IMG_MAP_SMALL | 0x04a8 | 16 | tiny map icon 8x8 (used for "special map" reception) | |
| IMG_CARD_SUITS | 0x04b8 | 64 | card faces: heart, spade, diamond, club, 8x8 each (used for "stamp" reception) | |
| IMG_ARROWS | 0x04f8 | 192 | arrows (up down left right), each in 3 configs (normal, offset, inverted) 8x8 each | |
| IMG_MENU_ARROW_LEFT | 0x05b8 | 32 | left arrow for menu 8x16 | |
| IMG_MENU_ARROW_RIGHT | 0x05d8 | 32 | right arrow for menu 8x16 | |
| IMG_MENU_ARROW_RETURN | 0x05f8 | 32 | "return" symbol for menu 8x16 | |
| 0x0618 | 40 | unused | ||
| 0x0638 | 16 | symbol for "have more message" in the bottom right of messages. orred into last 8 columns (thus 16 bytes). applied after 0x0648 | ||
| 0x0648 | 8 | symbol for "have more messages" in the bottom right of messages. each byte here is anded with each col of last 8 in the message. both bitplanes (so you can make it black or keep as is). applied before 0x0638 | ||
| 0x0650 | 16 | medicine vial (?) icon 8x8 | ||
| IMG_LOW_BATTERY | 0x0660 | 16 | low battery icon 8x8 | |
| IMG_TALK_FACES | 0x0670 | 576 | large talk bubbles from bottom right with pokemon feeling icon (exclamation, heart, music note, smile, neutral face, ellipsis) 24x16 each, 6 of them | |
| IMG_TALK_EXCLAMATION | 0x08b0 | 96 | large talk bubble from bottom left with exclamation point in it 24x16 | |
| IMG_MENU_TITLE_POKERADAR | 0x0910 | 320 | "poké radar" menu heading in a box. 80x16 | |
| IMG_MENU_TITLE_DOWSING | 0x0a50 | 320 | "dowsing" menu heading in a box. 80x16 | |
| IMG_MENU_TITLE_CONNECT | 0x0b90 | 320 | "connect" menu heading in a box. 80x16 | |
| IMG_MENU_TITLE_TRAINER_CARD | 0x0cd0 | 320 | "trainer card" menu heading in a box. 80x16 | |
| IMG_MENU_TITLE_INVENTORY | 0x0e10 | 320 | "pokémon & items" menu heading in a box. 80x16 | |
| IMG_MENU_TITLE_SETTINGS | 0x0f50 | 320 | "settings" menu heading in a box. 80x16 | |
| IMG_MENU_ICON_POKERADAR | 0x1090 | 64 | "poke-radar" icon for main menu 16x16 | |
| IMG_MENU_ICON_DOWSING | 0x10d0 | 64 | "dowsing" icon for main menu 16x16 | |
| IMG_MENU_ICON_CONNECT | 0x1110 | 64 | "connect" icon for main menu 16x16 | |
| IMG_MENU_ICON_TRAINER_CARD | 0x1150 | 64 | "trainer card" icon for main menu 16x16 | |
| IMG_MENU_ICON_INVENTORY | 0x1190 | 64 | "pokemon & items" icon for main menu 16x16 | |
| IMG_MENU_ICON_SETTINGS | 0x11d0 | 64 | "settings" icon for main menu 16x16 | |
| IMG_PERSON | 0x1210 | 64 | "person" icon for trainer card screen 16x16 | |
| IMG_TRAINER_NAME | 0x1250 | 320 | trainer's name rendered as an image 80x16 | |
| IMG_ROUTE_SMALL | 0x1390 | 64 | small route image for "trainer card" screen 16x16 | |
| IMG_STEPS_FRAME | 0x13d0 | 160 | "steps" in frame for second screen of trainer card 40x16 | |
| IMG_TIME_FRAME | 0x1470 | 128 | "time" in frame for second screen of trainer card 32x16 | |
| IMG_DAYS_FRAME | 0x14f0 | 160 | "days" in frame for second screen of trainer card 40x16 | |
| IMG_TOTAL_DAYS_FRAME | 0x1590 | 256 | "total days:" in frame for second screen of trainer card 64x16 | |
| IMG_SOUND_FRAME | 0x1690 | 160 | "sound" in frame for preferences screen 40x16 | |
| IMG_SHADE_FRAME | 0x1730 | 160 | "shade" in frame for preferences screen 40x16 | |
| IMG_SPEAKER_OFF | 0x17d0 | 96 | speaker icon with no waves (no sound) for preferences screen 24x16 | |
| IMG_SPEAKER_LOW | 0x1830 | 96 | speaker icon with one wave (low sound) for preferences screen 24x16 | |
| IMG_SPEAKER_HIGH | 0x1890 | 96 | speaker icon with two waves (high sound) for preferences screen 24x16 | |
| IMG_CONTRAST_DEMONSTRATOR | 0x18f0 | 32 | contrast demonstrator (drawn a bunch of times over) 8x16 | |
| IMG_TREASURE_LARGE | 0x1910 | 192 | large treasure chest icon for item view 32x24 | |
| 0x19d0 | 192 | large map scroll thingie 32x24 | ||
| IMG_PRESENT_LARGE | 0x1a90 | 192 | large present icon for item view 32x24 | |
| IMG_DOWSING_BUSH_DARK | 0x1b50 | 64 | small bush dark-colored, for dowsing 16x16 | |
| IMG_DOWSING_BUSH_LIGHT | 0x1b90 | 64 | small bush light-colored, for dowsing 16x16 | |
| TEXT_LEFT | 0x1bd0 | 128 | "left: " string on white background for dowsing. 32x16 | Y |
| 0x1c50 | 96 | blank image 16x24 | ||
| IMG_RADAR_BUSH | 0x1cb0 | 192 | bush dark 32x24 | Y |
| IMG_RADAR_BUBBLE_ONE | 0x1d70 | 64 | word bubble with one exclamation point (for poke hunting) 16x16 | |
| IMG_RADAR_BUBBLE_TWO | 0x1db0 | 64 | word bubble with two exclamation points (for poke hunting) 16x16 | |
| IMG_RADAR_BUBBLE_THREE | 0x1df0 | 64 | word bubble with three exclamation points (for poke hunting) 16x16 | Y |
| IMG_RADAR_CLICK | 0x1e30 | 64 | three lines radiating from bottom left (for bush we just clicked) 16x16 | |
| IMG_RADAR_ATTACK_HIT | 0x1e70 | 128 | skewed small 7-pointed star (attack) 16x32 | |
| IMG_RADAR_CRITICAL_HIT | 0x1ef0 | 128 | skewed large 7-pointed star (critical hit attack) 16x32 | |
| IMG_RADAR_APPEAR_CLOUD | 0x1f70 | 192 | cloud "for pokemon appeared" 32x24 | |
| IMG_RADAR_HP_BLIP | 0x2030 | 16 | "hp" item (4 of these make up an hp bar) 8x8 | |
| IMG_RADAR_CACH_EFFECT | 0x2040 | 16 | a little 5-pointed star image for when we catch something 8x8 | |
| TEXT_RADAR_ACTION | 0x2050 | 768 | "attack/evade/catch" directions placard for battles 96x32 | |
| IMG_POKEWALKER_BIG | 0x2350 | 256 | pokewalker image, blank screen, 32x32 | |
| IMG_IR_ARCS | 0x2450 | 32 | ir xmit icon (like wifi arcs) 8x16 | |
| IMG_MUSIC_NOTE | 0x2470 | 16 | music note icon 8x8 | |
| 0x2480 | 16 | blank icon 8x8 | ||
| IMG_HOURS_FRAME | 0x2490 | 160 | "hours" in a pretty frame. appears unused 40x16 | |
| TEXT_CONNECTING | 0x2530 | 384 | "connecting..." string for comms 96x16 | |
| TEXT_NO_TRAINER | 0x26b0 | 384 | "no trainer found" string for comms 96x16 | Y |
| TEXT_CANNOT_COMPLETE | 0x2830 | 768 | "cannot complete thisconnection" string for comms 96x32 | |
| TEXT_CANNOT_CONNECT | 0x2b30 | 384 | "cannot connect" string for comms 96x16 | |
| TEXT_TRAINER_UNAVAILABLE | 0x2cb0 | 768 | "other trainer isunavailable" s60tring for comms 96x32 | |
| TEXT_ALREADY_RECV_EVENT | 0x2fb0 | 768 | "already received this event" string for comms 96x32 | Y |
| TEXT_CANNOT_CONNECT_AGAIN | 0x32b0 | 768 | "canont connect to trainer again" string for comms 96x32 | Y |
| TEXT_COULD_NOT_RECV | 0x35b0 | 384 | "could not receive..." string for comms 96x32 | Y |
| TEXT_HAS_ARRIVED | 0x38b0 | 384 | "has arrived!" string for comms 96x16 | Y |
| TEXT_HAS_LEFT | 0x3a30 | 384 | "has left." string for comms 96x16 | |
| TEXT_RECV | 0x3bb0 | 384 | "received!" string for comms 96x16 | Y |
| TEXT_COMPLETED | 0x3d30 | 384 | "completed!" string for comms 96x16 | |
| TEXT_SPECIAL_MAP | 0x3eb0 | 384 | "special map" string for comms 96x16 | Y |
| TEXT_STAMP | 0x4030 | 384 | "stamp" string for comms 96x16 | |
| TEXT_SPECIAL_ROUTE | 0x41b0 | 384 | "special route" string for comms 96x16 | Y |
| TEXT_NEED_WATTS | 0x4330 | 384 | "need more watts." string 96x16 | |
| TEXT_NO_POKEMON_HELD | 0x44b0 | 384 | "no pokemon held!" string 96x16 | |
| TEXT_NOTHING_HELD | 0x4630 | 384 | "nothing held!" string 96x16 | |
| TEXT_DISCOVER_ITEM | 0x47b0 | 384 | "discover an item!" string 96x16 | |
| TEXT_FOUND | 0x4930 | 384 | "found!" string 90x16 | |
| TEXT_NOTHING_FOUND | 0x4ab0 | 384 | "nothing found!" string 90x16 | |
| TEXT_ITS_NEAR | 0x4c30 | 384 | "it's near!" string 90x16 | |
| TEXT_FAR_AWAY | 0x4db0 | 384 | "it's far away..." string 90x16 | Y |
| TEXT_FIND_POKEMON | 0x4f30 | 384 | "find a pokemon!" string 90x16 | Y |
| TEXT_FOUND_SOMETHING | 0x50b0 | 384 | "found something!" string 90x16 | |
| TEXT_GOT_AWAY | 0x5230 | 384 | "it got away..." string 90x16 | Y |
| TEXT_APPEARED | 0x53b0 | 384 | "appeared!" string 90x16 | |
| TEXT_WAS_CAUGHT | 0x5530 | 384 | "was caught!" string 90x16 | Y |
| TEXT_FLED | 0x56b0 | 384 | "fled..." string 96x16 | Y |
| TEXT_TOO_STRONG | 0x5830 | 384 | "was too strong." string 96x16 | |
| TEXT_ATTACKED | 0x59b0 | 384 | "attached!" string 96x16 | |
| TEXT_EVADED | 0x5b30 | 384 | "evaded!" string 96x16 | |
| TEXT_CRITICAL_HIT | 0x5cb0 | 384 | "a critical hit!" string 96x16 | |
| TEXT_SPACES | 0x5e30 | 384 | " " (yes a bunch of spaces) string 96x16 | |
| TEXT_THREW_BALL | 0x5fb0 | 384 | "threw a poke ball." string 96x16 | Y |
| TEXT_ALMOST_HAD | 0x6130 | 384 | "almost had it!" string 96x16 | |
| TEXT_STARE_DOWN | 0x62b0 | 384 | "stare down!" string 96x16 | |
| TEXT_LOST | 0x6430 | 384 | "lost!" string 96x16 | |
| TEXT_PEER_HAS_ARRIVED | 0x65b0 | 384 | "has arrived" (for walker to walker) string 96x16 | Y |
| TEXT_HAD_ADVENTURES | 0x6730 | 384 | "had adventures!" string 96x16 | |
| TEXT_PLAY_BATTLED | 0x68b0 | 384 | "play-battled." string 96x16 | |
| TEXT_WENT_RUN | 0x6a30 | 384 | "went for a run." string 96x16 | |
| TEXT_WENT_WALK | 0x6bb0 | 384 | "went for a walk." string 96x16 | |
| TEXT_RECV_GIFT | 0x6d30 | 384 | "played a bit." string 96x16 | |
| TEXT_RECV_GIFT | 0x6eb0 | 384 | "here's a gift..." string 96x16 | Y |
| TEXT_CHEERED | 0x7030 | 384 | "cheered!" string 96x16 | |
| TEXT_VERY_HAPPY | 0x71b0 | 384 | "is very happy!" string 96x16 | |
| TEXT_FUN | 0x7330 | 384 | "is having fun!" string 96x16 | |
| TEXT_FEEL_GOOD | 0x74b0 | 384 | "is feeling good!" string 96x16 | |
| TEXT_HAPPY | 0x7630 | 384 | "is happy." string 96x16 | |
| TEXT_SMILING | 0x77b0 | 384 | "is smiling." string 96x16 | |
| TEXT_CHEERFUL | 0x7930 | 384 | "is cheerful." string 96x16 | |
| TEXT_PATIENT | 0x7ab0 | 384 | "is being patient." string 96x16 | |
| TEXT_SITTING | 0x7c30 | 384 | "sits quietly." string 96x16 | |
| TEXT_TURN_LOOK | 0x7db0 | 384 | "turned to look." string 96x16 | |
| TEXT_LOOKING_AROUND | 0x7f30 | 384 | "is looking around." string 96x16 | Y |
| TEXT_LOOKING_HERE | 0x80b0 | 384 | "is looking this way." string 96x16 | |
| TEXT_DAYDREAMING | 0x8230 | 384 | "is daydreaming." string 96x16 | |
| TEXT_INTERACT_FOUND_SOMETHING | 0x83b0 | 384 | "found something." string 96x16 | |
| TEXT_WHAT | 0x8530 | 384 | "what?" string 96x16 | |
| TEXT_JOINED | 0x86b0 | 384 | "joined you!" string 96x16 | |
| TEXT_REWARD | 0x8830 | 384 | "reward" string 96x16 | |
| TEXT_GOOD_JOB | 0x89b0 | 384 | "good job!" string 96x16 | |
| TEXT_SWITCH | 0x8b30 | 320 | "switch?" string 80x16 | |
| 0x8c70 | 64 | ??? | ||
| SOUND_OFFSET | 0x8cb0 | 64 | offsets into SOUND_DATA that contain the sounds data, array of pw_sound_info_t[16] | Y |
| SOUND_DATA | 0x8cf0 | 528 | sound data, array of pw_sound_frame_t[264] | Y |
| ROUTE_INFO | 0x8f00 | 190 | struct routeinfo - current route data | |
| IMG_ROUTE_LARGE | 0x8fbe | 192 | current "area" we are strolling in graphic 32x24 | |
| TEXT_ROUTE_NAME | 0x907e | 320 | current "area" we are strolling in textual name 80x16 | |
| IMG_POKEMON_SMALL_ANIMATED | 0x91be | 384 | current pokemon animated sprite for "held items and pokemon" screen, fights, etc. 32 x 24 x 2 frames | |
| IMG_POKEMON_LARGE_ANIMATED | 0x933e | 1536 | current pokemon large nimated sprite for main screen 64 x 48 x 2 frames | |
| TEXT_POKEMON_NAME | 0x993e | 320 | cur pokemon name image 80x16 | |
| IMG_ROUTE_POKEMON_SMALL_ANIMATED | 0x9a7e | 1152 | route available pokemon selected by the same animated small sprites 32 x 24 x 2 frames x 3 pokemon | |
| IMG_ROUTE_POKEMON_LARGE_ANIMATED | 0x9efe | 1536 | large animated image (like at 0x933e) but of the third (option c) available pokemon on this route. used for "joined your walk" situation 64 x 48 x 2 frame | |
| TEXT_POKEMON_NAMES | 0xa4fe | 960 | available pokemon name images 80x16 x3 pokemon | |
| TEXT_ITEM_NAMES | 0xa8be | 3840 | available item names as images. 96x16 x 10 images (one per item) | Y |
| 0xb7be | 66 | ??? | ||
| RECEIVED_BITFIELD | 0xb800 | 1 | bitfield of special things received. 0x01 - "heart" stamp received, 0x02 - "spade" stamp received, 0x04 - "diamond" stamp received, 0x08 - "club" stamp received, 0x10 - "special map" received, 0x20 - walker contains an event pokemon (gifted or caught), 0x40 - walker contains event item (gifted or dowsed), 0x80 - walker has received a "special route" | |
| 0xb801 | 3 | unused | ||
| 0xb804 | 576 | data for "special map received". format unknown. possibly used by the ds games, but no evidence of this found in the games. | ||
| EVENT_POKEMON_BASIC_DATA | 0xba44 | 16 | gifted event poke, or radar-caught event poke. Basic data struct PokemonSummary | Y |
| EVENT_POKEMON_EXTRA_DATA | 0xba54 | 44 | extra data. struct eventpokeextradata | |
| IMG_EVENT_POKEMON_SMALL_ANIMATED | 0xba80 | 384 | small sprite 32 x 24 x 2 frames | |
| TEXT_EVENT_POKEMON_NAME | 0xbc00 | 320 | name image 80x16 | |
| EVENT_ITEM | 0xbd40 | 8 | gifted event item, or dowsed event item. item data. 6 bytes of zeroes, then u16 item, LE | Y |
| TEXT_EVENT_ITEM_NAME | 0xbd48 | 384 | item name image 96x16 | |
| 0xbec8 | 56 | unused | ||
| SPECIAL_ROUTE_STRUCT | 0xbf00 | 3260 | "special route" info (struct specialroute): | Y |
| 0xbf00 | 6 | 6 bytes of zeroes (part of item struct but unused by DS or walker) | Y | |
| ROUTE_IMAGE_IDXNAME | 0xbf06 | 1 | enum routeimageidx | |
| 0xbf07 | 1 | unused | ||
| SPECIAL_POKEMON_BASIC_DATA | 0xbf08 | 16 | special route-available pokemon basic data. struct pokemonsummary | |
| SPECIAL_POKEMON_EXTRA_DATA | 0xbf18 | 44 | special route-available pokemon extra data. struct eventpokeextradata | |
| SPECIAL_POKEMON_STEPS_REQUIRED | 0xbf44 | 2 | min steps to encounter this poke on the route. u16 le | |
| SPECIAL_POKEMON_PERCENT_CHANCE | 0xbf46 | 1 | percent chance to encounter this poke on route after step minimum met | |
| 0xbf47 | 1 | unused | ||
| SPECIAL_ITEM | 0xbf48 | 2 | special route-available item. u16 le | |
| SPECIAL_ITEM_STEPS_REQUIRED | 0xbf4a | 2 | min steps dowse this item. u16 le | |
| SPECIAL_ITEM_PERCENT_CHANCE | 0xbf4c | 1 | percent chance to dowse this item on route after step minimum met | |
| 0xbf4d | 3 | unused | ||
| SPECIAL_ROUTE_NAME_NINTENDOENC | 0xbf50 | 42 | routename u16[21] | |
| SPECIAL_POKEMON_EVENT_INDEX | 0xbf7a | 1 | "event index" for catching this route's special pokemon (1-127) | Y |
| SPECIAL_ITEM_EVENT_INDEX | 0xbf7b | 1 | "event index" for dowsing this route's special item (1-127) | Y |
| IMG_SPECIAL_POKEMON_SMALL_ANIMATED | 0xbf7c | 1920 | special route pokemon animates small sprite. 32 x 24 x 2 frames. should be 0x180 bytes big, but it 0x170. no idea why but confirmed | |
| TEXT_SPECIAL_POKEMON_NAME | 0xc6fc | 320 | special route pokemon name image 80x16 | |
| IMG_SPECIAL_ROUTE_IMAGE | 0xc83c | 192 | special routes's large image for home screen, like 0x8fbe is for a normal route 32x24 | |
| TEXT_SPECIAL_ROUTE_NAME_SMALL | 0xc8fc | 320 | special routes's textual name 80x16 | |
| TEXT_SPECIAL_ROUTE_NAME | 0xca3c | 384 | special route item textual name 96x16 | |
| 0xcbbc | 68 | unused | ||
| TEAM_DATA_STRUCT | 0xcc00 | 548 | struct teamdata on our whole team, so that any walkers we peer play with transfer it to their ds game and we can be battled in the trainer house | |
| 0xce24 | 92 | also written at walk start time as part of the above. probably just to keep the write a multiple of 0x80 bytes | ||
| 0xce80 | 8 | unused | ||
| 0xce88 | 1 | if low bit set, game will give player a starf berry once per savefile. used when 99999 steps reached | ||
| 0xce89 | 1 | unused | ||
| 0xce8a | 2 | current watts written to eeprom by cmd 0x20 before replying (likely so remote can read them directly). u16 be | ||
| CAUGHT_POKEMON_SUMMARY | 0xce8c | 48 | 3x route-available pokemon we've caught so far. 3x struct pokemonsummary | |
| OBTAINED_ITEMS | 0xcebc | 12 | 3x route-available items we've dowsed so far. 3x {u16 le item, u16 le unused} | |
| PEER_PLAY_ITEMS | 0xcec8 | 40 | 10x route-available items we've been gifted by peer play. 10x {u16 le item, u16 le unused} | Y |
| HISTORIC_STEP_COUNT | 0xcef0 | 28 | historic step count per day. u32 each, be, [0] is yesterday, [1] is day before, etc... | |
| EVENT_LOG | 0xcf0c | 3264 | event log. circularly-written, displayed in time order. 24x struct eventlogitem | Y |
| TEAM_DATA_STAGING | 0xd480 | 640 | team data written here before walk start action. struct teamdata | |
| STAGING_AREA | 0xd700 | 10496 | scenario data written here before walk start action. everything that 0x8F00-0xB7FF would have | Y |
| CURRENT_PEER_TEAM_DATA | 0xdc00 | 548 | current peer play peer. struct teamdata. uploaded as part of peer play. later shifted to index [0] at 0xde24 list of peers | |
| MET_PEER_DATA | 0xde24 | 5480 | peers we've met. for battle house info. newest element is first. 10x struct teamdata | |
| 0xf38c | 116 | unused | ||
| PEER_DATA_TEMP | 0xf400 | 760 | peer play temporary data about peer | Y |
| IMG_CURRENT_PEER_POKEMON_ANIMATED_SMALL | 0xf400 | 384 | medium pokemon animated image of pokemon we are peer-playing with (never erased) 32x24 x 2 frames | Y |
| TEXT_CURRENT_PEER_POKEMON_NAME | 0xf580 | 320 | rendered text name of pokemon we are peer-playing with 80x16 | |
| CURRENT_PEER_DATA | 0xf6c0 | 56 | data. struct peerplaydata |
IR Commands
Taken and modified from Dmitry.gr's writeup
| CMD | DIR | NOTES |
|---|---|---|
| 00 | g2w | Compressed EEPROM write. Documented at length above. Writes 128 bytes on a 128-byte boundary. "extra" byte is top 8 bits of address. Bottom 8 bits will always be zero. Reply will be CMD_04 |
| 02 | w2w, g2w | Direct EEPROM write. Documented at length above. Writes 128 bytes on a 128-byte boundary. "extra" byte is top 8 bits of address. Bottom 8 bits will always be zero. Reply will be CMD_04 |
| 04 | w2w, w2g | ACK for an EEPROM write (CMD_00, CMD_02, CMD_0A, CMD_80, CMD_82) |
| 06 | ?2w | Direct internal memory write. "extra" byte is top 8 bits of address. First byte of payload is low byte of address. The rest of the payload will be written to internal memory at that address. Yes, this means RAM, MMIO, etc. Reply will be CMD_06 |
| 0A | ?2w | Direct EEPROM write, random length. "extra" byte is top 8 bits of address. First byte of payload is low byte of address. The rest of the payload will be written to EEPROM at that address. Reply will be CMD_04 |
| 0C | w2w, g2w | EEPROM read. Payload is a 16-bit start address, big-endian, followed by a one-byte length. Reply will be CMD_0E |
| 0E | w2w, w2g | EEPROM read reply. Payload is the data requested by CMD_0C |
| 10 | w2w | Sent by the master walker in a peer-play scenario at start. Enclosed is 0x68 bytes of data - the "IDENTITY DATA". The expected reply from the peer is CMD_12 |
| 12 | w2w | Sent by the slave walker in a peer-play scenario in reply to CMD_10. Enclosed is 0x68 bytes of data - the "IDENTITY DATA". This data is explained later. The "master" will proceed to exchange needed data, and then will send CMD_14 |
| 14 | w2w | Sent by the master walker in a peer-play scenario after all the needed data has been exchanged. Enclosed is 0x34 bytes of data - the "PEER PLAY DATA", "extra" byte byte will be 1. Slave walker replies with CMD_14 as well, same format, "extra" byte byte will be 2. Master will then send CMD_16 |
| 16 | w2w | Sent by the master walker in a peer-play scenario. No data. Reply is also a CMD_16. Does the entire peer-play UI and awards gift item. |
| 1C | w2w | Sent by the either walker during peer play when it's detected that it's "played" with this peer too recently. Shows the "Cannot connect to trainer again" error. No data. |
| 20 | g2w | Requests walker's "IDENTITY DATA". No data attached. Walker is expected to reply with CMD_22 |
| 22 | w2g | Data is "IDENTITY DATA", 0x68 bytes |
| 24 | ?? | Ping request. No operation other than reply with CMD_26. |
| 26 | ?? | Ping reply. Sent in reply to CMD_24 |
| 2A | g2w, w2g | Data is "UNIQUE ID DATA", used to set RTC. Erases the walker. Differs somehow from CMD_2C. Reply is CMD_2A, 0x28 bytes, "UNIQUE ID DATA" which is now all 0xFF. This command is actually used by the game to reset the walker |
| 2C | ?2w | Data is "UNIQUE ID DATA", used to set RTC. Erases the walker. Differs somehow from CMD_2A. Reply is CMD_2C, 0x28 bytes, "UNIQUE ID DATA" whic is now all 0xFF |
| 32 | ?2w | Data is "IDENTITY DATA" that master sends to the slave. From it only RTC is used, if a flag is set. Reply from the walker will be CMD_34 |
| 34 | w2? | Reply to CMD_32, no data |
| 36 | ?2w | No data, no reply. Shows "Cannot complete this connection" error |
| 38 | ?2w | Performs the "walk start" action (same as what CMA_5A does), but without the UI. Does not erase "special event" status, unlike CMD_5A which does. See CMD_5A for details. |
| 40 | g2w | Same as CMD_32. Data is "IDENTITY DATA" that master sends to the slave. From it only RTC is used, if a flag is set. Reply from the walker will be CMD_42 |
| 42 | w2g | Reply to CMD_40, no data |
| 44 | w2g | No data, no reply. Show "cannot complete this connection" |
| 4E | g2w | No data. Reply is CMD_50. Performs "walk end" action, erases pokemon state, shows "walk end" UI |
| 50 | w2g | Reply to CMD_4E, no data |
| 52 | ?2w | Same as CMD_32. Data is "IDENTITY DATA" that master sends to the slave. From it only RTC is used, if a flag is set. Reply from the walker will be CMD_54 |
| 54 | w2? | Reply to CMD_52, no data |
| 56 | ?2w | No data, no reply. Shows "Cannot complete this connection" error |
| 5A | g2w | No data. Reply is CMD_5A. Performs "walk start" action, including copying data to proper places, shows the UI |
| 60 | ?2w | Same as CMD_32. Data is "IDENTITY DATA" that master sends to the slave. From it only RTC is used, if a flag is set. Reply from the walker will be CMD_62. Also reload our walker info from eeprom. |
| 62 | w2? | Reply to CMD_60, no data |
| 64 | ?2w | No data, no reply. Shows "Cannot complete this connection" error |
| 66 | ?2w | No data. Set the message to show at connection end to be "Completed". Reply is CMD_68 |
| 68 | w2? | No data. Reply to CMD_66 |
| 80 | g2w | Compressed EEPROM write. Documented at length above. Writes 128 bytes on a 128-byte boundary. "extra" byte is top 8 bits of address. Bottom 8 bits will always be 0x80. Reply will be CMD_04 |
| 82 | w2w, g2w | Direct EEPROM write. Documented at length above. Writes 128 bytes on a 128-byte boundary. "extra" byte is top 8 bits of address. Bottom 8 bits will always be 0x80. Reply will be CMD_04 |
| 9C | ?2w, w2? | No data. Show "Could not receive..." error. Reply is CMD_9C |
| 9E | ?2w, w2? | No data. Show "Could not receive..." error. Reply is CMD_9E. Sent by walker in reply to CMD_A0, CMD_A2, CMD_A4, CMD_A6, CMD_A8, CMD_AA, CMD_AC, CMD_AE |
| A0 A2 A4 A6 A8 AA AC AE | ?2w, w2? | Use byte 0x5A of the most recently received "IDENTITY DATA" as event index. Check if we've participated. If so, reply with an empty CMD_9E, if not, reply with the same CMD as we received, 17 bytes. First 16 will be "EVENT BITMAP", last will be the event index we just checked for. |
| B8 BA BC BE | ?2w | No data. Award the user a stamp. In order they are: heart, spade, diamond, club. Replies are CMD_C8, CMD_CA, CMD_CC, CMD_CE respectively |
| C0 | ?2w, w2? | No data. Award the user a "special map" This seems to do nothing but show an icon in the UI, and show the "Special map received" message. Reply is also an empty CMD_C0 |
| C2 | ?2w, w2? | No data. Award the user an "event pokemon". Details later. Reply is also an empty CMD_C2 |
| C4 | ?2w, w2? | No data. Award the user an "event item". Details later. Reply is also an empty CMD_C4 |
| C6 | ?2w, w2? | No data. Move the user to an "event route". Details later. Reply is also an empty CMD_C6 |
| C8 CA CC CE | w2? | Replies to CMD_B8, CMD_BA, CMD_BC, CMD_BE respectively. No data. |
| D0 | ?2w | No data. Same as CMD_C0, except also grant the user all 4 stamps |
| D2 | ?2w | No data. Same as CMD_C2, except also grant the user all 4 stamps |
| D4 | ?2w | No data. Same as CMD_C4, except also grant the user all 4 stamps |
| D6 | ?2w | No data. Same as CMD_C6, except also grant the user all 4 stamps |
| D8 | ?2w | No data. Show "Cannot complete this connection" error. No reply. |
| F0 | ?2w, w2? | Not yet fully understood. Data is 0x71 bytes of "ENROLL DATA" Some sort of enroll action. The only path to code that writes te EEPROM's "UNIQUE ID DATA" and "LCD INIT DATA". Reply is also a CMD_F0, with 0x28 bytes of data - a copy of the "UNIQUE ID DATA" |
| F4 | ?2w | Immediate disconnect. No reply. |
| F8 | w2g, w2w | Part of connection initiation as explained above. No data. Sent by slave walker to master walker or game after getting CMD_FA |
| FA | g2w, w2w | Part of connection initiation as explained above. No data. Sent by game or master walker to slave walker after seeing an advertising byte 0xFC on IR |
| FE | ?2w, w2? | Not yet fully understood. Writes the 8 data bytes to EEPROM at offset 8. Never seen in the wild. Reply is an empty CMD_FE |
Manipulating the Pokewalker
Programming the Pokewalker
Useful resources
- E8a Emulator
- E8a Emulator Users Manual
- Renesas Emulator Comparison
- Notes on Connecting the H8/38606F
- Renesas Flash Development Toolkit Available MCUs (page 13)
- H8/38602R Hardware Manual (See Section 6.4 - Flash Memory Programming and Erasure)
The E8a Emulator uses NMIB, Test, E7_0, E7_1, E7_2, RESB pins to debug and program.
H8/38602R Group Hardware Manual Section 6.3.1 on boot mode programming: Allows programming over the UART. MCU sends one byte of 0x00 to indicate it's ready to rx data. Host then sends 0x55 as an ack. Host sends a programming control program to the MCU addr (0xFB80-0xFF7F on a H8/38602R), control then goes to this program. Serial comms are still up but general regs and SP need to be initialised by program. To exit, hold reset, set NMI, release reset. Test and NMI need to be constant during the operation.
For a flowchart, see page 107 (PDF page 141).