Zoinkity

Xplorer64 Info

Mar 10th, 2025 (edited)
71
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.73 KB | None | 0 0
  1. Xplorer64 is similar to the GameShark/Action Replay line. By all appearances the firmware was written by 'sceners and, frankly, is a bit buggy. In other respects it is less invasive than the GS/AR (it undoes many of the changes it makes to hook into the exception handler) and somewhat more convenient for end-users (simpler method of booting non-6101/7102 CICs).
  2. For lack of any real documentation, here's some info gleaned from a partial disassembly of the more interesting parts of the device.
  3.  
  4. .: Codelists :.
  5.  
  6. There are three modes of operation, each with its own code limit. This isn't a total count of codes but instead allocated space for the ASM they generate. If 8MB of memory are present the default is to always run from the upper 4MB with a maximum* of 170 codes.
  7.  
  8. How many opcodes each code generates is listed in the "Normal Xplorer Code Types" section below.
  9.  
  10. Mode 0) opcode limit: 78 - 2 opcodes, so 25 normal codes
  11. Mode 1) opcode limit: 38 - 2 opcodes, so 12 normal codes
  12. Mode 2) opcode limit: 514 - 2 opcodes, so 170 normal codes.
  13.  
  14. This leads to the first bug. Two opcodes are needed for the return from the codelist but these aren't reserved ahead of time. When generating ASM, the list automatically cuts off if the end of the allocation is reached or exceeded. That means it's both possible to overflow the normal end and to overflow when the terminating jump is added.
  15.  
  16. Despite these limits it will parse up to 512 codes. That's more than the device holds, but whatever.
  17.  
  18.  
  19. .: Special Code Types :.
  20.  
  21. These codetypes are applied before the game is executed, used to circumvent protections or manipulate how the hook is generated.
  22.  
  23. Only a single 2* code type may be used. This is applied once, before the game is booted. Its most common use is to eliminate an opcode during initialization that removes the Watch.
  24.  
  25. 21.aaaaaa 00dd write byte [d]ata
  26. 22.aaaaaa dddd write halfword [d]ata
  27. 24.aaaaaa dddd write unsigned HW [d]ata as a word; upper halfword is always zero.
  28. 29.aaaaaa 00dd write byte [d]ata to an uncached address (A0000000 instead of 80000000)
  29. 2A.aaaaaa dddd write halfword [d]ata to an uncached address
  30. 2C.aaaaaa dddd write unsigned HW [d]ata as a word to an uncached address
  31.  
  32. These two special codes affect how the hook is set.
  33.  
  34. 3FFFFF.FE dddd
  35. Shifts the codelist return jump. The value [d]ata is ORed to the base jump instruction to create a new pointer. Depending on mode, the base jump is either 80000000 (08000000) or 80750000 (081D4000). To set [d]ata, take your target pointer, subtract the base, then shift right 2.
  36. Note that because the value is ORed it isn't possible to set pointers in the range 80760000 to 80770000.
  37.  
  38. 3FFFFF.FF cccc
  39. Allow Watch to trigger [c]ount times before removing the Watch. If set to zero, defaults back to 1.
  40. This is useful for 64DD expansion games where the disk will "false reset", causing the exception handler to be written again.
  41.  
  42.  
  43. .: Normal Xplorer Code Types :.
  44.  
  45. Any GS/AR style 8* or D* types are converted into Xplorer format before generating ASM for them.
  46. It should be noted that at some point the format changed from the bitflag method used in 2* codes to what appears below. Masks for the relevent flags are still present but values ignored.
  47.  
  48. It doesn't fix unaligned addresses like a GS/AR will, and a HW write to an odd address (as in not even) may crash the device. Likewise, word write addresses should be multiples of 4.
  49.  
  50. Unlike the GS/AR loop types are actual loops, so as long as you write at least four codes with them you'll show some kind of gain (11 opcodes per loop vs 3 for a normal code). Any less and you might as well expand the codes by hand.
  51.  
  52. 00.aaaaaa dddd store unsigned HW as word
  53. 3 opcodes
  54. Writes unsigned HW [d]ata to the word at [a]ddress. The upper two bytes will always be zero as a result.
  55. 3C1A**** LUI K0,{a}
  56. 341B00** ORI K1,R0,{d}
  57. AF5B**** SW K1,{a} (K0)
  58.  
  59. 30.aaaaaa 00dd store byte
  60. 3 opcodes
  61. Writes byte [d]ata to [a]ddress.
  62. 3C1A**** LUI K0,{a}
  63. 341B00** ORI K1,R0,{d}
  64. A35B**** SB K1,{a} (K0)
  65.  
  66. 50.cccc.AA DDDD 0f.aaaaaa dddd loop type 1
  67. 11 opcodes
  68. Writes [c]ount codes, incrementing the [a]ddress by [A] and [d]ata by [D]. This is an actual loop, only 11 opcodes long.
  69. If [f] is 0 writes bytes, if [f] is 1 writes HWs.
  70. 4081F000 MTC0 AT,ErrorPC (just storing the register safely)
  71. 3C1A**** LUI K0,{a}
  72. 275A**** ADDIU K0,K0,{a}
  73. 341B**** ORI K1,R0,{d}
  74. 3401**** ORI AT,R0,{c}
  75. A35B0000 / A75B0000 SB/SH K1,0000 (K0) (depending on [f])
  76. 277B**** ADDIU K1,K1,{D}
  77. 275A00** ADDIU K0,K0,{A}
  78. 1420FFFC BNE AT,R0,-4
  79. 2421FFFF ADDIU AT,AT,FFFF
  80. 4001F000 MFC0 AT,ErrorPC (restoring the register)
  81.  
  82. 70.aaaaaa dddd unsigned HW equal
  83. 5 opcodes
  84. Next code is run if HW at [a]ddress matches unsigned HW [d]ata.
  85. * Bugged
  86. The value at the address is loaded as a signed value but tested against an unsigned value. A test for a negative value will always be False!
  87. 3C1A**** LUI K0,{a}
  88. 875A**** LH K0,{a} (K0)
  89. 341B**** ORI K1,R0,{d}
  90. 175B**** BNE K0,K1,{len next code}
  91. 00000000 NOP
  92.  
  93. 80.aaaaaa dddd store HW
  94. 3 opcodes
  95. Writes HW [d]ata to [a]ddress.
  96. 3C1A**** LUI K0,{a}
  97. 341B**** ORI K1,R0,{d}
  98. A75B**** SH K1,{a} (K0)
  99.  
  100. 90.aaaaaa dddd unsigned HW not equal
  101. 5 opcodes
  102. Next code is run if HW at [a]ddress does not match unsigned HW [d]ata.
  103. * Bugged
  104. The value at the address is loaded as a signed value but tested against an unsigned value. A test for a negative value will always be False!
  105. 3C1A**** LUI K0,{a}
  106. 875A**** LH K0,{a} (K0)
  107. 341B**** ORI K1,R0,{d}
  108. 135B**** BEQ K0,K1,{len next code}
  109. 00000000 NOP
  110.  
  111. B.f.cc.AAAA DDDD 00.aaaaaa dddd loop type 2
  112. 11 opcodes
  113. Writes [c]ount codes, incrementing the [a]ddress by [A] and [d]ata by [D].
  114. If [f] is 0 writes bytes, if [f] is 1 writes HWs.
  115. 4081F000 MTC0 AT,ErrorPC (just storing the register safely)
  116. 3C1A**** LUI K0,{a}
  117. 275A**** ADDIU K0,K0,{a}
  118. 341B**** ORI K1,R0,{d}
  119. 340100** ORI AT,R0,{c}
  120. A35B0000 / A75B0000 SB/SH K1,0000 (K0) (depending on [f])
  121. 277B**** ADDIU K1,K1,{D}
  122. 275A**** ADDIU K0,K0,{A}
  123. 1420FFFC BNE AT,R0,-4
  124. 2421FFFF ADDIU AT,AT,FFFF
  125. 4001F000 MFC0 AT,ErrorPC (restoring the register)
  126.  
  127. D0.aaaaaa 00dd unsigned byte equal
  128. 5 opcodes
  129. Next code is run if byte at [a]ddress matches [d]ata.
  130. 3C1A**** LUI K0,{a}
  131. 935A**** LBU K0,{a} (K0)
  132. 341B00** ORI K1,R0,{d}
  133. 175B**** BNE K0,K1,{len next code}
  134. 00000000 NOP
  135.  
  136. F0.aaaaaa dddd terminate codelist if HW not equal
  137. 5 opcodes
  138. Ignore all codes following this one if the HW value at [a]ddress does not match [d]ata.
  139. * Potentially bugged. There's two problems here.
  140. First, if 80014878 is set it will be a jump that ends the codelist. They stick that jump in the delay slot of a branch with no regard for how the jump's delay slot will be handled. Shouldn't be a critical problem though.
  141. Second, if 80014878 is not set this code does nothing at all. The handler for setting it when NULL is right after the code parser, just before it's written to the end of the list. It appears there is an edge case in mode 1 where it won't be set automatically at an earlier point. To force the matter, include a 3FFFFFFE code before writing this code.
  142. 3C1A**** LUI K0,{a}
  143. 875A**** LH K0,{a} (K0)
  144. 341B**** ORI K1,R0,{d}
  145. 575B0001 BNEL K0,K1,+1
  146. ******** Jump to end of codelist or, theoretically, NOP
  147. You can use 3FFFFFFE codes to set individual targets for the jump so long as 1) you accurately predict where in the list you want to jump to and 2) after all of the 3F/F0 pairs you add a final 3F that sets the correct return target for the whole list. This may be handy to add complex switches for parts of a code, like different #players conditionals, etc. where you don't want to exit the whole list or to simply turn a single code off in a larger set of them.
  148.  
  149.  
  150. .: Comms Port Commands :.
  151.  
  152. The PC communication port is only used in the shell, not in the trainer. Only three commands are tested for, with an impossible condition set for a fourth.
  153. Interrupts are disabled during communication.
  154. To read a byte from comms:
  155. 1) Read the port until & 0x400 is set.
  156. 2) Read the port; data is & 0xFF.
  157. 3) Write 0x800 to port. This acknowledges the read.
  158. 4) Read the port until & 0x400 is NOT set.
  159. 5) Write 0 to port.
  160. 6) Return the data read at step 2.
  161.  
  162. Writing a byte to comms is more complex. The value is split across multiple writes and written in both the upper and lower halfword.
  163. 1) Read port until & 0x400 is NOT set.
  164. 2) Write value to port: v = byte >> 6; v, 00, (v | 0xC), 0
  165. 3) Read port until & 0x400 is set.
  166. 4) Write value to port: v = (byte >> 3) & 7; v, 00, (v | 0xC), 0
  167. 5) Read port until & 0x400 is NOT set.
  168. 6) Write value to port: v = byte & 7; v, 00, (v | 8), 0
  169. 7) Read port until & 0x400 is set.
  170. 8) Write 0x100 to port.
  171. 9) Read port until & 0x400 is NOT set.
  172.  
  173. Before any command the register is cleared by writing 0 to the port, then reading (and ignoring) the next byte posted by PC. The following byte will be interpreted as a command.
  174.  
  175. 'W' command (wait)
  176. PC is waiting. If N64 responds '6' communication is in a ready state.
  177.  
  178. 'X' command (execute)
  179. 4 bytes (little endian) p->rdram target
  180. 4 bytes (little endian) length
  181. (send "length" bytes. keep a 16bit sum of these values)
  182. 2 bytes (little endian) 16bit sum
  183. N64 will respond with 'OK', then execute your code!
  184. If it responds 'CF' the checksum on the data was invalid and nothing happens.
  185.  
  186. 'U' command (update)
  187. 4 bytes (little endian) p->rdram target
  188. 4 bytes (little endian) length
  189. (send "length" bytes. keep a 16bit sum of these values)
  190. 2 bytes (little endian) 16bit sum
  191. If it responds 'CF' the checksum on the data was invalid and nothing happens.
  192. The Xplorer's eeprom will be overwritten with data starting at address 0. Maximum is not tested, but should cap at 0x40000. After flashing it will check if what's on the eeprom matches the data you sent. If all is well it responds 'OK', otherwise 'FF' is sent (flash failure). It is *highly* suggested you continue trying to reflash the device until successful, otherwise it's fairly likely not to boot again.
  193.  
  194.  
  195. .: Special Registers :.
  196.  
  197. The device has multiple possible mappings that can be enabled and disabled via registers.
  198. At boot, the device overloads the cart domain mapping (10400000). When applying the hook & trainer the mapping is switched to BF400000.
  199.  
  200. B02F0000 Reading this register enables and disables Xplorer mapping. This is required before and after DMA from an attached cart.
  201. BF200000 Same as B02F0000 when BF- mapping applied?
  202.  
  203. B0300000 Read/Write: comms port. 0x400 & 0x800 flags are used as semaphores.
  204.  
  205. B0400400 Write 001E001E here to switch between the B0- mapping and BF- mapping. It is unknown if the value itself is used to create the new mapping (via << 3).
  206.  
  207. B0710000 Read: button index; increases when the XP button pressed (to change CIC selection).
  208.  
  209. B0740000 Read after an eeprom read/write operation to place back in normal address mode.
  210.  
  211. B0760000 Read before reading or writing the low (0) halfword of an eeprom address.
  212. B0770000 Read before reading or writing the high (2) halfword of an eeprom address.
  213.  
  214.  
  215. .: Compatible IC Types :.
  216.  
  217. (Please note the author does not have this device nor has seen board scans. Below is based on disassembly of the reflash code.)
  218. Devices are alternately called EEPROM & FLASH in their datasheets. They act like EEPROM though, being programmable without prior erase.
  219. They're sorted by device ID first, then if an ID isn't found by manufacturer. Because of that lazy sorting false positives are possible.
  220. Compatible devices (well, probably):
  221. **DA 512Kx16 MX29LV800* (or compatible; i.e.: S29AL008J)
  222. **A4 512Kb SST27SF512 (or compatible; likely includes AM29F040)
  223. **D5 1Mbit SST39LF/VF010 (or compatible)
  224. BF** Literally any other SST device that conforms to the interface.
  225. DA** An unknown Winbond IC (manufacturer ID 0xDA) that uses 0x90, not 0xA0, to program.
  226.  
  227.  
  228. .: Plugin Support :.
  229.  
  230. Plugins are executables read from Controller Paks. As an important note, multibank Controller Paks are accepted but only so long as they have no more than 16 banks! That's the largest one Datel ever made but nowhere near the max for the specification. (Blaze's own 1MB paks all appear to use switches instead of being actual high-capacity.)
  231.  
  232. Only the first plugin found will be used! Any others will be ignored. A message will be displayed if a plugin is found.
  233. The GUID must be set to "PLGX" and company ID to "XP". Filenames/extensions are ignored.
  234.  
  235. Until an example appears, this is a best-guess as to the format:
  236. 0x0 4 total size
  237. 0x4 4 offset in data to entrypoint; must be at least 8 bytes before the end of the file (this is an actual test)
  238. 0x8 ???
  239. 0x10 4 checksum for data below; each uses a different algo
  240. 0x14 4 hash for data below
  241. 0x18 4 inverse checksum for data below
  242. 0x1C 4 inverse hash for data below
  243. 0x20 start of data
  244.  
  245. The funny thing about the checksum algos is they appear to throw away the more intricate of the two registers (the actually hashed ones). The first is a simple sum of every byte, the third an inverse sum. The other two take each byte and XOR it in the lowest two bytes of the hash.
  246.  
  247. Entrypoint function accepts two arguments:
  248. 1) p->start of data above
  249. 2) p->an array of 16 function imports.
  250. They are not expected to return. If they do you'd be greeted with a confusing "Loading Plugin failed" message.
  251.  
  252. Specifics aren't known for all 16 of the import functions, but here's a list of the function called in 1.067E:
  253. 80011550 # V0 = interrupts enabled flag; disable interrupts
  254. 80011568 # Status |= A0; enable interrupts
  255. 80012CAC # V0 = p->A0 allocated bytes
  256. 80012E50 # free A0
  257. 800135D8 ^ 0x87654321 # reflash A2 bytes at addr A1 to A0; XOR with 0x87654321 before calling with a JALR
  258. 80012398 # V0 = p->data starting at controller pak page A0 else None
  259. 800122B8 # V0 = p->Note matching GUID A0, company A1 else None
  260. 80012180 # V0 = p->A2 bytes of data at controller pak address A1 read to A0
  261. 800107E0 # set display mode A0
  262. 80010BA4 # ???; fill screen with color A0? Called before printing a string.
  263. 800043CC # print string A2 at (A0, A1)
  264. None
  265. 80010920 # ???; video buffer swap over A0 frames? Called before printing a string.
  266. 80010E94 # set pixel at (A0, A1) to c16 color A2
  267. 80011248 # read PIFram to 8001F1B0
  268. 80011344 # V0 = controller A0 button state
  269.  
  270. Considering this list of functions, updates to the BIOS (0x40000) or cheat files (0xE000 bytes) would be the most likely use. If replaced in their entirety either compression or higher-capacity Controller Paks would be required. That said, any executable code could be used.
  271.  
  272. -Zoinkity
  273.  
Advertisement
Add Comment
Please, Sign In to add comment