Recently the ACE was leveraged into a Credits-Warp, this page is intended to collect information about it.
Proof of Concept
this video shows a proof of concept execution.
Note that the route was only tested on the German version, the principles apply to all PAL versions, minor adjustments might however be needed.
So far no ACE for the Japanese version has been found, so this whole page does not apply to NTSC
Also the validity of this on original cartridges is still unclear, it should however work on all emulators, sd2snes etc. (SRAM needs to be initialized to 0xFF for this to work reliably)
To reproduce the video, :
The savegame in the first slot needs to be manipulated in the following ways:
- Saved at Indus River after entering Indus from the world map (no death warping and saving without re-entering) - Filename of the saved game has to be "ZZQRZ" - When saving the game, the Filename in the third slot has to be "jhKpy" - Experience total has to be any of 9182, 9382, 9582 9782 or 9982 - Exactly the right chests and Magirocks need to be collected (check the video or lsnes movie file for exactly which)
When the savegame is prepared, the chicken glitch has to be performed, and the ACE triggered with specific buttons pressed in order to execute the payload embedded in the savegame:
- go to Shadowkeeper with 2 fire rings - make sure your xp is at a value where killing shadow keepers final phase will give you a level up (<90 or 170-269 before entering the fight should be the relevant ranges) - enter the chest with 0 hp, equip the magic chest, optionally remap y to "use item" and cast a fire ring that kills Shadowkeeper to achieve a "double kill" - the game will glitch out at when souls reenter the body. you now have to use the magic chest while pressing a specific set of buttons on both controllers: P1: Y select up right L P2: A L R - to achieve that: - press select to enter the chest - if not done yet, remap y to "use item" - press select to leave the chest, keeping select pressed - during the transition out of the chest press at least L on controller 1 and keep it pressed - press and keep pressed all buttons except Y. order, timing etc. does not matter here - finally press y to use the magic-chest and trigger the code-execution
If everything was done correctly the screen should turn black and the game freeze.
After resetting the console, a savegame should be seen which was saved in the glitched state (slot 2 atm but this may change with route development).
Now just load the glitched savegame, walk to Crysta, go to bed, GG!
When activating the magic ring menu during the chicken glitch the execution of gamecode will reach the autojoypad input registers ($4218 to $421f),
depending on input device configuration (multitap / no multitap) either $4218 (no multitap, 2 controllers) or $4219 (with multitap) will be executed as an opcode.
Withn two controllers plugged in without a multitap, when the bug is triggered with the keys pressed as outlined above, code execution will eventually reach the SRAM like this:
276000 sbc $ffffff,x  A:f9f9 X:421a Y:6c00 S:01e2 D:80f3 DB:81 NvmxdizC
Here it gets a little weird, because the game never uses the first $100 bytes of SRAM, so the state of those bytes is technically undefined.
However, as far as i know all the emulators, as well as Mister FPGA and SD2SNES initialize SRAM to 0xFF - which is what the payload expects.
I did not find any information about initalization of SRAM on actual cartridges and i lack the hardware to check for myself
I expect however, that the SRAM on physical cartridges is actually initialized to random values which will render this route broken or at least inconsistent across cartridges.
Additional testing and/or research will be required to figure out if this route actually works on real, un-tampered cartridges.
The savefile manipulations outlined above were made in a way so that the following parts do the following things:
save-location and bonepin location: nothing useful, just dont crash, or jump away, continuing execution into the following savefile names savefile names: set the direct-page register to a value that allows adressing a specific memory location with a single byte in the later stages jump over uncontrollable memory to total XP total XP: jump over parts of the savefile to reach the chestflags safely chest flags: set event flags indicating we killed dark gaia load constant into the accumulator jump over some bytes towards magirock flags magirock flags: jump into subroutine that saves the game into the slot specified by the accumulator
detailed description of execution of the savegame
when SRAM is initalized to 0xFF, execution will reach the beginning of the Savegame in Slot 1 like this:
276100 sta $0103  A:df8e X:421a Y:6c00 S:01e2 D:80f3 DB:81 Nvmxdizc
the relevant details are that bits 9 and 10 in the accumulator are set, that the direct page register is set to $80f3, and Y is set to $6c00
execution proceeds through the first 16 bytes, which are determined by save-location and last overworld-map position (possibly bonepin-marker)
8d 03 01 00 VV 01 WW 01 02 00 XX 00 YY 04 ZZ 06
VV and WW are variable depending on pixel position when saving, but the values are irrelevant to us
XX YY and ZZ are variable depending on how you enter indus river, but the values are irrelevant to us
sta $0103 brk #$VV ora ($WW,x) ora ($02,x) brk #$XX brk #$YY tsb $ZZ asl $3a -- operand $3a is actually the first byte of the filename, also it does not matter what the first character is
none of the executed instructions so far are relevant to the following payload
name of our save (names are always terminated with d4)
(3a) 3a 31 32 3a d4
sta $0103 dec a # does nothing useful - any 1-byte instruction should be fine here and ($32),y # this will AND the accumulator with #$0600 and thereby set it to #$0600 dec a # decrement accumulator to #$05ff pei ($00) # the terminating d4, basically a noop
some null bytes between the savefile names
(00) 00 00 00 00 00
brk #$00 brk #$00 brk #$4a # operand here is again the first byte of the name, and can be anything
all basically noops but again costing us the first byte of the name
name of save in 3rd slot
(4a) 48 2b 50 59 d4
pha # push accumulator (#$05ff) pld # pull direct page register -> D: #$05ff # we have now set the direct page register to #$05ff, enabling following payload to adress relevant event flags with a single byte bvc IP+#$59 # we now jump forward as fas as possible to skip a lot of stuff that we cannot properly manipulate (e.g. frame counter)
execution lands in a bunch of $00 bytes that work out to noops once again
then execution reaches experience total which we manipulated in order to jump over even more junk
the second byte here can be 91,93,95,97 or 99 because we again will land in a bunch of $00 bytes and dint have to hit any specific byte, just the alignment is important
after a couple of $00 bytes, we hit one stray $07
but this works out to be a noop for us as well
some more $00 bytes and finally we reach the main payload, the chest flags:
c6 f6 03 00 a9 01 00 c0 00 00 80 40
dec $f6 # this makes use of our prepared direct page register to decrement $0006f5, underflowing from $0000 to $ffff and thereby setting our relevant event flag ora $00,s # just work around restrictions in how we can set chestflags, basically a 2-byte noop lda #$0001 # load 1 in the accumulator, this will determine in which saveslot we will save, a chest could be skipped if one wants to overwrite slot 1, which i did not want for the POC cpy #$0000 # again just working around restrictions, basically a 3-byte noop bra IP+#$40 # one last time we jump over some junk bytes to reach the final part of the payload
after some more $00 bytes we reach the magirock flags:
08 22 d7 87 07
php # one-byte opcode for alignment jsl $0787d7 # jump into the routine that will save the current game into the slot indicated by the value in the accumulator
the code will now continue to save our game with the manipulated event-flags, indicating that we have already beaten dark-gaia
afterwards the code will continue to execute sram until it crashes or gets locked up - however after a reset of the console the glitched savegame persists