Secret of Evermore:Alchemy RAM manipulation

From Data Crystal
Revision as of 06:10, 28 December 2019 by R.bin (talk | contribs)
Jump to navigation Jump to search

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
    • Projectile 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 (Which can lead to a crash, if 8 spells of that type are already active)
    • 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 all 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

Reproducing the Crash

Preparation:

  • Read up on 8casts
  • Stock up on ingredients and/or Call Beads

Triggered by a Boss

  • 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"

With Screen Transitions

  • 8cast on an enemy near a map exit
  • Leave the screen instantly
  • The game ends up in a buggy state, where the information of the 8cast is being stored somehow (Similar to to a 9cast)
  • Once the next spell is being cast the game "freezes"

Manipulating Memory

Affects on the Hardware

S-CPU:

  • 99% of crashes end up in a "freeze" (S-CPU comes to a halt)
  • The rest of the crashes aren't freezing the game, but produce severe visual glitches (The S-CPU keeps going)
    • Black screen
    • Repeating patterns
    • Colorful forms

Sound:

  • Unaffected

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

Manipulating X_alchemy

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

  • $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

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.

X_alchemy=$D22C → X_ram=$800A ($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

X_alchemy=$0003 → X_ram=$C900 ($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

X_alchemy=$004E → X_ram=$00B2 ($80B2 in HiROM)

9198f7 ldx $0014,y   [7e3378] A:000a X:0000 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 78 H: 74 F:53
9198fa beq $9907     [919907] A:000a X:004e Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 78 H: 84 F:53
9198fc lda $0028,y   [7e338c] A:000a X:004e Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 78 H: 87 F:53
9198ff sta $4c       [00004c] A:4e89 X:004e Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 78 H: 97 F:53
919901 jsl $919750   [919750] A:4e89 X:004e Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 78 H:104 F:53
919750 lda $910000,x [91004e] A:4e89 X:004e Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 78 H:117 F:53
919754 tax                    A:00b2 X:004e Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 78 H:127 F:53
919755 jsr ($8000,x) [9180b2] A:00b2 X:00b2 Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 78 H:130 F:53

918988 bit $00,x     [0000b2] A:00b2 X:00b2 Y:3364 S:1fec D:0000 DB:7e nvmxdizC V: 78 H:153 F:53
91898a cop #$00               A:00b2 X:00b2 Y:3364 S:1fec D:0000 DB:7e nvmxdizC V: 78 H:162 F:53
00885e stp                    A:00b2 X:00b2 Y:3364 S:1fe8 D:0000 DB:7e nvmxdIzC V: 78 H:177 F:53

X_alchemy=$FF85 → X_ram=$0300 ($8300 in HiROM)

9198f7 ldx $0014,y   [7e3378] A:000a X:0000 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 62 H: 28 F:22
9198fa beq $9907     [919907] A:000a X:ff85 Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 62 H: 38 F:22
9198fc lda $0028,y   [7e338c] A:000a X:ff85 Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 62 H: 41 F:22
9198ff sta $4c       [00004c] A:4e89 X:ff85 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 62 H: 51 F:22
919901 jsl $919750   [919750] A:4e89 X:ff85 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 62 H: 58 F:22
919750 lda $910000,x [91ff85] A:4e89 X:ff85 Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 62 H: 72 F:22
919754 tax                    A:0300 X:ff85 Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 62 H: 81 F:22
919755 jsr ($8000,x) [918300] A:0300 X:0300 Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 62 H: 84 F:22

91001a rol $ff00     [7eff00] A:0300 X:0300 Y:3364 S:1fec D:0000 DB:7e nvmxdizC V: 62 H: 97 F:22
91001d sbc $000000,x [000300] A:0300 X:0300 Y:3364 S:1fec D:0000 DB:7e nvmxdizc V: 62 H:112 F:22
00885f stp                    A:c8c1 X:0300 Y:3364 S:1fe8 D:0000 DB:7e NvmxdIzc V: 62 H:150 F:22

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
$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
$A000-$BFFF - $2000-$FFFF can't be addressed via HiROM

TL;DR

It's possible to flag all 8 available projectile 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:

7E3364 to 7E3563 = Alchemy attack slots (x40 bytes per active alchemy attack).
                   x28-x29 = Pointer to caster
                   x2A-x2B = Alchemy power/damage
                   x2E-x40 = Pointers to target players/monsters/npcs

7E3564 to ...... = Alchemy attack slots (x76 bytes per active alchemy attack).  Filled by
                   projectile type alchemy like flash and fireball.
                   x28-x29 = Pointer to caster
                   x2A-x2B = Alchemy power/damage
                   x2E-x40 = Pointers to target players/monsters/npcs

Progressing the animations seems to be polled and contains a relative jump with the 14th Byte (16 Bit) as offset. By overflowing the projectile spells the 14th Byte of the 9th projectile is being written into the that memory. (Which crashes the game within frames after the projectiles are visible)

If the crash is being executed in the very right moment it leads to executing RAM values, like the currencies, as code:

  • X_alchemy=$????
    • $7E3378 preferably contains a value between $0000 and $1FFF, to end in the LowRAM
    • Alternatively the same value can be read from ROM or any other addressable memory
  • X_ram=$8AC6
    • $8000-$9FFF ends up in LowRAM
    • E.g. $0AC6 is the start of the in game 4 currencies
9198f7 ldx $0014,y   [7e3378] A:000a X:0000 Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 72 H:226 F:48point at
9198fa beq $9907     [919907] A:000a X:???? Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 72 H:236 F:48
9198fc lda $0028,y   [7e338c] A:000a X:???? Y:3364 S:1ff1 D:0000 DB:7e NvmxdizC V: 72 H:239 F:48
9198ff sta $4c       [00004c] A:4e89 X:???? Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 72 H:249 F:48
919901 jsl $919750   [919750] A:4e89 X:???? Y:3364 S:1ff1 D:0000 DB:7e nvmxdizC V: 72 H:256 F:48
919750 lda $910000,x [91d22c] A:8AC6 X:???? Y:3364 S:1fee D:0000 DB:7e nvmxdizC V: 72 H:270 F:48
919754 tax                    A:8AC6 X:???? Y:3364 S:1fee D:0000 DB:7e NvmxdizC V: 72 H:279 F:48
919755 jsr ($8000,x) [91000a] A:8AC6 X:8AC6 Y:3364 S:1fee D:0000 DB:7e NvmxdizC V: 72 H:282 F:48

// This would jump into the 12 bytes that store the 4 currencies of the game