Secret of Evermore/Alchemy RAM manipulation: Difference between revisions

From Data Crystal
Jump to navigation Jump to search
No edit summary
No edit summary
Line 188: Line 188:
e2 00 00 00 60 01 a7 80 a7 00 00 c3 7f 00 12 00
e2 00 00 00 60 01 a7 80 a7 00 00 c3 7f 00 12 00
0d 00 00 00 10 00 27 00 03 00 74 02 ?? 4? ?? 0?
0d 00 00 00 10 00 27 00 03 00 74 02 ?? 4? ?? 0?
1? 00 0? 00 ?? 00 ?? 0? 7f xx_xx xx_xx ?? ?? c7
1? 00 0? 00 ?? 00 xx_xx 7f xx_xx xx_xx ?? ?? c7
00 00 00 00 00 00 ?? ?? 10 ?? 0? 10 50 36 00 00
00 00 00 00 00 00 ?? ?? 10 ?? 0? 10 50 36 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Line 203: Line 203:
* x can be manipulated
* x can be manipulated
* ? is pseudo random (Current script)
* ? is pseudo random (Current script)
|-
| $9101xx
| <pre>
xx_xx_xx_xx xx_xx 0f 00 00 00 00 00 04 00 b8 02
17 01 2d 02 df 00 00 00 ff ff 00 00 ff 00 ff 00
ff 00 00 00 00 8b 85 80 00 00 80 8b 85 80 00 00
00 00 00 93 50 46 91 fe 92 23 1b 78 00 78 00 80
64 a0 7f 32 0c 1a 00 80 64 a0 7f 32 0c 1a 00 80
64 a0 7f 32 0c 1a 00 80 64 a0 7f 32 0c 1a 00 80
64 a0 7f 32 0c 1a 00 80 64 a0 7f 32 0c 1a 00 80
...
</pre>
| See $9100xx
|-
|-
| $91ffxx
| $91ffxx
Line 232: Line 245:
! Values
! Values
! Comment
! Comment
|-
| $0056-$0057
| ?
| Unknown purpuse, but any moving entity changes this value deterministically (Therefore hard to manipulate)
Coordinates over $0FFF don't exist!
|-
|-
| $0059-$005A
| $0059-$005A
| $0000-$0620
| $0000-$0620 (Utopia map)
| Copy of the camera offset X (Can be pause buffered)
| Copy of the camera offset X (Can be pause buffered)
Values over $0FFF don't exist
Coordinates over $0FFF don't exist!
|-
|-
| $005B-$005C
| $005B-$005C
| $0000-$0440
| $0000-$0440 (Utopia map)
| Copy of the camera offset Y (See $0059-$005A)
| Y (See $0059-$005A)
|-
|-
| $0082
| $0082
Line 259: Line 277:
| Most recent ring menu ($93 and $94 get cleared)
| Most recent ring menu ($93 and $94 get cleared)
Other menus can't be closed without entering the boys or dogs ring menu
Other menus can't be closed without entering the boys or dogs ring menu
|-
! colspan="3" | Slightly outside $9100FF
|-
| $0100-$0103
| $00000000-$FFFFFFFF
| Frame counter
|-
| $0104-$0105
| ?
| Controller inputs (Player 1)
|}
|}



Revision as of 14:50, 5 January 2020

This is a sub-page of Secret of Evermore.

Affects of the Alchemy Crash to a Player

Details

Usually there are only up to 2-3 spells active, because the game limits how many can be cast:

  • The boy has an internal cooldown on opening the ring menu after using alchemy
  • Bosses have some kind of internal cooldown
  • Only 8 alchemy spells per type can be active at any given moment
    • Particle type alchemy (like Flash and Fireball)
    • Animation type alchemy (like Crush and Acid Rain)

But there are known ways to circumvent these limitations:

  • Opening the boys ring menu as the dog can be done once per frame (Also known as 8cast, because the same spell can be cast up to 8 times)
  • Bosses cast their spells regardless of the limit (See Enemies Casting Spells)
    • Magmar casts at a certain damage threshold Heat Wave, which is an animation spell
    • Aquagoth randomly casts Lightning Storm and other spells, which are animation spells
    • Verminator randomly casts Acid Rain and other spells, which is are animation spells

Additional facts:

  • The 2x8 alchemy slots aren't cleared once the spell has been resolved, they are just flagged as inactive (Leaving the game overrides most memory with zeros, though)

Crash

Once the game tries to put the 9th alchemy spell in a slot the game somewhat freezes:

  • The game no longer progresses states
    • User inputs are blocked
    • Spell projectiles stop moving
    • Enemies stop moving
  • The music keeps playing the most recent loop (because it's not linked to the hardware)

In very rare cases the game does not crash and the following effects may occur based on what memory is being executed:

  • Severe visual glitches
    • Black screen
    • Repeating patterns
    • Corrupted HUD
    • Corrupted background
  • Side effects to the projectile
    • Animation alchemy like healing is randomly applied (E.g. Healing)
    • The projectile might fly in random directions (E.g. orbiting the boy, or flying outside the screen)

Reproducing the Crash

  • Cast a projectile alchemy spell near the exit of a map
  • Leave the screen instantly (before the projectile has been processed)
  • Unfinished projectiles are being flagged as "currently active" and cannot be reused anymore
    • This also circumvents the "only 8 per school" limitation, because it uses a separated stack
  • Once the 9th projectile is being cast the game "freezes"

Manipulating Memory

Details on the Crash

Casting 6 Hard Balls in the transition leads to the game crashing on the next 3+cast, which in almost all cases looks the same:

9198f7 ldx $0014,y   [7e3378] A:000a X:0000 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 72 H:226 F:48
9198fa beq $9907     [919907] A:000a X:d22c Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 72 H:236 F:48
9198fc lda $0028,y   [7e338c] A:000a X:d22c Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 72 H:239 F:48
9198ff sta $4c       [00004c] A:4e89 X:d22c Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 72 H:249 F:48
919901 jsl $919750   [919750] A:4e89 X:d22c Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 72 H:256 F:48
919750 lda $910000,x [91d22c] A:4e89 X:d22c Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 72 H:270 F:48
919754 tax                    A:800a X:d22c Y:3364 S:1fee D:0000 DB:7e NvmxdizC V: 72 H:279 F:48
919755 jsr ($8000,x) [91000a] A:800a X:800a Y:3364 S:1fee D:0000 DB:7e NvmxdizC V: 72 H:282 F:48
00885f stp                    A:800a X:800a Y:3364 S:1fe8 D:0000 DB:7e NvmxdIzC V: 72 H:312 F:48
Address/Value Register/Usage Comment
$7E3378 (16 bit) X Is the 14th and 15th byte of the first animation alchemy slot ($7E3364-$7E3563, 40 bytes per slot)
$7E338c (16 bit) A Is the 28th and 29th byte of the first animation alchemy slot ($7E3364-$7E3563, 40 bytes per slot)
$910000 Base jump address for both X values Bank $91 mirrors bank $11 ($0000-$1FFF refer to LowRAM)
$8000 Final jump offset -

Which means that the jump address can be altered, by altering the first animation alchemy slot.

  • Store 2 bytes from $7E3378 in X_alchemy
    • $7E3378 happens to be the 14th and 15th byte of the first animation alchemy slot
    • MSB: $7E3379 (Seems to be some kind of timer which advances after $7E3378 overflows)
    • LSB: $7E3378 (Seems to be some kind of timer which advances after each partial animation)
  • Read 2 bytes from $910000+X_alchemy
    • X_alchemy=$0000-$1FFF happens to be LowRAM
    • MSB: $910000+X_alchemy+1 refers to $7E000+X_alchemy+1
    • LSB: $910000+X_alchemyrefers to $7E000+X_alchemy
  • Store that new value in X_ram
  • Jump to $918000+X_ram

This leads to the following formula: (Random value for X_alchemy)

[$7E3378] = 0x002B = X_alchemy
Jump
[$91002B] = 0x8080 = X_ram (Has an offset of 0x8000)
Jump with offset
[$910080] = 0xBFFFFE = FE FF BF = Code (Opcode=FE, Argument=BFFF)
Execute code

Or shorter:

[7E_3378]=0x002B -> [91_002B]=0x8080 -8k-> [91_0080]=00BFFFFE (opcode=FE, argument=BFFF) 	

Therefore the code can be manipulated by altering the indirect, indirect (with offset) jump.

Manipulating X_alchemy

To this point it's not known if X_alchemy can be overwritten at the right moment. Therefore it has to be routed with RNG first.

Examples:

Seed=$D22C:

[7E_3378]=0xD22C -> [91_D22C]=0x800A -8k-> [91_000A]="???" (In LowRAM)
9198f7 ldx $0014,y   [7e3378] A:000a X:0000 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 72 H:226 F:48
9198fa beq $9907     [919907] A:000a X:d22c Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 72 H:236 F:48
9198fc lda $0028,y   [7e338c] A:000a X:d22c Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 72 H:239 F:48
9198ff sta $4c       [00004c] A:4e89 X:d22c Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 72 H:249 F:48
919901 jsl $919750   [919750] A:4e89 X:d22c Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 72 H:256 F:48
919750 lda $910000,x [91d22c] A:4e89 X:d22c Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 72 H:270 F:48
919754 tax                    A:800a X:d22c Y:3364 S:1fee D:0000 DB:7e NvmxdizC V: 72 H:279 F:48
919755 jsr ($8000,x) [91000a] A:800a X:800a Y:3364 S:1fee D:0000 DB:7e NvmxdizC V: 72 H:282 F:48

00885f stp                    A:800a X:800a Y:3364 S:1fe8 D:0000 DB:7e NvmxdIzC V: 72 H:312 F:48

Seed=$0003:

[7E_3378]=0x0003 -> [91_0003]=0xC900 -8k-> [91_5900]="???" (In Unused)
9198f7 ldx $0014,y   [7e3378] A:000a X:0000 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 45 H:176 F:31
9198fa beq $9907     [919907] A:000a X:0003 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 45 H:186 F:31
9198fc lda $0028,y   [7e338c] A:000a X:0003 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 45 H:189 F:31
9198ff sta $4c       [00004c] A:4e89 X:0003 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 45 H:199 F:31
919901 jsl $919750   [919750] A:4e89 X:0003 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 45 H:206 F:31
919750 lda $910000,x [910003] A:4e89 X:0003 Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 45 H:220 F:31
919754 tax                    A:c900 X:0003 Y:3364 S:1fee D:0000 DB:7e NvmxdizC V: 45 H:230 F:31
919755 jsr ($8000,x) [914900] A:c900 X:c900 Y:3364 S:1fee D:0000 DB:7e NvmxdizC V: 45 H:233 F:31

918080 iny                    A:c900 X:c900 Y:3364 S:1fec D:0000 DB:7e NvmxdizC V: 45 H:246 F:31
918081 sta $a841,y   [7edba6] A:c900 X:c900 Y:3365 S:1fec D:0000 DB:7e nvmxdizC V: 45 H:249 F:31
918084 stx $a7       [0000a7] A:c900 X:c900 Y:3365 S:1fec D:0000 DB:7e nvmxdizC V: 45 H:259 F:31
918086 ora ($ab),y   [7edaf8] A:c900 X:c900 Y:3365 S:1fec D:0000 DB:7e nvmxdizC V: 45 H:266 F:31
918088 rti                    A:c900 X:c900 Y:3365 S:1fec D:0000 DB:7e NvmxdizC V: 45 H:278 F:31
00885f stp                    A:c900 X:0000 Y:0065 S:1fec D:0000 DB:7e nVmXdIZC V: 45 H:307 F:31

With RNG

Based on yet unknown factors seemingly random values between $0000 and $FFFF are being used. That leads to different jumps for every X_alchemy value, unless the RNG has been locked.

Observed patterns:

  • $7E3379 seems to be either $00 or $FF, most of the time
    • $00-$1F are LowRAM ($01-1F contain relevant values)
  • $7E3378
    • Seems to be rather random

This leads to mostly to two specific memory blocks:

Memory Values Comment
$9100xx
00 00 ?? 00 ?? ?? ?? ?0 0? 00 00 00 ?? ?? 0? 00
64 35 ?? 01 ?? 51 e8 00 c9 00 3? 00 ?? ?? 00 00
00 00 f? ?? ?? 00 ?? ?? c? 8e ?? 80 ?? 00 ?? 0?
e2 00 00 00 60 01 a7 80 a7 00 00 c3 7f 00 12 00
0d 00 00 00 10 00 27 00 03 00 74 02 ?? 4? ?? 0?
1? 00 0? 00 ?? 00 xx_xx 7f xx_xx xx_xx ?? ?? c7
00 00 00 00 00 00 ?? ?? 10 ?? 0? 10 50 36 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
fe ff xx 00 00 00 00 00 00 00 01 00 80 a7 09 04
00 00 02 0c c4 34 08 xx 99 8e ff 00 00 00 85 14
00 00 8a 00 00 00 00 e4 81 a7 4f 93 a7 2e 13 7f
00 00 a7 00 00 00 00 00 00 00 00 00 00 00 00 70
02 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 ff 01 40 96 0? 00 ?? 00 ?? 0? ??
00 ?? 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
LowRAM contains a lot of volatile values:
  • x can be manipulated
  • ? is pseudo random (Current script)
$9101xx
xx_xx_xx_xx xx_xx 0f 00 00 00 00 00 04 00 b8 02
17 01 2d 02 df 00 00 00 ff ff 00 00 ff 00 ff 00
ff 00 00 00 00 8b 85 80 00 00 80 8b 85 80 00 00
00 00 00 93 50 46 91 fe 92 23 1b 78 00 78 00 80
64 a0 7f 32 0c 1a 00 80 64 a0 7f 32 0c 1a 00 80
64 a0 7f 32 0c 1a 00 80 64 a0 7f 32 0c 1a 00 80
64 a0 7f 32 0c 1a 00 80 64 a0 7f 32 0c 1a 00 80
...
See $9100xx
$91ffxx
00 01 00 c0 00 00 00 00 00 00 00 80 00 00 07 00
1f 00 01 00 c0 00 00 00 00 00 00 00 80 80 00 e0
00 80 00 00 00 00 00 03 00 00 00 00 00 00 80 00
e0 00 80 00 00 00 00 00 03 00 00 00 00 e0 00 80
00 00 00 00 00 07 00 00 00 00 00 00 00 00 e0 00
80 00 00 00 00 00 07 00 00 00 00 00 00 00 00 00
00 00 00 e0 00 f0 00 00 00 00 00 00 00 00 00 00
00 00 00 00 e0 00 f0 00 00 00 00 00 00 3f 03 00
00 00 00 00 00 00 03 00 1f 00 01 00 00 00 00 03
00 00 00 00 00 00 00 03 00 1f 00 01 00 00 80 00
00 00 00 00 00 00 c0 00 f0 00 c0 00 00 00 00 80
00 00 00 00 00 00 00 c0 00 f0 00 c0 00 00 00 00
00 00 00 00 00 00 80 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 1c 00 70 00 00 00 00 00
00 00 00 00 00 00 00 00 00 1c 00 70 00 00 00 08
ROM

Bytes that can be manipulated:

Address Values Comment
$0056-$0057 ? Unknown purpuse, but any moving entity changes this value deterministically (Therefore hard to manipulate)

Coordinates over $0FFF don't exist!

$0059-$005A $0000-$0620 (Utopia map) Copy of the camera offset X (Can be pause buffered)

Coordinates over $0FFF don't exist!

$005B-$005C $0000-$0440 (Utopia map) Y (See $0059-$005A)
$0082 $00-$FF? Lower half of the pointer to the most recent script ($83 and $84 get cleared)
$0095
  • Can be used:
    • $8C (Boy)
    • $98 (Dog)
    • $C2 (Call Beads - Choose spell)
  • Can't be used:
    • $AC (Buy ingredients)
    • $B0 (Call Beads - Choose person)
    • $9C (Buy consumables)
    • $A4 (Sell consumables)
Most recent ring menu ($93 and $94 get cleared)

Other menus can't be closed without entering the boys or dogs ring menu

Slightly outside $9100FF
$0100-$0103 $00000000-$FFFFFFFF Frame counter
$0104-$0105 ? Controller inputs (Player 1)

X_alchemy values that load specific values as X_ram:

X_alchemy X_ram Comment
$0081 $xxFF See $0082
$0082 $00xx $82 can be manipulated by triggering scripts
$0094 $(8C,98,C2)00 See $0095
$0095 $00(8C,98,C2) $95 can be manipulated by closing ring menus
? $8100 Frame counter ($0100)
$0002, $0018, $001C, $0054, $00DB, $00E1, etc. $xx00 Wildcards (Very hard to manipulate)

Example: Enforce $0100 with the table

Possible X_alchemy values: $910089, $91ff00, $91ff11, $91ff89, $91ff9A

Seed=$FF89:

[7E_3378]=0xFF89 -> [91_FF89]=0x0100 -8k-> [91_8100]="???" (In ??)
; missing ldx line
9198fa beq $9907     [919907] A:000a X:ff89 Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 60 H: 26 F:48
9198fc lda $0028,y   [7e338c] A:000a X:ff89 Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 60 H: 29 F:48
9198ff sta $4c       [00004c] A:4e89 X:ff89 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 60 H: 39 F:48
919901 jsl $919750   [919750] A:4e89 X:ff89 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 60 H: 46 F:48
919750 lda $910000,x [91ff89] A:4e89 X:ff89 Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 60 H: 60 F:48
919754 tax                    A:0100 X:ff89 Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 60 H: 69 F:48
919755 jsr ($8000,x) [918100] A:0100 X:0100 Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 60 H: 72 F:48
918416 bit $00,x     [000100] A:0100 X:0100 Y:3364 S:1fec D:0000 DB:7e nvmxdizC V: 60 H: 85 F:48
918418 eor ($00)     [7e0000] A:0100 X:0100 Y:3364 S:1fec D:0000 DB:7e nvmxdiZC V: 60 H: 93 F:48
91841a bpl $841c     [91841c] A:0100 X:0100 Y:3364 S:1fec D:0000 DB:7e nvmxdizC V: 60 H:104 F:48
91841c cop #$00               A:0100 X:0100 Y:3364 S:1fec D:0000 DB:7e nvmxdizC V: 60 H:109 F:48
00885e stp                    A:0100 X:0100 Y:3364 S:1fe8 D:0000 DB:7e nvmxdIzC V: 60 H:124 F:48

With Alchemy

Usually X can be altered by casting animation based alchemy, but not during the crash.

There is no proof yet, but the memory behind X_alchemy can also be set by casting an animation. Items are also included in this list, because they are treated as animation spell.

This is done by casting them and waiting for the sub animation of interest to set the memory value:

Alchemy Possible X_alchemy Values Comment
Acid Rain $86F0, $86F2, $86F8, $8716, $871C, $871E, $8722, $8726 -
Atlas $8B2A, $8B2C, $8B32, $8B44, $8B48, $8B4A, $8B50, $8B52 -
Barrier $944C, $944E, $9452, $9462, $9466, $9474 -
Cure $85D8, $85DA, $85DC, $85E2, $85F2, $86C6, $86CC, $86CE, $86D2, $86E2 -
Crush $8A9C, $8A9E, $8AA4, $8AAE, $8ABA, $8B04, $8B0C -
Corrosion $8F20, $8F22, $8F2C, $8F42, $8F48, $8F4A, $8F54, $8F5A, $8F5C, $8F46, $8F6A, $8F78, $8F7E, $8F80, $8F8A -
Defend $872A, $872C, $8732, $8740, $8744 -
Double Drain $9362, $9364, $9376, $937E, $9380, $9382, $938E, $9390, $9446 -
Drain $89B4, $89B6, $89C8, $89D0, $89D2, $89D4, $89E0, $89E2 -
Energize $9610, $9612, $9616, $961C -
Escape $8E22, $8E24, $8E2C -
Explosion $90D2, $90D4, $90DA, $90EA, $90FE, $91BE, $91C0, $91C4, $91CA -
Fireball - -
Fire Power $8F8E, $8F90, $8F92, $8F9A, $8FA6, $8FA8, $8FAA $9068, $9070 -
Flash - -
Force Field $962A, $962C, $9630, $9636 -
Heal $886C, $886E, $8872, $8880 $894E, $895E, $8960, $8964, $8968 -
Lance $8E36, $8E38, $8E3A, $8E40, $8E4E $8F14, $8F1E -
Levitate $896E, $8970, $8974, $897E, $8982, $8984 Can only be cast once per rock
Lightning Storm $91CC, $91CE, $91D4, $91Da, $91E4 -
Miracle Cure $920C, $920E, $9212, $9226, $9230, $9240, $9242, $9246, $9254, $925A, $925C, $9268 -
Nitro $94F6, $94F8, $94FE, $9508, $9522, $95D8 -
One Up $9072, $9074, $907A, $9080, $909E, $90A0, $90A4 -
Reflect $95DC, $95DE, $95E4, $95EA -
Regrowth $90AE, $90B0, $90B4, $90C2 -
Reveal ? Can only be cast in act 2
Revive $8B60, $8B62, $8B66, $8C34, $8C4A Requires the dog to be dead
Slow Burn $91E6, $91E8, $91F2, $9202 -
Speed $8988, $898A, $898E, $899C, $89A4 -
Sting $8B10, $8B12, $8B18, $8B1E, $8B26 -
Stop $94D6, $94D8, $94DE, $94E8 -
Super Heal $947E, $9480, $9484, $9492, $9494, $949E, $94A8, $94BE, $94C8, $94CA, $94CE -
Storm $81C0, $81C2, $81C8, $81CE, $81D8 -
Life Spark $81DA, $81DC, $81E2, $81EE, $81F0, $81F8 -
Flare - -
Heat Wave $8164, $8166, $816C, $817E, $818A, $818C, $8198, $819A, $81A4, $81AA, $81AC, $81B6, $81CB -
Time Warp $8200, $8202, $8208, $823E, $8244, $8246, $825C -
First Aid $8270, $8272, $8278, $8284, $828A, $8294, $829E, $82B4, $82BE, $82C0, $82C4, $82CA -
Confound $82CE, $82D0, $82D6, $82E0, $82F4, $82FE, $8300, $8304 -
Plague $8416, $8418, $8420, $842A, $8440, $8432, $845C, $8474, $847E, $8480, $8484, $849C, $84A2, $84A4 -
Hypnotize $84AC, $84AE, $84B4, $84E8, $84EA, $8500 -
Shock Wave $8374, $8376, $837C, $8390, $8392, $839C, $839E, $83A8, $83AA, $83B4, $83B6, $83C0, $83C2, $83CC, $83CE, $83D8, $83DA, $83E4, $83E6, $83F0, $83F2, $83F6 -
Electra-Bolt $85B0, $85B2, $85B8, $85C6, $85D0, $85D2, $85D8 -
Disrupt $8512, $8514, $851A, $8524, $8538, $8542, $8544, $8548, $854E -
Dog Biscuit $96D8, $96DA, $96DE, $9718, $972E Requires the dog to be dead
Essence $9644, $9646, $964A, $965C, $9674, $967A, $967C, $9680 -
Honey $9690, $9692, $9696, $96A4, $96B8, $96C8, $96CA, $96CE, $96D2 -
Nectar (See Honey) -
Petal (See Honey) -
Pixie Dust $830E, $8310, $8314, $8324, $8328, $8336, $8344 -
Wings $8E22, $8E24, $8E2C -

Desired Values for X_ram

X_alchemy just has to find a good X_ram value. (It's hard to make a list of #FFFF values, which are partially dynamic)

The offset of $918000 leads to the following jumps for X_ram:

X_ram Value Bank Addresses Comment
$0000-$7FFF $11 $8000-$FFFF HiROM section (program memory)
$8000-$9FFF $12 $0000-$1FFF LowRAM, shadowed from bank $7E
$A000-$A0FF $12 $2000–$20FF Unused
$A100–$A1FF $12 $2100–$21FF PPU1, APU, hardware registers
$A200–$AFFF $12 $2200–$2FFF Unused
$B000–$BFFF $12 $3000–$3FFF DSP, SuperFX, hardware registers
$C000–$C0FF $12 $4000–$40FF Old Style Joypad Registers
$C100–$C1FF $12 $4100–$41FF Unused
$C200–$C4FF $12 $4200–$44FF DMA, PPU2, hardware registers
$C500–$DFFF $12 $4500–$5FFF Unused
$E000–$FFFF $12 $6000–$7FFF RESERVED

LowRAM is the easiest memory to manipulate and contains promising values:

X_ram Value LowRAM Address Comment
$8A3F-$8A40 $0A3F-$0A40 Boy - Attack (Roughly $0000-$00FF and $FF00-$FFFF)
$8A41-$8A42 $0A41-$0A42 Boy - Defense (Roughly $0000-$00FF and $FF00-$FFFF)
$8AC6-$8AC8 $0AC6-$0AC8 Money - Talons
$8AC9-$8ACB $0AC9-$0ACB Money - Jewels
$8ACC-$8ACE $0ACC-$0ACE Money - Gold Coins
$8ACF-$8AD1 $0ACF-$0AD1 Money - Credits
$B218-? $4218-? Controller inputs

Routing the Exploit

Setup

TODO

Examples for Executing Code

Arbitrary example of code that shall be executed:

Instruction Comment
5C 18 42 00 Long jump to $004218 (Without inputs this will crash, because $00 can't be executed)
004218 brk #$00
4C 18 42 Jump to $914218 (Same as above, but with fewer Bytes)
914218 brk #$00

The following methods are only theories were tested with injected X_alchemy values. That implies, that a setup has to be found first.

Currencies

The currencies contain 12 Bytes, which can be manipulated by killing enemies and buying items:

7E0AC6 to 7E0AC8 = Money - Talons
7E0AC9 to 7E0ACB = Money - Jewels
7E0ACC to 7E0ACE = Money - Gold Coins
7E0ACF to 7E0AD1 = Money - Credits
Address Alias Value Comment
$7E3378 $AnimationAlchemySlot0Byte14 $SemiRandomNumber Somewhat based on the animation of the 9th particle
$7E0AC6 $Talon C9 8A XX 35,529 Talons:

$Jewels with an offset of 8000

$7E0AC9 $Jewels CC 0A XX 2,764 Jewels:

$GoldCoins

$7E0ACC $GoldCoins 5C 18 42 4,331,612 Gold Coins:

Arbitrary code 1: Long jump ($5C) to controller inputs ($4218)

$7E0ACF $Credits 00 XX XX 0 Credits:

Arbitrary code 2: The chosen example requires a 4th Byte

$004218 $ControllerInputs $ArbitraryCode Can be altered by pressing buttons and is usually used for Arbitrary Code Execution

Seed=$0AC6: (Manually injected)

[7E_FF89]=0x0AC6 -> [91_0AC6]=0x8AC9 -8k-> [91_0AC9]="JSL 004218" (Long jump to $004218)
; x_alchemy=$SemiRandomNumber ([AnimationAlchemySlot0Byte14])
9198f7 ldx $0014,y   [7e3378] A:000a X:0000 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 60 H:167 F: 9

; x_alchemy=$Talon (Injected $Talon, for testing)
9198fa beq $9907     [919907] A:000a X:ffb8 Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 60 H:177 F: 9

9198fc lda $0028,y   [7e338c] A:000a X:0ac6 Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 60 H:180 F: 9
9198ff sta $4c       [00004c] A:4e89 X:0ac6 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 60 H:190 F: 9
919901 jsl $919750   [919750] A:4e89 X:0ac6 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 60 H:197 F: 9

; A=$Jewels ([Talon], including an 8k offset)
919750 lda $910000,x [910ac6] A:4e89 X:0ac6 Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 60 H:210 F: 9

; x_ram=A
919754 tax                    A:8ac9 X:0ac6 Y:3364 S:1fee D:0000 DB:7e NvmxdizC V: 60 H:220 F: 9

; jump to $GoldCoins ($Jewels)
919755 jsr ($8000,x) [910ac9] A:8ac9 X:8ac9 Y:3364 S:1fee D:0000 DB:7e NvmxdizC V: 60 H:223 F: 9

; jump to $ControllerInputs ($GoldCoins)
910acc jml $004218   [004218] A:8ac9 X:8ac9 Y:3364 S:1fec D:0000 DB:7e NvmxdizC V: 60 H:237 F: 9

004218 brk #$00               A:8ac9 X:8ac9 Y:3364 S:1fec D:0000 DB:7e NvmxdizC V: 60 H:245 F: 9
00885f stp                    A:8ac9 X:8ac9 Y:3364 S:1fe8 D:0000 DB:7e NvmxdIzC V: 60 H:260 F: 9

Similar Crashes

Enemies Casting Spells

Reproducing the Crash:

  • Damage Magmar if neccessary (Heat Wave is triggered by a damage threshold)
  • Cast 8 animation spells via 8cast (E.g. Storm from Fire Eyes Call Beads)
  • Magmar will cast Heat Wave in response to the damage
  • The game tries to adds a 9th animation spell in a slot (Sometimes refereed to as 9cast)
  • The game "freezes"

Executed code:

8cd01f ldy $2f28,x   [7e2f2c] A:b912 X:0004 Y:0002 S:1ff1 D:0000 DB:7e nvmxdizc V:  0 H:311 F: 7
8cd022 bne $d011     [8cd011] A:b912 X:0004 Y:29e9 S:1ff1 D:0000 DB:7e nvmxdizc V:  0 H:321 F: 7
8cd011 stx $80       [000080] A:b912 X:0004 Y:29e9 S:1ff1 D:0000 DB:7e nvmxdizc V:  0 H:325 F: 7
8cd013 sty $7e       [00007e] A:b912 X:0004 Y:29e9 S:1ff1 D:0000 DB:7e nvmxdizc V:  0 H:332 F: 7
8cd015 ldx $0003,y   [7e29ec] A:b912 X:0004 Y:29e9 S:1ff1 D:0000 DB:7e nvmxdizc V:  0 H:339 F: 7
8cd018 jsr ($d029,x) [8cd02b] A:b912 X:0002 Y:29e9 S:1ff1 D:0000 DB:7e nvmxdizc V:  1 H:  9 F: 7
8cd09c lda $0000,y   [7e29e9] A:b912 X:0002 Y:29e9 S:1fef D:0000 DB:7e nvmxdizc V:  1 H: 22 F: 7
8cd09f sta $82       [000082] A:f404 X:0002 Y:29e9 S:1fef D:0000 DB:7e Nvmxdizc V:  1 H: 32 F: 7
8cd0a1 lda $0001,y   [7e29ea] A:f404 X:0002 Y:29e9 S:1fef D:0000 DB:7e Nvmxdizc V:  1 H: 39 F: 7
8cd0a4 sta $83       [000083] A:95f4 X:0002 Y:29e9 S:1fef D:0000 DB:7e Nvmxdizc V:  1 H: 49 F: 7
8cd0a6 lda [$82]     [95f404] A:95f4 X:0002 Y:29e9 S:1fef D:0000 DB:7e Nvmxdizc V:  1 H: 56 F: 7
8cd0a8 inc $82       [000082] A:f4d6 X:0002 Y:29e9 S:1fef D:0000 DB:7e Nvmxdizc V:  1 H: 68 F: 7
8cd0aa and #$00ff             A:f4d6 X:0002 Y:29e9 S:1fef D:0000 DB:7e Nvmxdizc V:  1 H: 80 F: 7
8cd0ad asl a                  A:00d6 X:0002 Y:29e9 S:1fef D:0000 DB:7e nvmxdizc V:  1 H: 85 F: 7
8cd0ae tax                    A:01ac X:0002 Y:29e9 S:1fef D:0000 DB:7e nvmxdizc V:  1 H: 88 F: 7
8cd0af jsr ($e8bd,x) [8cea69] A:01ac X:01ac Y:29e9 S:1fef D:0000 DB:7e nvmxdizc V:  1 H: 91 F: 7

8c0285 bpl $0287     [8c0287] A:01ac X:01ac Y:29e9 S:1fed D:0000 DB:7e nvmxdizc V:  1 H:104 F: 7
8c0287 bra $0307     [8c0307] A:01ac X:01ac Y:29e9 S:1fed D:0000 DB:7e nvmxdizc V:  1 H:109 F: 7
8c0307 bra $037d     [8c037d] A:01ac X:01ac Y:29e9 S:1fed D:0000 DB:7e nvmxdizc V:  1 H:115 F: 7
8c037d ora $00,s     [001fed] A:01ac X:01ac Y:29e9 S:1fed D:0000 DB:7e nvmxdizc V:  1 H:120 F: 7
8c037f ora ($00,x)   [7e4043] A:b1ad X:01ac Y:29e9 S:1fed D:0000 DB:7e Nvmxdizc V:  1 H:130 F: 7
8c0381 brk #$00               A:b1ad X:01ac Y:29e9 S:1fed D:0000 DB:7e Nvmxdizc V:  1 H:153 F: 7
00885f stp                    A:b1ad X:01ac Y:29e9 S:1fe9 D:0000 DB:7e NvmxdIzc V:  1 H:169 F: 7
8cea4f lda [$82]     [94b92a] A:0001 X:0062 Y:0089 S:1fea D:0000 DB:7e nvMXdizc V:254 H:216 F:29
8cea51 inc $82       [000082] A:0078 X:0062 Y:0089 S:1fea D:0000 DB:7e nvMXdizc V:254 H:227 F:29
8cea53 bne $ea57     [8cea57] A:0078 X:0062 Y:0089 S:1fea D:0000 DB:7e nvMXdizc V:254 H:235 F:29
8cea57 asl a                  A:0078 X:0062 Y:0089 S:1fea D:0000 DB:7e nvMXdizc V:254 H:240 F:29
8cea58 rol $8a       [00008a] A:00f0 X:0062 Y:0089 S:1fea D:0000 DB:7e NvMXdizc V:254 H:243 F:29
8cea5a tax                    A:00f0 X:0062 Y:0089 S:1fea D:0000 DB:7e nvMXdiZc V:254 H:251 F:29
8cea5b rep #$30               A:00f0 X:00f0 Y:0089 S:1fea D:0000 DB:7e NvMXdizc V:254 H:254 F:29
8cea5d jmp ($f041,x) [8cf131] A:00f0 X:00f0 Y:0089 S:1fea D:0000 DB:7e Nvmxdizc V:254 H:259 F:29
8cf03a sta $99a8,y   [7e9a31] A:00f0 X:00f0 Y:0089 S:1fea D:0000 DB:7e Nvmxdizc V:254 H:268 F:29

8cf03d brk #$00               A:00f0 X:00f0 Y:0089 S:1fea D:0000 DB:7e Nvmxdizc V:254 H:278 F:29
00885f stp                    A:00f0 X:00f0 Y:0089 S:1fe6 D:0000 DB:7e NvmxdIzc V:254 H:293 F:29

TL;DR

It's possible to flag all 8 available particle alchemy slots as "being currently used" by casting them into a screen transition. The 9th cast overflows that logic and is being written into the first animation alchemy slot, which has an incompatible structure.

This wouldn't be harmful as is, but progressing the animations seems to execute code for each animation in every frame. So based on the properties of the projectile various memory addresses can be manipulated. If the crash is being executed in the very right moment it leads to executing RAM values, like the currencies, as code.

Example:

This can be exploited by having a very specific crash and having some specific amounts of each currency:

  • 35,529 Talons
  • 2,764 Jewels
  • 4,331,612 Gold Coins
  • 0 Credits

The amount of Gold Coins is an instruction to execute controller inputs as code.

This example has flaws, though:

  • It's hard to control the semi random number (That's why it has been injected in this example and a setup has to be found first, that leads to the specific X_alchemy value)
  • The game is still crashing, unless the arbitrary code fixes the 9cast bug and jump back to regular code