 |
 |
GoldenEye 007 Nintendo 64 Community, GoldenEye X, Nintendo 64 Games Discussion GoldenEye Cheats, GoldenEye X Codes, Tips, Help, Nintendo 64 Gaming Community
|
 |
 |
 |
 |
|
 |
 |
 |
 |
 |
zoinkity 007


Joined: 24 Nov 2005 Posts: 1725
 |
Posted: Wed Mar 05, 2025 3:48 pm Post subject: |
 |
|
Yeah, the other big difference from a GS/AR is that if you have 8MB it always places the codes up there, extending the max list length. That's why there's all these conditions for jumps in the first place. That probably lowered overall compatibility--or at least versatility--but more casual-user-friendly.
The way these work is they hook into the exception handler but only when non-critical things occur. It checks what caused the exception/interrupt to fire, and if it isn't one of the ones that must go through will redirect to the codes (or things that call the codes). Xplorers, GS/AR, even the Partner64 development devices all do this the same way.
The reason for the maximum is they set an allocation size. There's optional things that go between the list and the handler mentioned above, and then at the end of it this bounces to the original exception handler. Does seem odd the limit isn't enforced when creating the list. Wonder why not...
[edit]It both is and isn't. They forgot to subtract out the two opcodes that end the list and don't test that you're going to overrun, only that you already have.
Probably best to write shorter custom stages. It's more practical and you can do it right now.
Extending the list is mostly just pointer redirection, then coming up with a way to add more things to it in a non-standard way. Or editing the firmware. Or, honestly, uploading code via PC at runtime and replacing it that way via pointer redirection. All the same methods as with GS/AR, none very user-friendly.
Anyway, curious about the device so will continue looking into it.
Last edited by zoinkity on Thu Mar 06, 2025 1:37 am; edited 1 time in total |
|
|
|
|
|
 |
 |
 |
 |
 |
zoinkity 007


Joined: 24 Nov 2005 Posts: 1725
 |
Posted: Thu Mar 06, 2025 1:29 am Post subject: Rescinding all praise this device ever got. |
 |
|
Found the list, or I should say found the list of Xplorer codes. GS/AR format codes are turned into them before getting parsed.
Rescinding all praise afforded before. There's some obvious bugs in the code processing and it's a bit too redundant. The function 8000850C is (probably) the one generating codelists. Other things look like fragments of old code. It sets the arbitrary limit based on the mode passed.
0) Max codes in mode 0: 78 - 2 opcodes, so 25 normal codes
1) Max codes in mode 1: 38 - 2 opcodes, so 12 normal codes
2) Max codes in mode 2: 514 - 2 opcodes, so 170 normal codes.
Despite these limits it will parse up to 512 codes. That's more than the device holds, but whatever.
If the code is a 2*- type or a 3FFFFF** type it handles that separately. Otherwise, it converts any GS/AR style 8* or D* types into its own format and generates ASM for them.
The code types that are added to the list are:
Code: | 00.aaaaaa dddd store unsigned HW as word
3 opcodes
Writes unsigned HW [d]ata to the word at [a]ddress. The upper two bytes will always be zero as a result.
3C1A**** LUI K0,{a}
341B00** ORI K1,R0,{d}
AF5B**** SW K1,{a} (K0)
30.aaaaaa 00dd store byte
3 opcodes
Writes byte [d]ata to [a]ddress.
3C1A**** LUI K0,{a}
341B00** ORI K1,R0,{d}
A35B**** SB K1,{a} (K0)
50.cccc.AA DDDD 0f.aaaaaa dddd loop type 1
11 opcodes
Writes [c]ount codes, incrementing the [a]ddress by [A] and [d]ata by [D]. This is an actual loop, only 11 opcodes long.
If [f] is 0 writes bytes, if [f] is 1 writes HWs.
4081F000 MTC0 AT,ErrorPC (just storing the register safely)
3C1A**** LUI K0,{a}
275A**** ADDIU K0,K0,{a}
341B**** ORI K1,R0,{d}
3401**** ORI AT,R0,{c}
A35B0000 / A75B0000 SB/SH K1,0000 (K0) (depending on [f])
277B**** ADDIU K1,K1,{D}
275A00** ADDIU K0,K0,{A}
1420FFFC BNE AT,R0,-4
2421FFFF ADDIU AT,AT,FFFF
4001F000 MFC0 AT,ErrorPC (restoring the register)
70.aaaaaa dddd unsigned HW equal
5 opcodes
Next code is run if HW at [a]ddress matches unsigned HW [d]ata.
* Bugged
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!
3C1A**** LUI K0,{a}
875A**** LH K0,{a} (K0)
341B**** ORI K1,R0,{d}
175B**** BNE K0,K1,{len next code}
00000000 NOP
80.aaaaaa dddd store HW
3 opcodes
Writes HW [d]ata to [a]ddress.
3C1A**** LUI K0,{a}
341B**** ORI K1,R0,{d}
A75B**** SH K1,{a} (K0)
90.aaaaaa dddd unsigned HW not equal
5 opcodes
Next code is run if HW at [a]ddress does not match unsigned HW [d]ata.
* Bugged
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!
3C1A**** LUI K0,{a}
875A**** LH K0,{a} (K0)
341B**** ORI K1,R0,{d}
135B**** BEQ K0,K1,{len next code}
00000000 NOP
B.f.cc.AAAA DDDD 00.aaaaaa dddd loop type 2
11 opcodes
Writes [c]ount codes, incrementing the [a]ddress by [A] and [d]ata by [D].
If [f] is 0 writes bytes, if [f] is 1 writes HWs.
4081F000 MTC0 AT,ErrorPC (just storing the register safely)
3C1A**** LUI K0,{a}
275A**** ADDIU K0,K0,{a}
341B**** ORI K1,R0,{d}
340100** ORI AT,R0,{c}
A35B0000 / A75B0000 SB/SH K1,0000 (K0) (depending on [f])
277B**** ADDIU K1,K1,{D}
275A**** ADDIU K0,K0,{A}
1420FFFC BNE AT,R0,-4
2421FFFF ADDIU AT,AT,FFFF
4001F000 MFC0 AT,ErrorPC (restoring the register)
D0.aaaaaa 00dd unsigned byte equal
5 opcodes
Next code is run if byte at [a]ddress matches [d]ata.
3C1A**** LUI K0,{a}
935A**** LBU K0,{a} (K0)
341B00** ORI K1,R0,{d}
175B**** BNE K0,K1,{len next code}
00000000 NOP
F0.aaaaaa dddd terminate codelist if HW not equal
5 opcodes
Ignore all codes following this one if the HW value at [a]ddress does not match [d]ata.
* Potentially bugged. There's two problems here.
First, [i]if 80014878 is set[/i] 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.
Second, [i]if 80014878 is not set[/i] 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.
3C1A**** LUI K0,{a}
875A**** LH K0,{a} (K0)
341B**** ORI K1,R0,{d}
575B0001 BNEL K0,K1,+1
******** Jump to end of codelist or NOP (see note above)
|
This device has two loop types with different formats. It also doesn't fix unaligned addresses like a GS/AR will, and there were some bad codes back in the day that would use a HW write to an odd address (as in not even).
They're 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.
Not completely certain if the B1 code can happen though. There are other parsers that mask bit 1 away and it's a bit difficult to say what exactly happens when or where. For that matter, where are the "button" types? Are they not real?
Not sure if I'll revisit this thing in the future or not. Might look into its port commands, unless there's another specific question.
[edit]
Unless missing the obvious here, the port is only active in the main menus.
It actively handles only three commands:
'W" (wait), which just sort of sits there
"U" (update), used to reflash up to entire device
'X" (execute), used to run arbitrary code!
(There is also a "S" (stop) with an impossible condition.)
First, should note some important addresses.
Code: | B0400000 to B0440000 location of firmware in menus
BF400000 to BF440000 location of firmware during game
B02F0000 likely a lock for address latching; read before and after DMA from attached cart
B0300000 comms
B0400400 001E001E written here; may switch between the B0- mapping and BF- mapping (next read is from BF200000).
B0710000 button index; increases when the XP button pressed (to change CIC selection)
B0740000 read after an eeprom read/write operation; probably relocks access
B0760000 read before reading or writing the low (0) halfword of an eeprom address; unlocks access, and works around the whole "writing a word to write a halfword" issue
B0770000 read before reading or writing the high (2) halfword of an eeprom address; same as above
BF200000 same as B02F0000 when BF- mapping applied? |
Interesting bit here is you send what rdram address you'd like this thing to write to. There are no noticeable limitations except the obvious "changing something in use will probably come back to bite you" problem.
It keeps and checks a 16bit checksum of all the data sent. This is the only integrity test used; no size of validity tests at all. You can literally send it anything and it will run it or flash it.
Code: | 'X' command (execute)
4 bytes (little endian) p->rdram target
4 bytes (little endian) length
(send "length" bytes. keep a 16bit sum of these values)
2 bytes (little endian) 16bit sum
N64 will respond with 'OK', then execute your code!
If it responds 'CF' the checksum on the data was invalid and nothing happens.
'U' command (update)
4 bytes (little endian) p->rdram target
4 bytes (little endian) length
(send "length" bytes. keep a 16bit sum of these values)
2 bytes (little endian) 16bit sum
If it responds 'CF' the checksum on the data was invalid and nothing happens.
The Xplorer's flash will be overwritten with data starting at address 0. Maximum is not tested, but should be 0x40000. After flashing it will check if what's on the flash matches the data you sent. If all is well it responds 'OK', otherwise 'FF' is sent (flash failure).
|
That lack of address testing is a big win here. GS/AR does have an exploitable set of menu commands but they're on rails. Could mod and boot neon64 directly in this thing's menu.
Are there any board scans? Curious if it has a CIC inside of it and what these reflashable chips are. Found the routine but kinda obtuse trying to work backward from a spec. Wrote "flash" above but these commands look a lot like one of SST's EEPROMs.
Last edited by zoinkity on Sun Mar 09, 2025 1:22 am; edited 2 times in total |
|
|
|
|
|
 |
 |
 |
 |
 |
Gahr Agent

Joined: 11 Feb 2025 Posts: 27
 |
Posted: Thu Mar 06, 2025 1:35 am Post subject: |
 |
|
I was actually in the process of replying when I saw your new reply.
Interesting findings, it looks like the whole thing was rushed, unfortunately. Sounds like there would be room for a better cheat device but instead it's half baked.
Anyway glad to hear you're not done just yet with it.
I will work on Xplorer specific lists which remain below 170 codes and don't make use of 50 codes.
I think there are definitely other limitations not identified yet because I've tested lists matching these criterias which either lead to no boot, freeze at multiplayer level selection screen or black screen when loading the level. Like some specific codes that aren't processed correctly by the Xplorer. My Action Replay is dead and I need to revive it with a Retroblaster but when I do, I will test the same codes on Action Replay and I'm prepared to bet they will work just fine. |
|
|
|
|
|
 |
 |
 |
 |
 |
zoinkity 007


Joined: 24 Nov 2005 Posts: 1725
 |
Posted: Thu Mar 06, 2025 1:53 am Post subject: |
 |
|
It's more about opcode count than total number really. You can make a calculator pretty easily though.
8* types: 3 opcodes
D* types: 5 opcodes
loops: 11 opcodes
If this list is actually correct there's some pretty serious bugs with conditionals.
- All halfword conditionals are broken. The conditional only works if value is in the range 0 to 7FFF.
- There is no "not equal" byte conditional test or it sure isn't being obvious.
- The one that turns the list off is only guaranteed to work if a 3FFFFFFE code was used prior to it being parsed. Honestly not certain the upper half of the opcode is written at that point though.
It's strange because elsewhere these are treated more like specific behavior depends on the upper bits. Almost wonder if there's a second set of these someplace.
Would be worth making a new firmware honestly. The device itself is a bit of an improvement over Datels. Eliminating all the redundant stuff and cleaning up bugs like these would also make extra room for more codelist storage. That's its other problem, the maximum is pretty low. |
|
|
|
|
|
 |
 |
 |
 |
 |
zoinkity 007


Joined: 24 Nov 2005 Posts: 1725
 |
Posted: Thu Mar 13, 2025 9:13 pm Post subject: Plugins are real. |
 |
|
Okay..."plugins" are executable code stored on Controller Paks.
One of the function pointers it passes as an argument is the that that overwrites the BIOS, so guessing the primary use was to update these things. Obviously compression would be involved, otherwise you'd minimum need an 8Mbit pak. Just doing a cheat list would fit on any pak though.
So here's the big question:
are there any examples of Controller Paks with Xplorer updates?
Even pictures will suffice.
Here's what's known so far.
Only the first plugin found will be used! Any others will be ignored. [s]Only a Controller Pak in controller slot 1 will be read.[/s] It does iterate them and find which one has the thing (80008DCC), but not sure if it can actually read the code off any of them besides the first.
The GUID must be set to "PLGX" and company ID to "XP". Filename/ext aren't read.
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 and Blaze ever made but nowhere near the max for the specification. [s]Haha, didn't realize Blaze made these too...[/s] Theirs had switches not actual banks, what the heck? So not a bug--until somebody makes a bigger pak.
Until an example appears, this is a best-guess as to the format:
Code: | 0x0 4 total size
0x4 4 offset in data to entrypoint; must be at least 8 bytes before the end of the file (this is an actual test)
0x8 ???
0x10 4 checksum for data below; each uses a different algo
0x14 4 hash for data below
0x18 4 inverse checksum for data below
0x1C 4 inverse hash for data below
0x20 start of data |
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 word.
Entrypoint function accepts two arguments:
1) p->start of data above
2) p->an array of 16 function imports.
Not sure about all the functions called, but here's the scratchlist:
Code: | k+0 = 80011550 # V0 = interrupts enabled flag; disable interrupts
k+4 = 80011568 # Status |= A0; enable interrupts
k+8 = 80012CAC # V0 = p->A0 allocated bytes
k+C = 80012E50 # free A0
k+10 = 800135D8 ^ 0x87654321 # reflash A2 bytes at addr A1 to A0
k+14 = 80012398 # V0 = p->data starting at controller pak page A0 else None
k+18 = 800122B8 # V0 = p->Note matching GUID A0, company A1 else None
k+1C = 80012180 # V0 = p->A2 bytes of data at controller pak address A1 read to A0
k+20 = 800107E0 # set display mode A0
k+24 = 80010BA4
k+28 = 800043CC # print string A2 at (A0, A1)
k+2C = None
k+30 = 80010920
k+34 = 80010E94
k+38 = 80011248 # read PIFram to 8001F1B0
k+3C = 80011344 # V0 = controller A0 button state |
|
|
|
|
|
|
 |
 |
 |
 |
 |
|
 |
 |
 |
 |
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|
|
 |