Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Topics - ISSOtm

Pages: [1] 2
Because why not :)
This is useful, for example, when making custom save files (poke @TheZZAZZGlitch :P). Or if you want to be an ugly troll to your friends ^^

This is based off DMA hijacking, a technique that allows "automatic" ACE on each frame.
First off, you will want to write this piece of code somewhere :
Code: [Select]
ld a, [$C3AA]
cp $79
jr nz, .ok
ld hl, $0014
add hl, sp
ld [hl], $E8
inc hl
ld [hl], $29
ld a, $C3
ld c, $46
And for those who want hex :
Code: [Select]
FAAAC3 FE79 2009 211400 39 36E8 23 3629 3EC3 0E46 C9
Then, you will want to write
Code: [Select]
CDYYXX E2at $FF80, where the address of the above function is $XXYY (be careful of the order, it's in reverse !)
For example, if NoStart is at $CAFE you will write
Code: [Select]
But be careful, those four bytes have to be written in one frame ! Otherwise you will almost certainly crash :D

Now, if everything is in place, the START menu will pop up when you press START, but will close immediately, without even printing any text inside
Until you reset the console, actually. This doesn't persist through resets :3 (although it is possible to make it permanent)
But this can make challenge runs where you aren't allowed to save (unless you change boxes), use items out of battle, re-order your Pokémon (outside of the PC). Or just have fun screwing around :P

How does it work ?

$C3AA is a part of the game's WRAM tilemap, and this is where tiles are written to before being copied to VRAM (because of access restrictions)
Specifically, the game writes a $79 there (top-left menu tile) when opening the START menu. As far as I know, no other text box in the game does this.
If such a tile is detected, the script knows the game is attempting to open the START menu. Specifically, due to how text boxes work, the game is processing the DisplayTextIDInit function, which consistently waits for a few frames.
What we do is manipulate this function's return address so instead of displaying the menu, it will directly go to the function that closes it.
Code: [Select]
ld hl, $0014
add hl, sp
makes hl point to the aforementioned return address, which we overwrite with $29E8 (CloseTextDisplay) which undoes everything. Pop :P
All that remains of the menu is the blank text box, which is displayed by DisplayTextIDInit. It would be possible to avoid it, but that would be heavier. Besides, if you want to troll a friend, he will probably freak out a bit more :D

Note that this doesn't affect any other text box ;)

I'd like to make a demonstration video but I don't have any working screen recorder.
Arbitrary Code Execution Discussion / Easy tool to make 8F setups
« on: February 25, 2017, 12:31:39 pm »
GBz80 to Items : Making 8F setups easier

GBz80 to Items is an online tool that allows you to write 8F item setups very easily.
You just type your code, click a button, and you get an item list. Easy as cake !
It's currently available online at

Knowledge of Gameboy assembly is required, this is only intended to replace using The Big HEX List and speed up the process of writing setups.

No problem ! I have you covered.
If you have a GitHub account, I suggest you go here and create a new issue.
Otherwise, just post your request in the thread. I visit the forums quite often, so I'll probably work on it shortly.

#002  "ld (mem16), a" throws "Line undefined : Invalid operand (mem16) !" Fixed in 3.0.1
#003  "ld a, (mem16)" throws "Line 1 : mem16 isn't a valid 16-bit number !" Fixed in 3.0.1
#004  ldh doesn't work and throws "memAccess is undefined" internal (!) errors Fixed in 3.0.1
#005  Bit rotation instructions throw "reg8.indexof is not a function !" Fixed in 3.0.1
#006  jp mem16 throws "invalid operand $36e0 !"

Legacy stuff :
This is the third version of the compiler I make, and the second I publish.
The old v2 version can be checked out at (version 2.1) and (version 2.2, never finished because shitty code)
Arbitrary Code Execution Discussion / Using 8F to ACE other games
« on: February 11, 2017, 10:52:57 am »
This is a method to perform ACE on each game frame - provided it uses OAM DMA. I guess all games use it, so, yeah.
It also has the bonus of being cartswap-friendly.
Need no more to be convinced ? Then here is the thing !

This works only on Pokémon Red or Blue.
You need to place the following hex data at the following locations.
Use your favorite emulator's (or BGB if you intend to do cartswap) memory viewer to do that.
Alternatively, you can use an in-game memory editor such as offgao's (see this thread)
Also, make sure all data at FF80 is not executed until it is complete. It's very sensitive data executed on each frame, and you don't wanna crash.
One way to do it is to write a $C9 at $FF80 first, and write the $CD last.

These two pieces of data don't persist after saving and resetting.
Code: [Select]
At DF00 : (WRITE FIRST !!)


Once both pieces of code are in place, go into any house, exit it and o surprise, Hall of Fame !
Tech details are below.

If you want to try the cartswap-based funky badass-showoff version (Pokémon Blue "cartswap wrong warp%"), then set up DF00 and FF80 as above, and then this.
Code: [Select]
At D163 :

At D31D :

At D53A :
Now, open your bag (surprise, there's 8F in there), use 8F. The game will then freeze, so use "Load ROM without reset" on BGB and load Pokémon Blue (or Red, works fine too).
Press any direction on the D-Pad, and the game will start. You can then choose to start a new game, and you will soon find out your house leads directly to the Hall of Fame ! Nice.

I attached a BGB save state to be tried on Pokémon Red that has all set up. This was hex edited, but is doable legitimately. You just have to load it in BGB, then use the above method or just close the menus and walk out of your house.

Tech stuff
On each LCD frame, the game has to run a small routine in RAM to transfer sprites from a temporary buffer to the location the GB uses. However, we hijack this routine to run custom code.
The trick is simple : hijack usual code flow, run our code, and make sure everything else goes as intended.

At $FF80 is this code :
Code: [Select]
ld a, $C3
ldh [$FF46], a
which we overwrite with
Code: [Select]
call $DF00
ld [hl], a

Pokémon R/B/Y allocate $DF00 to $DFFF to be stack space. However, during usual play, I never experienced the game using more than ~100 bytes at once. This leaves us with more than 128 bytes for code storage !
Code: [Select]
ld a, $76 ; Hall of Fame map ID
ld [$D365], a ; Door mats exit
ld hl, $FF46 ; OAM DMA
ld a, $C3
This code essentially sets all door mats exits to warp Red to the Hall of Fame. Pretty neato, huh ?
This is a half gameshark emulator, because it can't set a value mid-frame. However, it is cartswap-friendly, using for example the following setup :

The data at $D163 and $D31D set up 8F properly, I'll just skip explaining them.

The code at $D53B is a bigger deal.
Code: [Select]
PartialInit:: ; $D53B
  ld hl, Init
; Real init function. We copy it to RAM for patching.
  ld de, $DF0B
; Routine copy location.
  ld bc, $0049
; How large the target code is : 73 bytes !
; We will append a jump to end it later.
  call CopyData
  ld a, $10
  ldh [$FFFF], a
; Only enable joypad interrupt.
; It should pop instantly, but it doesn't crash, phew !
; a = $20
  ldh [$FF00], a
; Select Dpad
  dec a
; a = $1F
; Patch WRAM clear size to not clear $DF00 onwards.
  ld [$DF0B+$2B], a
; Write part of the "return" address.
  ld [$DF0B+$3F], a
  ld a, $8A
; Patch HRAM clear code to not clear patched DMA code.
  ld [$DF0B+$38], a
; It will clear past HRAM, but no problem.
  ld a, $C3
; JP instruction.
  ld [$DF0B+$3D], a
  ld a, $A1
  ld [$DF0B+$3E], a
; Patch DMA writing with returning to ROM code.
  jp $DF0B
; Jump to patched init. Cartswap is complete.
Generation I Glitch Discussion / WTW and battles
« on: January 26, 2017, 11:30:36 am »
The game actually checks whether you're being guided by the game before sending you into a battle, be it Trainer or wild.

The called function resides here :

Notes that it returns non-zero is bit 7 of $D730 is set.
This bit is used by the game when a sprite is following scripted movement.
But did you know the Pewter Museum and Brock Through Walls of activating NoClip actually set this flag ? Yeah, no kidding !
After testing (setting $CD38 to $FF, $CD3B to $FF and $D730 to $80 enables NoClip and makes you being controlled by the game unless you hold any button), I found out two interesting things.
First, wild battles do NOT trigger. Like when you enable bit 1 of $D732 and hold down B.
Second, Trainer battles... stuff. First, Trainers do notice you, and bring their text properly. They don't have to walk to you. However, when you close their text... no battles begins until $D730 bit 7 is zero, which happens when $CD38 hits zero.

tl;dr : using Pewter Museum Guy NoClip or Browk Through Walls disables wild battles as long as the glitch stays active (eg it cancels when entering any building). Trainers don't begin their battle until CD38 has hit 0, which may take a couple seconds, but you can't avoid Trainers that way. Too bad !
General Discussion / GCLf member sprites
« on: January 23, 2017, 10:00:25 am »
Anyone that wants to can submit here a 16x16 pixel image that represents them. 4 levels of grayscale are allowed (yep it's for gray GB). I accept anything that can be safely shown to anyone under 18.
Non-Core Game Glitch Discussion / Pokémon Stadium - N64 ACE HYPE !
« on: January 21, 2017, 07:34:10 am »
MrCheeze did it. Basically, attempting to use Pokémon Stadium to trade Pokémon to a Gen I cartridge with more than 20 Pokémon, you get a buffer overflow.
Demonstration video
Tech stuff

Get hyped guys, if we manage to make cartswap real on the N64, we basically pwn the fifth generation of consoles.

I'm going to send a R.O.B to Game Freak at this point. Via mail.
The Dumpster Out Back / Re: Who can approve wiki edits ?
« on: January 20, 2017, 01:39:09 am »
Bump, but I still can't approve edits :(

[EDIT] Thanks Torchickens !
Wiki Discussion / Improving the homepage
« on: January 20, 2017, 01:24:51 am »
The homepage doesn't link to wiki articles, except in the sidebar, which I find not that easy to browse.
I suggest we put some thing along the lines of "If you want to start browsing glitches, then here is the [[glitch list]]" right before "Want to help?"

[[glitch list]] should contain links to glitch categories, such as a "Browse per generation" section, a "Getting Mew" section, a "Catch'em all" section, etc.
I don't want to start working on the page unless it gets approved by the community.
Video Games / Routing a "Pokémon Red Playaround" TAS
« on: January 16, 2017, 12:03:56 pm »
The principle of a TAS, for those that don't know, is basically to program an emulator to play a game "perfectly". To do so, tools such as frame advance, rewind and save states are allowed, hence the name Tool-Assisted Speedrun. The goal is usually to finish the game as fast as possible, using glitches most of the time but not always.

Here, I would like the discuss a route for a TAS that doesn't aim at finishing Pokémon Red as fast as possible - others do that already and way better than me - but instead to showcase glitches in an entertaining way.

The goal I planned for this TAS is to obtain 8F ACE, use WTW to skip all Victory Road, then 8F-induced WTW to skip the whole Elite Four. I think winning by pure ACE or underflow somewhat sucks.

Here is the current planned route :
1. Maybe manipulate the female NPC to trigger the Oak softlock ?
2. Trigger the sprite overflow in the lab
3. Manipulate Charmander's stats for Brock Through Walls (BTW) later
4. Open the START menu after the Rival battle, just for fun
5. Go and heal in Viridian
6. Get and deliver the Parcel
6 1/2. Buy items to have 6 item slots filled. The highest-selling must be bought last
7. Set up the death Trainer-Fly in Viridian Forest
8. Battle the Rival
9. Get Oak's glitched text (pause on it for like a second, have slow text speed)
10. On the way back, get the level 3 Pidgey encounter, Growl 6 times
11. Capture Nidoking 8)
12. Level it up in the forest to speed future battles up
13. Heal at Pewter to set Teleport destination
14. Perform Brock Skip
15. Reload the map to respawn the NPC
16. Perform BTW
17. Reach Cerulean, skipping Mt. Moon
18. Catch an Abra for Teleport
19. Go to Celadon while making a detour through Lavender and Vermilion to add to the Fly list
20. Obtain Fly HM
21. Go back to Pewter using Teleport
22. Perform BTW again
23. Set up a Trainer-Fly on Route 24 using Teleport
24. Save at the PC then reset
25. Return to Cerulean by BTW
26. Engage the swimmer in Misty's Gym
27. Let Charmander against his second Pokémon to showcase the dumb AI
28. Obtain level 1 Mew, if possible we should remove Snorlax to save another BTW later
29. Set up another Teleport TFly
30. Save at the PC, then reset
31. Perform BTW still again
32. Beat Misty, levelling Mew up in the process. If not possible, it has to be levelled up ASAP since we need to deposit it later, and that crashes if it has negative XP
33. Encounter Missingno, rearrange items and catch it to make maximal profit
34. Teach Fly to Mew
35. Fly to Celadon
36. Sell the 256 cloned items
37. Buy a Water Stone and an X Special. The X Special should land in the inventory's sixth slot
38. Go to Cycling Road. If Snorlax cannot be removed by TFly, do another BTW
39. Bypass the bike requirement. If doing BTW, pass through the loading zone (described at the end of this post) and go on the road without a bike to showcase downwards movement being applied anyways, but go back to be forced on the (faster) bike
40. Go to Fuchsia to set on Fly list
41. Catch Ditto and set it up for Cooltrainer (faster item duplication than TFly)
42. Fly to Vermilion
43. Enter Diglett's Cave
44. Duplicate X Specials. Might as well dupe Water Stones if possible
45. Fly to Celadon
46. Obtain 8F. Deposit items in the PC instead of tossing (avoid wasting the Water Stone(s) and buy items to stabilize the inventory)
47. Fly to Cerulean
48. Deposit 8F, withdraw items to stabilize the inventory
49. Set up a Trainer-Fly, going to Lavender
50. Engage the guy that yields a Pidgey, Growl 6 times with Charmander and finish with Mew
51. Fly to Cerulean
52. Capture Pidgey at level 1, manipulating its stats so it will have 233 Max HP later
53. Teach Fly to Pidgey (can open START menu for TFly, teach then fly)
54. Get it to level 100 and cancel the evolution
55. Set up TFly again, going to Celadon
56. Go to the PC, deposit Abra, Nidoking and Mew, then save and reset
57. Set up another TFly using the famous Gambler, going to Pewter
58. Trigger BTW again
59. Fly to Fuchsia, this doesn't cancel the glitch
60. Go on the water to catch a Tentacool
61. Go east of Fuchsia and fight an Arbok-yielding Trainer
62. Fly back to Cerulean
63. Obtain Arbok through the warp
64. Battle the guy to the Gambler's left to get a Parasect yield (other Trainers may be faster, though, but I found none)
65. Go back to Cerulean to get it

Now what is left is :
- getting Onix (or another Pokémon to tweak where ACE will begin in the pack)
- getting the required items
- optimizing the route
- doing the thing
- being showcased at AGDQ

Removing Snorlax
TheZZAZZGlitch explained it in one of his videos. Basically we need to load Snorlax's map right before doing a TFly, and due to the glitch's properties, this will remove Snorlax.

Passing through loading zones
If a warp is triggered not by stepping on a door, it can be passed through. To do so, NoClip (aka WTW) is required. Then, the player must be on the title that he should stand on to trigger the warp, but not facing the warp. By pressing and holding towards the warp, he will pass right through.

An example will be given on the Cycling Road gate west of Celadon City. The player should stand one step north of where exiting the gate puts him, with NoClip active. Then, move one step down. Pressing and holding left will have the player step on the gate. Tapping left to turn, releasing then holding will have the player enter the gate, however.
Generation I Glitch Discussion / Trainer-Fly plus Cycling Road
« on: January 15, 2017, 06:02:30 pm »
I'm playing EU Pokémon Yellow, and I have triggered a Trainer-Fly using the Gambler we all know about. This doesn't seem important for what's next, but I say in case it might come in handy.
I flew to Celadon City for a match with a Cue Ball (I was aiming for a Kadabra), but when I entered the "sloped" area, I wasn't forced downwards ! All non-downwards movement is still slow, but Red doesn't move when not holding any button.
Both wild battles and Trainer battles cancel the glitch.

I assume this comes from that the game doesn't apply downwards movement when Red engages a battle, but the fact that wild battles also cancel the glitch proves that non-Trainer battles also unset a few flags. These flags might be worth researching.
Nintendo Switch live presentation : 13/01/2017, 4:00 GMT

Yeah, I know that was this morning, but whatevs.
Let's talk about it !

Did you watch the Switch presentation ?
Do you think it'll be better than the Wii U ?
Do you think it'll suxx way harder ?
Will you buy it ?
Y'know, stuff like that.

I love the features Nintendo added. The "HD vibration" thingy and this "Arms" game hype me near 0%, but the semi-portability rings a few bells for me.
I also give a kudo for Splatoon 2 (looks even better than the original ? Wah.), Mario Odyssey and MK8 Deluxe (just for the new characters and tracks. 0 f**k given about the engine being the same)
Plus, Skyrim and Breath of the Wild. FREAKING BREATH OF THE WILD AS A LAUNCH TITLE. If I was working hard enough for my ever-nearing terms, I'd pre-order all right now, but gotta work hard, heh.
Whatcha think 'bout it ?
Have you seen Pokémon plays Twitch ? If yes, you know that it is possible to gain SNES ACE "through" the SGB.
"Isn't that TAS-only ?" you might ask. Nope ! And here is how to do.

We are first going to write a tiny payload to feed the SNES some code.
Why not using items ? Because the code maps to invalid items ! So we can't use items to store the packet data and sending code :(
And I've thought of a somewhat original (I guess) storage solution. We're gonna use Pokédex flags. Hell. Yeah.
First, setup your pack ! But don't use 8F quite yet !! The items are actually fairly simple (and cheap) to acquire, and you don't need many duplications.
Code: [Select]
Any item x[any qty]
Awakening x[byte to write]
HP Up x175
Water Stone x4
Poké Ball x121
Great Ball x3
Burn Heal x3
X Accuracy x16
Antidote x34
Parlyz Heal x125
Ice Heal x46
Leaf Stone x34
TM01 x[any qty]
Lemonade x28
Guard Spec. x3
Super Repel x233
First, use 8F once. It's very important ! Then we're ready.

The setup has two modes : an "entering" mode and a "packet sending" mode.

Right now, the setup is in "entering" mode, but we will need to write a bit of code that is essential for the "packet sending" mode to function properly.
This will also corrupt your Pokédex flags. It makes the code simpler (and actually possible), and I guess when doing SGB to SNES ACE, Pokédex progression doesn't matter that much :D
If you messed up a byte write, toss an X Accuracy and try again. This is built to be fairly forgiving :)

So, we're going to 10 bytes of code to help send packets to the SGB.
The method is the following :
  • Toss as many Awakenings as indicated by the first number of the table
  • Use 8F
  • Repeat with the next number in the table.
Here goes the table :
51 | 68 | 203 | 149 | 51 | 21 | 161 | 61 | 51 | 203

Once finished, toss all X Accuracies but one. You are now ready to write all 16 bytes of the packet to be sent.
The usage is very simple : you just need to make the quantity of Awakenings match the value you're willing to write, and then you just use 8F.
Basically, when you use 8F while having "A" Awakenings and "X" X Accuracies, the "X"th byte of the packet will turn into "A". The quantity of X Accuracies will increment each time to target the next byte to write. Full automation, man !
(If you screwed up a write, toss one X Accuracy and do it again.)
Also, don't use 8F if you have 17 X Accuracies or more ! You will screw up the code you have previously written. At best the "packet sending" mode will just not work, at worst you''l get a crash. Be cautious !

Once you have written all 16 bytes, you should swap the Lemonades with the Awakenings, the Guard Spec.s with the HP Ups, and the Super Repels with the Water Stones. The setup will be in "packet sending" mode, so use 8F, and BAM ! The packet is sent.
Then you must swap the items again to be in "entering" mode again, toss all X Accuracies but one, and repeat !

Source code
Here is the code that builds the packets.
Code: [Select]
ld c, $byte
inc hl ; points to third item's quantity
xor a
ldi (hl), a ; reset it for next write
inc b
inc b
ld a, c
inc bc
inc bc
inc c
inc bc
ld l, $01
dec bc
ldi (hl), a
ld a, l
dec c
ld l, $2F
ldi (hl), a

Here is the code that prepares the packet send :
Code: [Select]
ld a, $1C
jr c, $D311 ; start of the second part of the packet sending payload

Here is the code appended to the packets :
Code: [Select]
; The caller made a = $1C, so we will switch to bank $1C, home of SendSGBPacket !
call BankswitchHome ; $35BC
ld l, e ; hl = $D301, start of packet
call SendSGBPacket ; $5FEB
jp BankswitchBack ; $35CD
Hex :
Code: [Select]
CD BC 35
C3 CD 35
This code would have been a pain to write using items, so instead it is jumped to when using 8F in "packet sending" mode.

One last thing : I'm going to write a program that gives packet building indications to help use this setup.
It should be done in a few days.
Generation II Glitch Discussion / Obtaining TM33 via Pokémon R/B/Y.
« on: December 11, 2016, 08:12:47 am »
I'd like to try giving the player a TM33 in a pocket by editing save files, in order to make my "Luigi exploit" something more useful.

The idea would be :
  • Use the PoC I've written to hang the CPU
  • Switch to Pokémon G/S/C
  • Call the routine that "decompresses" save data
  • Modify an item pocket
  • Call the routine that saves the game
  • Hang the CPU, have the user reboot the game to enjoy !

(The "Luigi Exploit" is here)
Double-posting because IT WORKED.

... Okay, wit some tweaks. I tested the setup, and it didn't work. I then realized that the values read from "ROM" when the cartridge was out were $FF and not $00
(To see this, check out the "Nintendo" logo on startup when no cartridge is in. It is fully black, as in "all bits set", as in $FF. That plus other stuff)

So I edited the thing slightly... before realizing that would NOT work. Why ? Because Pokémon R/B's ROM0:0000 is $FF ! So I modified one more byte to not modify de, whose value stays $0001 (ROM0:0001 is $00 in R/B)

Here is my current setup :
Code: [Select]
Hyper Ball x68 (doesn't matter, just to catch some MissingNo's)
Ice Heal x243 (because I don't have TM43)
Burn Heal x99
Antidote x106
Parlyz Heal x26
X Attack x60
Fire Stone x251
Poké Ball x26
Fresh Water x55
Rare Candy x251
Lemonade x1
TM33 x1
Note that this was set up quite poorly because I needed to make hotfixes because I was too lazy to redesign everything.

When using 8F, the game freezes (since its program is interrupted and all interrupts are cut off), the music too.
It is then possible to remove the cartridge, which sometimes makes the console crash for some reason.
What is intended is that placing a cartridge in should have it boot as if the console just started... yet it only worked (for me) 6 times in a row out of ~50 tries total.
I couldn't record it at that time, and I didn't manage to pull this off successfully again.

So, I think we need to give some delay to the cartridge to be fully in place again before running that jp (hl) (to $0100), to give time for all the cartridge pins to be in place when the jump is executed.
Something that should go along the lines of this :
Code: [Select]
dec e ; de = $0000
dec de ; underflows the first time it happens
inc b ; add some delay for the poor, slow user :(
ld a, d
inc bc ; still more delay ! (and padding)
or e ; a = $00 if and only if d = $00 and e = $00
jr nz, delayCartReboot
Counting (1 + 1 + 1 + 1 + 1 + 3) * 65536 = 524288 clock cycles @ 4 MHz (Source), that should give 7 seconds before control is given back to the cartridge. Sounds good to me. 0.13 second. Not enough, we need to make another loop.

Also, I tried on GBA. The console reboots as the cartridge is re-inserted, so this will be GB/GBC-only.
I also need to test on a DMG to confirm whether it has a chance to work or not.

tl;dr : yeah, that worked. Still gotta improve it to increase the success rate.

Computed the list of items required to perform the 7-second method.
Code: [Select]
Ice Heal x243 (= 1 + 128 - 14 + 128)
Burn Heal x99
Antidote x106 (= 1 + 128 - 23)
Escape Rope x27
Poké Ball x122 (= 1 + 128 - 7)
Great Ball x179 (= 1 + 128 - 78 + 128)
Fire Stone x249 ( = 1 + 128 - 8 + 128)
Lemonade x1 / x17
TM33 x[any qty]
TM33 is sold in the Celadon Dept. Store.
And yeah, gotta MissingNo-dupe'em all. (8 duplications)

Tested, and the delay is too shots. Actually, the game freezes for ~0.13 second. Meh >.>
We need to make this loop run ~70 times (gives us 9 seconds to swap)
Code: [Select]
dec c ; padding
inc h ; padding
ld h, e ; $0122
dec bc ; padding
ld l, d ; hl = $0100
ld c, $01 ; adjust this for delay : will wait (256 - c) * 0.13 second
dec e ; de = $0000

dec de
inc b ; padding
ld a, d
inc bc ; padding ; won't affect C since this is run 256 times
or e ; a = $00 if and only if de = $0000
jr z, delayCartReboot

inc c ; we need C to be zero
rrca ; padding
jr nz, delayCartReboot

ld a, $01
jp (hl)
Code: [Select]
Does not x[matter at all]
Ice Heal x243
Protein x99
Antidote x106
Awakening x1
Escape Rope x27
Poké Ball x122
Great Bal x179
Fire Stone x249
Burn Heal x15
Rare Candy x245 (= 1 + 128 - 12 + 128)
Lemonade x1
TM33 x[nobody cares]
The CartSwap ACE
This is a Proof of Concept (or PoC) that we can use Pokémon Red, Blue and Yellow to manipulate other games.

The principle is extremely simple :
  • Set up in Pokémon R/B/Y
  • Temporarily hang the CPU
  • Switch cartridges while the CPU is doing nothing
  • Do things with the new cartridge

This PoC limits itself to rebooting the cartridge, but a more useful application would be modifying save files, stuff like that. This exploit just proves this is possible.

How to do this ?
First, you'll need Pokémon Red, Blue or Yellow, and a Game Boy Color. I insist on GBC, because it won't work on any GBA model.
It may or may not work on black&white GBs, but nobody tested it yet.
For obvious reasons, this cannot be done on emulators.

Once you are running R/B/Y on your GBC, you need to acquire 8F/ws m arbitrary code execution. Visit our wiki article.
That is, obtain the item, and set up the bootstrap accordingly.

Then, you need to setup your inventory exactly like this, starting from the first item :
Code: [Select]
(V  Warning, these panels scroll  V)

8F / ws m
Any item x[any qty]
Ice Heal x243
Protein x99
Antidote x106
Awakening x240
Escape Rope x27
Poké Ball x122
Great Ball x179
TM06 x255
Super Repel x247
Fresh Water x4
Burn Heal x177
Fire Stone x241
Lemonade x1
TM33 x[whatever]
[the rest of the item pack doesn't matter]

Here is a method to get all of this :
  • Obtain 8F and set the party up
  • Empty your bag, save for the first two slots
  • Buy Antidote x1, Awakening x1, Escape Rope x27, Poké Ball x1 and Burn Heal x1 at Pewter City Mart
  • Buy Ice Heal x1, Great Ball x1, Super Repel x1 and TM33 x1 (or more, it doesn't matter) at the Celadon Dept. Store 1F
  • Buy Fire Stone x1 at Celadon Dept Store 3F
  • Buy Protein x99 (or buy Protein x1 and duplicate it later) at Celadon Dept Store 4F
  • Buy Lemonade x1 or x11 and Fresh Water x4 at Celadon Dept Store 5F
  • Defeat Koga to get TM06 (or retrieve it from your storage system if you already beat him)
  • MissingNo-duplicate Ice Heal, Antidote, Awakening, Poké Ball, Great Ball, TM07, Super Repel, Burn Heal and Fire Stone.
  • Toss Ice Heal x14, Antidote x23, Awakening x17, Poké Ball x135 (you can toss Poké Ball x99 then Poké Ball x36), Great Ball x78, TM07 x2, Super Repel x10 and Fire Stone x16
  • Duplicate again Ice Heal, Awakening, Poké Ball, Great Ball, TM07, Super Repel and Fire Stone
  • Swap items using the SELECT button to move items around until you match the item pack described above.
(For newbies, "MissingNo-duplicate" means "encounter or capture MissingNo with the item you wish to duplicate in the sixth slot of your item pack")

I then recommend that you save. You're going to pull your cartridge out, anyway, so it's a good idea to save this hard work :D
If your item pack doesn't match what is described above, I take no responsibility from what happens next, including crashes, corruptions and loss of save data. YOU HAVE BEEN WARNED.

Then use 8F / ws m. If the game just freezes, you probably got it right. If not, that's where you'll be happy you saved.
You now have like ten seconds to pull your Pokémon game out of the Game Boy and place another cartridge in.
Then the game in place now will start, but it will believe it is running on a black & white GB instead of a GBC. Yep.

That's what this exploit does : start a game without going through the "Game Boy" script, and thus you could, for example, edit your save file !

Tech details
When pulling the cartridge out of the socket, the CPU doesn't hang, reset or anything. What happens is all reads from ROM return hex:FF.
Strangely, it appears that the GBC boot ROM may actually be ran again. I don't know why this doesn't work 100% of the time.

The thing is, Furrtek proved it was possible to remove the cartridge and have the CPU still run. (See TheZZAZZGlitch's post below).
As such, the idea was to prevent the CPU from running anything ROM while cartridges were being swapped.
This implied two things.
First, disabling interrupts. Because all reads from ROM return $FF, this would result in constant rst 38h, and as such, a stack overflow that would erase all RAM.
Second, running code from a memory location NOT in ROM nor SRAM.
We were left with VRAM, WRAM (and Echo RAM, but that's essentially the same :P), OAM and HRAM.
WRAM was the good candidate, since all ACE exploits ran code from WRAM.

We thus needed to disable interrupts, temporarily hang the CPU while in RAM, and resume once the cartridge was back in.
I first tried a code that first waited to read a $FF from ROM, then boot back up as it read no more $FF. It got a 12% success rate, probably from badly engaged pins.
Then TheZZAZZGlitch suggested a code that just waited for a set amount of time. After many mistakes, I finally made the code ! And here it is.

Here is the GBz80 assembly code that is actually ran by the exploit, very dirty and hackish, but what matters are the items :D
Code: [Select]
dec c ; padding
inc h ; padding
ld h, e ; $0122
dec bc ; padding
ld l, d ; hl = $0100
ld c, $F0 ; adjust this for delay : the more there are, the less you'll wait. 240 give ~12 seconds of delay.
dec e ; de = $0000

dec de
inc b ; padding
ld a, d
inc bc ; padding ; won't affect C since this is run 256 times
or e ; a = $00 if and only if de = $0000. Also resets the C flag, so the next op will really be an "add".
adc a, $FF ; will overflow unless a = $00, ie de = $0000
jr c, delayCartReboot

inc a ; a becomes $00 (it was $FF)
inc b ; padding
inc c ; we need C to be zero
or c ; A = $00 at this point, so essentially this is "ld a, c" but this updates the Z flag
jr nz, delayCartReboot

ld a, $01
jp (hl)
This code is full of padding instructions, intended to make the code "viable" as items, because some values are represented by invalid items, or some we can't the quantity of.
They are intended to not trash sensible data (RAM and important registers) and not lock execution.

Step-by-step explanation :
First, we have the instruction that disables interrupts. It could have replaced the "inc b" instruction sandwiched between the "inc a" and "inc c" in the later part of the code, saving one slot, but this would be dangerous.
Then, I use the fact that, when this code starts, register DE's value is hex:0001. As such, I make H equal E ($00) and L equal D ($01), that gives HL = $0100, the memory address the Game Boy boot ROM jumps to when it gives control to the game. We will preserve this value during the whole process as to make the final jump a simple "jp (hl)".
We then decrement E to make DE equal $0000, the value required for all entries in the following loop.

We then enter the two intricate loops, marked each by their jr instruction.
The inside one simply decrements DE, and ends when the bitwise OR of D and E plus $FF doesn't trigger overflow.
This only happens when D OR E = 0, ie when D = E = 0, ie when DE = $0000. Tests gave that these 65536 cycles (due to overflow) took roughly a tenth of a second.
This complicated pattern of recognizing D = E = 0 was done because circumstances restricted us to only the NZ, Z and C conditions of the JR instruction, and NZ was needed by both. So we figured this setup could translate a NZ into a C !
Note that even though the C register is very precious, it is modified through every iteration by the "inc bc". Yet, it is incremented 65536 = 256 * 256 times... so, the value before entry and after exit will be the same.

The extern loop increments C (which was trashed so much it was preserved :P), and returns when it is 0. As such, the higher C is set in the preamble, the less this extern loop re-loops. And there you go !

Then, the last two instructions. The jp (hl) jumps to $0100 (preserved from the preamble), performing the "reboot", while A is set to 1 The reason for this is that games functioning for both GB and GBC use the value of register A to determine the console they are being played on. $11 indicates GBC, and usually all other value are treated as B&W GB. (The GB ouputs a $1, which was also convenient, so I chose 1).
Because Pokémon R/B make the GBC boot in "B&W compatibility", we need games to boot as if on a GB.

Original post

I was browsing Twitter, and then I found this.

Now, I'm wondering : what if we ran a 8F payload that did this :
1. Disable ALL interrupts
2. Loop forever until something occurs (button press, I think)
3. Do some more stuff (enabling interrupts and whatnot :P)
And then, during step 2, we pulled off the Pokémon R/B/Y cartridge and plugged some other cart inside ?

I don't see why the console would crash, no I/O seems to R/W/J/X from ROM... soooo it just might be possible to transfer ACE's between games ! That'd be hella sweet.
But I don't know if there isn't a protection that freezes the CPU when the cartridge is removed ! That's the deal.

Ideas :
1. If it works, we'd just need to make two payloads and transfer one to a part of memory where it won't be overwritten... but first we need to know if this can theoretically work.
2. If the console stops, enable the Joypad interrupt ONLY, then run a STOP and trigger the interrupt once cartridges have been stop'n'swop'd. Since the Joypad interrupt points to a reti in R/B(/Y?), this should be fine.

8F setup that might work :
Code: [Select]
Item x[qty]
TM43 x4
Escape Rope x29
Repel x15
Lemonade x16
Hyper Potion x118
[More stuff]

Registers on startup :
af = 6300 [a=63, f=00]
bc = 22B8 [b=22, c=B8]
de= 0001 [d=00, e=01]
hl= D322 [h=D3, l=22]
All flags reset

di ; prevent bad code from being executed. Interrupts will stack requests in FFFF, but meh.
inc b ; filler
dec e ; de = $0000
dec de ; de = $FFFF
ld e, $0F ; de = $FF0F
ld a, $10
ld (de), a
; return to game code, perform ACE, write something to VRAM ?
Pages: [1] 2