Punkline
Dr. Frankenstack
- Joined
- May 15, 2015
- Messages
- 423
The “Impossible Cancel” is a project meant to recreate a playable version of Melee Impossible.
The original author did not provide a code, or any information to work with.
As a result, in addition to being a code (in development) this project is largely an R&D effort and aims to document as much as possible about the involved mechanics.
The project aims to eventually recreate Turbo Mode, but currently uses manually input L/R (Edit - light shield) presses in order to create an action state cancel that may be applied at any time. Works with B-specials, as well as normal movements like running or dodging.
---
Update 4/29/2016
Get the newest code here. Here are some instructions on how to install it.
See the update post for more info:
I’ve made my syntax highlighted ASM available at a glance here:
(v0.20 demo video)
---
Legacy experiments:
Original post with lots of info about early concepts:
(contains a lot of images, videos, documents, and codes exploring external player data offsets 0x904, 0x908, 0x444, and 0x44C)
Update with information about how to use animation interrupts and player updates to make previous exploits much safer:
The original author did not provide a code, or any information to work with.
As a result, in addition to being a code (in development) this project is largely an R&D effort and aims to document as much as possible about the involved mechanics.
The project aims to eventually recreate Turbo Mode, but currently uses manually input L/R (Edit - light shield) presses in order to create an action state cancel that may be applied at any time. Works with B-specials, as well as normal movements like running or dodging.
Update 4/29/2016
Get the newest code here. Here are some instructions on how to install it.
See the update post for more info:
Code:
-==-
Impossible Cancel v0.20 (library)
Stable recreation of "Melee Impossible"
[Punkline]
Version -- DOL Offset ------ Hex to Replace ---------- ASM Code
1.02 ----- 0x8006b090--- C03F0650 -> Branch
C03F0650 C0028874
FC000800 4182002C
C03F0654 FC000800
40820020 9061FFF8
7FC3F378 83C1FFF8
bl <ICNCv020_softCancel>
9061FFF8 7FC3F378
83C1FFF8 C0228874
00000000
<ICNCv020_softCancel>
#r3= external player data offset
#experimental
7C0802A6 90010004
9421FF30 D8210008
BC610010 83E3002C
83DF0004 8BBF2073
3B800000 3B600000
3B400000 48000041
7C6802A6 A0830000
5480067F 41820068
7C00E800 40820020
5480C6BE 7C00F000
40820014 549C8FFE
549B97FE 549ACFFE
48000044 A4830002
4BFFFFD0 4E800021
01910194 81138493
0496049C 04A204A9
05948713 47940991
0C948C13 0E940F91
90139313 53941691
16949613 17949713
00000000 807F1A58
2C030000 41820014
8063002C 38000000
90031A4C 480000F8
833F0010 2C190011
408100B0 2C190018
4182004C 2C190027
41820044 2C190029
4180002C 2C19002B
40810034 2C190046
4180001C 2C19004A
40810024 2C1900E9
4180000C 2C1900EC
40810014 2C1D0000
41820068 2C1D0058
41820060 887F221E
60600080 981F221E
807F0000
bl <ICNCv020_animationSpeedCancel>
807F0010 7C03C800
41820010 2C1A0000
4082FED8 48000034
2C1B0000 4082002C
807F0004 2C030007
40820018 807F0010
2C03015E 41820014
2C030161 4182000C
807F0000
bl <ICNCv020_abortActionState>
807F0010 2C03001D
41800034 2C030026
4181002C 2C1C0000
41820024 807F0000
38800001 38A00001
80CDAEB4 C0260340
C0460344 38C00000
bl 0x80096900
8B5F221E 735A007F
9B5F221E B8610010
C8210008 382100D0
80010004 7C0803A6
4E800020
<ICNCv020_animationSpeedCancel>
#r3= external player data offset
#--sets animation speed to x1000
#--updates player frame stuff, which uses animation speed to create frame unit size
#--updates player article stuff (mask out bit 0x80 of player.0x2270 if you want to avoid projectiles)
#--attempts player animation interrupt check (cancels action state)
#--sets animation speed back to old speed if still == x1000 (cases where cancel fails)
7C0802A6 90010004
9421FFE8 BFC10010
83E3002C C03F089C
D021FFF8 C022E864
807F0000
bl 0x8006f190
807F0000
bl 0x8006eba4
807F0000
bl 0x8006C80C
807F0000
bl 0x8006A360
C002E864 C03F089C
FC010000 40820010
C021FFF8 807F0000
bl 0x8006f190
BBC10010 38210018
80010004 7C0803A6
4E800020
<ICNCv020_abortActionState>
#r3= external player data offset
#--if player is in air, set animation interrupt to transition into falling
#--else, set animation interrupt to transition into standing
#--set 0x904 to a non-0 value (bypasses frame logic check for next call)
#--attempt player animation interrupt check (cancels action state)
7C0802A6 90010004
9421FFF0 93E10008
83E3002C 887F2073
2C030000 4182003C
807F00E0 2C030000
41820014 3C60800C
6063B2F8 907F21A0
48000010 3C608009
60639E80 907F21A0
38600001 907F08A4
807F0000
bl 0x8006A360
83E10008 38210010
80010004 7C0803A6
4E800020
$Impossible Cancel v0.20 [Punkline]
C206B090 0000006A
C03F0650 C0028874
FC000800 4182002C
C03F0654 FC000800
40820020 9061FFF8
7FC3F378 83C1FFF8
48000019 9061FFF8
7FC3F378 83C1FFF8
C0228874 4800030C
7C0802A6 90010004
9421FF30 D8210008
BC610010 83E3002C
83DF0004 8BBF2073
3B800000 3B600000
3B400000 48000041
7C6802A6 A0830000
5480067F 41820068
7C00E800 40820020
5480C6BE 7C00F000
40820014 549C8FFE
549B97FE 549ACFFE
48000044 A4830002
4BFFFFD0 4E800021
01910194 81138493
0496049C 04A204A9
05948713 47940991
0C948C13 0E940F91
90139313 53941691
16949613 17949713
00000000 807F1A58
2C030000 41820014
8063002C 38000000
90031A4C 48000104
833F0010 2C190011
408100B0 2C190018
4182004C 2C190027
41820044 2C190029
4180002C 2C19002B
40810034 2C190046
4180001C 2C19004A
40810024 2C1900E9
4180000C 2C1900EC
40810014 2C1D0000
41820068 2C1D0058
41820060 887F221E
60600080 981F221E
807F0000 480000B9
807F0010 7C03C800
41820010 2C1A0000
4082FED8 48000034
2C1B0000 4082002C
807F0004 2C030007
40820018 807F0010
2C03015E 41820014
2C030161 4182000C
807F0000 4800011D
807F0010 2C03001D
41800040 2C030026
41810038 2C1C0000
41820030 807F0000
38800001 38A00001
80CDAEB4 C0260340
C0460344 38C00000
3D808009 618C6900
7D8803A6 4E800021
8B5F221E 735A007F
9B5F221E B8610010
C8210008 382100D0
80010004 7C0803A6
4E800020 7C0802A6
90010004 9421FFE8
BFC10010 83E3002C
C03F089C D021FFF8
C022E864 807F0000
3D808006 618CF190
7D8803A6 4E800021
807F0000 3D808006
618CEBA4 7D8803A6
4E800021 807F0000
3D808006 618CC80C
7D8803A6 4E800021
807F0000 3D808006
618CA360 7D8803A6
4E800021 C002E864
C03F089C FC010000
4082001C C021FFF8
807F0000 3D808006
618CF190 7D8803A6
4E800021 BBC10010
38210018 80010004
7C0803A6 4E800020
7C0802A6 90010004
9421FFF0 93E10008
83E3002C 887F2073
2C030000 41820048
807F00E0 2C030000
41820014 3C60800C
6063B2F8 907F21A0
48000010 3C608009
60639E80 907F21A0
38600001 907F08A4
807F0000 3D808006
618CA360 7D8803A6
4E800021 83E10008
38210010 80010004
7C0803A6 4E800020
60000000 00000000
C206B090 0000006A
C03F0650 C0028874
FC000800 4182002C
C03F0654 FC000800
40820020 9061FFF8
7FC3F378 83C1FFF8
48000019 9061FFF8
7FC3F378 83C1FFF8
C0228874 4800030C
7C0802A6 90010004
9421FF30 D8210008
BC610010 83E3002C
83DF0004 8BBF2073
3B800000 3B600000
3B400000 48000041
7C6802A6 A0830000
5480067F 41820068
7C00E800 40820020
5480C6BE 7C00F000
40820014 549C8FFE
549B97FE 549ACFFE
48000044 A4830002
4BFFFFD0 4E800021
01910194 81138493
0496049C 04A204A9
05948713 47940991
0C948C13 0E940F91
90139313 53941691
16949613 17949713
00000000 807F1A58
2C030000 41820014
8063002C 38000000
90031A4C 48000104
833F0010 2C190011
408100B0 2C190018
4182004C 2C190027
41820044 2C190029
4180002C 2C19002B
40810034 2C190046
4180001C 2C19004A
40810024 2C1900E9
4180000C 2C1900EC
40810014 2C1D0000
41820068 2C1D0058
41820060 887F221E
60600080 981F221E
807F0000 480000B9
807F0010 7C03C800
41820010 2C1A0000
4082FED8 48000034
2C1B0000 4082002C
807F0004 2C030007
40820018 807F0010
2C03015E 41820014
2C030161 4182000C
807F0000 4800011D
807F0010 2C03001D
41800040 2C030026
41810038 2C1C0000
41820030 807F0000
38800001 38A00001
80CDAEB4 C0260340
C0460344 38C00000
3D808009 618C6900
7D8803A6 4E800021
8B5F221E 735A007F
9B5F221E B8610010
C8210008 382100D0
80010004 7C0803A6
4E800020 7C0802A6
90010004 9421FFE8
BFC10010 83E3002C
C03F089C D021FFF8
C022E864 807F0000
3D808006 618CF190
7D8803A6 4E800021
807F0000 3D808006
618CEBA4 7D8803A6
4E800021 807F0000
3D808006 618CC80C
7D8803A6 4E800021
807F0000 3D808006
618CA360 7D8803A6
4E800021 C002E864
C03F089C FC010000
4082001C C021FFF8
807F0000 3D808006
618CF190 7D8803A6
4E800021 BBC10010
38210018 80010004
7C0803A6 4E800020
7C0802A6 90010004
9421FFF0 93E10008
83E3002C 887F2073
2C030000 41820048
807F00E0 2C030000
41820014 3C60800C
6063B2F8 907F21A0
48000010 3C608009
60639E80 907F21A0
38600001 907F08A4
807F0000 3D808006
618CA360 7D8803A6
4E800021 83E10008
38210010 80010004
7C0803A6 4E800020
60000000 00000000
I’ve made my syntax highlighted ASM available at a glance here:
Code:
/*Impossible Cancel v0.20 (library) [Punkline]
Stable recreation of "Melee Impossible" -- an unavailable cancel code that was apparently never playable.
Now available in the following forms, in descending order of flexibility and complexity:
--Soft Cancel -- safe, stable, versatile, customizable, really big
--Animation Speed Cancel -- safe, may cause glitchs in limited cases (special thanks to Achilles for this concept!)
--Abort Action State -- simple, rigid, potentially unsafe when used from falling states
Any or all of this library may be included in MCM (or a DOL mod)
Doing so will allow codes to be written that utilize these cancels from any context
*/
#example injection mod:
#Impossible Cancel 0.20 (Melee 1.02) [Punkline]
#@8006b090: lfs f1, 0x0650 (r31) (checking analog input for digitization)
#lr, cr0 and f0 are safe to use
#r3 == internal player data offset 0x65C (digital button data)
#r30 == external player data offset 0x0 (player entity)
#r31 == internal player data offset 0x0 (player data)
#f1 == to become analog L/R value for current frame
#0x778C(rtoc) == 0.0 (no analog L/R press to digitize)
lfs f1, 0x0650 (r31) #original instruction, loads current frame L/R value for digitization in digital buttons
lfs f0, -0x778C (rtoc) #load 0.0 for comparison
fcmpu cr0, f0, f1 #compare to current L/R value
beq return #return if not pressed
lfs f1, 0x654(r31) #else, check last frame value
fcmpu cr0, f0, f1
bne return #if this isn't empty, then analog button was not pressed on this frame
stw r3, -0x8(sp) #temporarily store this; it gets saved in r30 (which is backed up in call)
mr r3, r30
lwz r30, -0x8(sp) #swaps r3 and r30; so we pass and save registers appropriately
bl <ICNCv020_softCancel>
stw r3, -0x8(sp)
mr r3, r30
lwz r30, -0x8(sp) #swap registers again, everything back to normal~
return:
lfs f1, -0x778C (rtoc) #Sheild nop overwrites Original Instruction
.long 0x00000000 #hard return (compiled by MCM)
/*ICNCv020_softCancel
r3 = player entity
This function uses some extra logic to wrap conditions around a procedure that
tries to intelligently uses both of the other cancles provided in this library.
Experimental.
*/
mflr r0
stw r0, 0x4(sp)
stwu sp, -0xD0(sp)
stfd f1, 0x8(sp)
stmw r3, 0x10(sp) #follow your dreams~
lwz r31, 0x2C(r3)
loopCancel: #some moves will continue canceling until they reach a wait state
#First we parse an array of data located in the inline data table after these instructions
lwz r30, 0x4(r31) #r30 = (real) player character ID
lbz r29, 0x2073(r31) #r29 = (real) player move ID
li r28, 0 #r28 = bool (force special falling?)
li r27, 0 #r27 = bool (force preservation?)
li r26, 0 #r26 = bool (loop cancel?)
bl getParseDataTable
mflr r3 # r3 = parse data pointer
lhz r4, 0(r3) # r4 = starting hword entry from data table
parseLoop:
rlwinm. r0,r4,0,25,31 #check if the (7-bit) move ID is 00
beq grabCheck #if so, terminate loop (00 is a termination code)
cmpw r0, r29 #else, start parsing the entry
bne parseNextEntry #check for move ID match
rlwinm r0,r4,24,26,31
cmpw r0, r30 #check for character ID match
bne parseNextEntry
#at this point, we've found an entry in the table that matches our current character move
#so we save the bools for the code to use later in order to make some decisions about how to work the cancel
#--The parse is basically answering the following questions for us:
rlwinm r28,r4,17,31,31 #do we want to convert non-special air wait states into special falling?
rlwinm r27,r4,18,31,31 #do we want to avoid "aborting" the action state if we have problems?
rlwinm r26,r4,25,31,31 #do we want to keep canceling this move until it reaches a wait state?
b grabCheck
parseNextEntry:
lhzu r4,2(r3) #load next hword to be parsed
b parseLoop #loop back to actually parse it
getParseDataTable:
#This is an inline data table. It contains hardcoded variables that get parsed by the above system.
#It's possible to organize this table as a standalone function--but this data has little remote application...
#--Each hword in the array is an entry that is parsed like so:
#0x8000 - bool -- "force special falling" -- the cancel will assign special falling instead of falling
#0x4000 - bool -- "force preservation" -- the cancel will avoid using the "abort actionstate" fallback
#0x3F00 - 6-bit ID -- internal character ID (specifies a character for "character move")
#0x0080 - bool -- "continue cancel until wait state"
#0x007F - 7-bit ID -- common move ID (specifies a move for "character move")
#--0x00 as a move ID is a termination code for the parse loop
blrl #any branches to "parseData" will return with a pointer to the data in lr
.hword 0x0191 #fox laser
.hword 0x0194 #fox shine
.hword 0x8113 #fox firefox
.hword 0x8493 #kirby upB
.hword 0x0496 #kirby foxB
.hword 0x049C #kirby NessB
.hword 0x04A2 #kirby jpB
.hword 0x04A9 #kirby falcoB
.hword 0x0594 #Bowser downB
.hword 0x8713 #Sheik upB
.hword 0x4794 #Sheik downB
.hword 0x0991 #Ness B
.hword 0x0C94 #Pikachu thunder
.hword 0x8C13 #Pikachu quick attack
.hword 0x0E94 #yoshi downB
.hword 0x0F91 #JP B
.hword 0x9013 #Mewtwo Teleport
.hword 0x9313 #Zelda upB
.hword 0x5394 #Zelda downB
.hword 0x1691 #Falco laser
.hword 0x1694 #Falco Shine
.hword 0x9613 #Falco firebird
.hword 0x1794 #Pichu thunder
.hword 0x9713 #Pichu quick attack
.hword 0x0000 #terminate parse
.hword 0x0000
#make sure to pad this if necessary in order to stay 32-bit aligned
grabCheck:
#Check if player is holding another player.
#-- We want to prevent a "soulbreaker" glitch, so we handle this by making the grab expire
lwz r3, 0x1A58(r31) #grabbed player entity slot
cmpwi r3, 0
beq ASblacklist
lwz r3, 0x2C(r3) #if a player entity is in this slot, then load data
li r0, 0
stw r0, 0x1A4C(r3) #kill breakout countdown
b return #and simply return
ASblacklist: #a hardcoded blacklist that stops the cancel during certain moves/action states
#-- for some wait states, this is to prevent technical problems that may arise from wait-state interrupt logic
#-- for other states, it is mostly for the sake of preserving gameplay mechanics
lwz r25, 0x10(r31) #player action state; we're going to check this again later, so save in r25
cmpwi r25, 0x11 #masks out AS 0x00-0x11 (death-walking)
ble SFcheckAndReturn #-- without this you can cancel your death animation and rack up SDs. Almost kept it.
#these are some whitelist entries; to be exceptions for the "wait state" check that follows
#they're kind of last-minute additions that can be optimized, but allow this to cancel jumps and things
cmpwi r25, 0x18 #kneebend
beq beginCancel
cmpwi r25, 0x27 #squat
beq beginCancel
cmpwi r25, 0x29 #squat reverse
blt waitCheck
cmpwi r25, 0x2B
ble beginCancel
cmpwi r25, 0x46
blt waitCheck
cmpwi r25, 0x4A #various landings
ble beginCancel
cmpwi r25, 0xE9
blt waitCheck
cmpwi r25, 0xEC #various dodges
ble beginCancel
waitCheck:
cmpwi r29, 0 #r29 still holds move Id, 00 is used for a large range of wait-like states
beq SFcheckAndReturn #this return will terminate looping cancels when canceling whole moves
cmpwi r29, 0x58 #parasol falling; canceling this allows for infinite jumps...
beq SFcheckAndReturn
beginCancel:
#before starting, we use this bit to turn the player invisible
#while invisibility isn't a feature of this code, doing this prevents particles from spawning during cancel
lbz r3, 0x221e(r31)
ori r0, r3, 0x80
stb r0, 0x221e(r31) #temporarily make player invisible, to stop particle spawns
#Now, we try the animation speed cancel. We do this by calling ICNv020_animationSpeedCancel
lwz r3, 0(r31)
bl <ICNCv020_animationSpeedCancel>
#The call doesn't return without updating some player stuff that should make the effect immediate
#so now we check the action state to make sure there was a transition
lwz r3, 0x10(r31) #current action state
cmpw r3, r25 #compare with saved action state
beq abortActionState #if the state hasn't changed, we attempt to (conditionally) use the other cancel method
#At this point, we've successfully used the animation speed cancel
#we just need to check to see if we're going to loop the cancel again from the parsed bool in r26
cmpwi r26, 0
bne loopCancel
b SFcheckAndReturn #if not, then return; because we're finished~
abortActionState: #branch here when animation speed cancel doesn't seem to work
#Sometimes it isn't possible to use a frame timer threshold to force an "animation interrupt"
#in these cases we use a less-safe cancel method--but with an animation interrupt function known to be safe.
#the drawback of this cancel is that it destroys the current action state progression, and preserves no subaction event data
#--In most cases, animation cancels can even be used in a similar fashion for a similar result
#the advantage however, is that this method may cancel things that otherwise softlock the player with an animation speed cancel
cmpwi r27, 0 #first make sure the "force preservation" option (from the parse) doesn't apply to this move
bne SFcheckAndReturn #if it does, then return. It's assumed that moves given this option will actually cancel properly.
lwz r3, 0x4(r31) #---Throwing this little bit in to cover up an old bug with 0x904
cmpwi r3, 7 #DI animations (like with falling states) cause the 0x904 cancel to freeze the game
bne notSheik #sheik's chain whip uses DI animations
lwz r3, 0x10(r31) #It isn't covered by "wait" states, so we add that here for now...
cmpwi r3, 0x15e
beq SFcheckAndReturn
cmpwi r3, 0x161
beq SFcheckAndReturn #A messy mess. This allows the chain to be used cleanly, however.
notSheik:
lwz r3, 0(r31)
bl <ICNCv020_abortActionState> #else, call abortActinState
SFcheckAndReturn: #this does a check to see if we should convert state into a special falling state (helpless)
lwz r3, 0x10(r31)
cmpwi r3, 0x1D #beginning of regular falling states
blt return
cmpwi r3, 0x26 #ending of regular falling states (includes damage fall)
bgt return
#under these circumstances, we just need to check the "force special falling" bool before we have enough info
#if it's == true, then we're going to convert the action state into a special falling
cmpwi r28, 0
beq return
#Since these are already assumed to be wait states, a normal animation interrupt will not work correctly.
#Instead, the action state needs to transition in some othere way. This can be tricky if the state isn't known at compile time
#Luckily we only need a specific state, which can be easily accomplished by setting up a call like so:
lwz r3, 0(r31)
li r4, 1 #this is how common air dodges handle calls to the special falling AS transition
li r5, 1
lwz r6, -0x514C(r13) #floats
lfs f1, 0x340(r6)
lfs f2, 0x344(r6)
li r6, 0
bl 0x80096900
return:
lbz r26, 0x221e(r31) #to ensure that the character is visible, we will mask out the invisibility bit we used to stop particles
andi. r26, r26, 0x7F #currently this doesn't handle the few cases where you cancel into a state meant to be invisible
stb r26, 0x221e(r31)
lmw r3, 0x10(sp)
lfd f1, 0x8(sp)
addi sp, sp, 0xD0
lwz r0, 0x4(sp)
mtlr r0
blr
/*ICNCv020_animationSpeedCancel
r3 = player entity
This subroutine makes calls to the following game functions in order to void action state frame logic:
-- (8006F190) animationSpeedChange
-- (8006EBA4) ac_AS_AnimationFrameUpdate&More
-- (8006C80C) player article/projectile update/spawn function (?)
-- (8006A360) animation interrupt update (? -- "playerThinkAgain")
Successful animation interrupts are usually invoked by this nullification of frame logic;
however, it is dependent on the function pointed to in 0x2200 (0x21a0 internal)
-- it's possible to modify the above pointer in order to change the effect of this cancel before calling
-- doing this with wait states however may cause softlocks in a few character moves
This method of cancel will resolve subaction data and may be used to continue natural state progression
!!Because of this, an unsuccessfully handled cancel will sometimes result in 1000x frames worth of particle spawns
-- an easy way to handle this is to prefix a call to this function with a write to this bit:
lbz r26, 0x221e(r31) #bit 0x80 in this byte makes the player turn invisible at some point in a frame update
# this bit is checked by GFX parses in order to skip particle spawns
*/
#the core of this concept was courteously provided by Achilles1515 on smashboards~
mflr r0
stw r0, 0x4(sp)
stwu sp, -0x18(sp)
stmw r30, 0x10(sp)
lwz r31, 0x2C(r3) #trying to get in the habit of doing this in my prolog--good practice for player stuff
lfs f1, 0x89C(r31) #we're going to store this so that we can optionally restore it before returning
stfs f1, -0x8(sp) #
lfs f1,-0x179C(rtoc) # load 1000.0 from table of contents; f1 function argument
# f1=newframespeed, r3= "external player data offset" (I may adopt that notation)
lwz r3, 0(r31) #make sure to pass player entity struct; do this properly by loading the pointer at 0x0(internal player)
bl 0x8006f190
#Now, to immediately force the effects of this animation speed transition,
#we make a call to the function that appears to be responsible for updating player action state frames
#it calls a number of functions that update the player based on the frame of the current action state.
#--this includes the player subaction event parsing loop function; which will (in theory) ignore all "timer" events
#Before returning, animation speed is returned to what it was before the action state transition (if it is still x1000.0)
#all of the following simply require the argument r3= external player data offset
#update frame (ac_AS_AnimationFrameUpdate&More) -- reads action state data, and other stuff
lwz r3, 0(r31) #make sure to pass player entity struct
bl 0x8006eba4
#update character projectile/article spawns -- will stop model spawining issues
lwz r3, 0(r31) #in sequence with the previous call, this will also properly update projectile spawns
bl 0x8006C80C
#update action state with animation interrupt (ac_playerThinkAgain -- the blrl towards the end is important)
lwz r3, 0(r31) #this call softens up a blrl into the player's currently stored animation interrupt
bl 0x8006A360 #I haven't looked into it very far, so it may do other things as well
return:
lfs f0, -0x179C(rtoc) #"1000.0"
lfs f1, 0x89C(r31)
fcmpu cr0, f1, f0 #make sure current animation speed hasn't been altered by action state change
bne noSpeedReset
lfs f1,-0x8(sp) # otherwise, load our original animation speed and restore it
# f1=newframespeed, r3= external player data offset
lwz r3, 0(r31) #make sure to pass player entity struct
bl 0x8006f190
noSpeedReset:
lmw r30, 0x10(sp)
addi sp, sp, 0x18
lwz r0, 0x4(sp)
mtlr r0
blr
/*ICNCv020_abortActionState
r3 = player entity
This subroutine checks the ground/air state in order to assign standing/falling animation interrupts
player.0x904 (external) is used to create a "shortcut" to the assigned action state; aborting the previous one
--special falling interrupts are preserved, but will only apply to the current action state transition.
--"playerThinkAgain" is called in order to make this state change happen before returning
*/
mflr r0
stw r0, 0x4(sp)
stwu sp, -0x10(sp)
stw r31, 0x8(sp)
lwz r31, 0x2C(r3)
lbz r3, 0x2073(r31) #player move ID
cmpwi r3, 0
beq return #don't attempt cancel on wait states
lwz r3, 0xE0(r31)
cmpwi r3, 0 #check if in air or on ground
beq ground
air:
lis r3, 0x800C
ori r3, r3, 0xB2F8 #animation interrupt used by "jump"
stw r3, 0x21A0(r31) #animation interrupt function pointer
b abortPoke
ground:
lis r3, 0x8009
ori r3, r3, 0x9E80 #animation interrupt used by "rebound"
stw r3, 0x21A0(r31) #animation interrupt function pointer
abortPoke:
li r3, 1
stw r3, 0x8A4(r31) #do a hard 0x904 cancel that uses the animation interrupt pointer we just set
#update action state with animation interrupt (ac_playerThinkAgain -- the blrl towards the end)
lwz r3, 0(r31) #make sure to pass player entity struct;
bl 0x8006A360
return:
lwz r31, 0x8(sp)
addi sp, sp, 0x10
lwz r0, 0x4(sp)
mtlr r0
blr
![](http://i.imgur.com/sIIFY8o.png)
![](http://i.imgur.com/I8yOgwa.png)
![](http://i.imgur.com/Fl0tkf8.png)
![](http://i.imgur.com/L8tjK5D.png)
![](http://i.imgur.com/LGa2Gz6.png)
(v0.20 demo video)
---
Legacy experiments:
The original concept explored what looks like an exploit involving action state frame mechanics via the memory address located at player.0x904. It was very unstable, but I created a heavy-handed actionstate mask that allows the concept to be exploited relatively safely. This version is very stable and has very few problems, but is quite large all on its own:
Achilles suggested a far more elegant solution that involves utilizing the same mechanic that the game uses to make L-canceled aerial landing animations speed up. The idea is that by changing your animation speed to something ridiculous, you can effectively create an action state transition that takes only a single frame.
This comes with some animation-related glitches, but it is a far more stable solution than the above and thus requires much less code to work smoothly. Here’s an early implementation of an experimental fix for one of these animation problems related to spawning particle effects:
There was some discussion about turning this into a special game type that used slow-motion in order to give players time to consider their technical options. Here are a couple versions of the Impossible Cancel (with animation bugs~) that incorporate early versions of a variable slowmo engine:
$Impossible Cancel v0.1.4b (Melee 1.02) [Punkline]
C206B090 0000002F
48000058 000E0016
001D0026 00CD00CE
00DB00DE 040F5556
57585986 8905055F
60656606 145E6107
075D5E60 610A0B55
560E0E5D 626E1010
5F600000 00000000
10260038 00000000
00000000 00000000
00000000 00000000
7F0802A6 48000005
7FE802A6 7F0803A6
3BFFFFDC 831E06B0
2C180000 418200F4
929F0008 92DF000C
92FF0010 933F0014
92BF0018 8ABE006C
7EB5FA14 831E06B4
2C180000 4082000C
3B000001 9B150000
8B150000 2C180001
408200A4 82DE0070
82FE0064 8A9F0004
A33F0006 7F39F850
A3190000 7C16C000
41800010 A3190002
7C16C000 40810070
3A94FFFC 3B390004
2C140000 4181FFDC
8A9F0005 8B190000
7C17C000 40820030
8F190001 2C180020
4081FFF8 3A800000
2C180020 40810018
63180100 7C16C000
8F190001 41820028
4BFFFFE8 3A94FFFF
3B390001 2C140000
4181FFBC 3B000001
931E0904 931E0908
4800000C 3B000000
9B1F0000 829F0008
82DF000C 82FF0010
833F0014 82BF0018
3BFE0060 7FD8F378
C0228874 00000000
C206B090 0000002F
48000058 000E0016
001D0026 00CD00CE
00DB00DE 040F5556
57585986 8905055F
60656606 145E6107
075D5E60 610A0B55
560E0E5D 626E1010
5F600000 00000000
10260038 00000000
00000000 00000000
00000000 00000000
7F0802A6 48000005
7FE802A6 7F0803A6
3BFFFFDC 831E06B0
2C180000 418200F4
929F0008 92DF000C
92FF0010 933F0014
92BF0018 8ABE006C
7EB5FA14 831E06B4
2C180000 4082000C
3B000001 9B150000
8B150000 2C180001
408200A4 82DE0070
82FE0064 8A9F0004
A33F0006 7F39F850
A3190000 7C16C000
41800010 A3190002
7C16C000 40810070
3A94FFFC 3B390004
2C140000 4181FFDC
8A9F0005 8B190000
7C17C000 40820030
8F190001 2C180020
4081FFF8 3A800000
2C180020 40810018
63180100 7C16C000
8F190001 41820028
4BFFFFE8 3A94FFFF
3B390001 2C140000
4181FFBC 3B000001
931E0904 931E0908
4800000C 3B000000
9B1F0000 829F0008
82DF000C 82FF0010
833F0014 82BF0018
3BFE0060 7FD8F378
C0228874 00000000
Achilles suggested a far more elegant solution that involves utilizing the same mechanic that the game uses to make L-canceled aerial landing animations speed up. The idea is that by changing your animation speed to something ridiculous, you can effectively create an action state transition that takes only a single frame.
This comes with some animation-related glitches, but it is a far more stable solution than the above and thus requires much less code to work smoothly. Here’s an early implementation of an experimental fix for one of these animation problems related to spawning particle effects:
$Animation Cancel Concept v0.1.2 (Melee 1.02) [Achilles1515] [Punkline]
C206B844 00000007
838306C8 579F0001
41820028 579F06F7
40820020 C022E864
3FE08006 63FFF190
7FE803A6 7C7F1B78
4E800021 7FE3FB78
7C7C1B78 00000000
C2071048 00000006
80A308FC 3CC0447A
7C053000 60650060
40820014 3CC08007
60C611C4 7CC803A6
4E800020 80CA0008
60000000 00000000
C206B844 00000007
838306C8 579F0001
41820028 579F06F7
40820020 C022E864
3FE08006 63FFF190
7FE803A6 7C7F1B78
4E800021 7FE3FB78
7C7C1B78 00000000
C2071048 00000006
80A308FC 3CC0447A
7C053000 60650060
40820014 3CC08007
60C611C4 7CC803A6
4E800020 80CA0008
60000000 00000000
There was some discussion about turning this into a special game type that used slow-motion in order to give players time to consider their technical options. Here are a couple versions of the Impossible Cancel (with animation bugs~) that incorporate early versions of a variable slowmo engine:
$Slowmo Combo Concept A (Melee 1.02) [Punkline]
C237E24C 00000016
48000018 3C1000A0
00000000 00125000
00000000 00000000
7C0802A6 48000005
7CA802A6 7C0803A6
38A5FFE4 9065000C
90850010 3C808043
60842A2C 88050002
2C000000 40820010
80640008 90640000
48000044 88650004
2C030001 88650002
4182000C 3863FFFF
98650002 88050000
7C001800 80650008
41800010 7C6303D6
88050002 7C6301D6
80040008 7C630214
90640000 38000000
98050004 8065000C
80850010 80A3C05C
60000000 00000000
C20410E0 00000008
7D8802A6 48000005
7DE802A6 7D8803A6
39EFFF44 898F0001
8B6F0002 7D8CDA14
8B6F0003 7C0CD800
41810008 998F0002
7F4CD378 7F4FD378
83610024 00000000
C2069B64 0000000A
7C8802A6 48000005
7D4802A6 7C8803A6
394AFEFC 809A089C
3C60447A 7C041800
40A20020 888A0001
886A0002 7C841A14
886A0003 7C041800
41810008 988A0002
7F4AD378 7F43D378
60000000 00000000
C206B090 0000000E
7FC802A6 48000005
7F2802A6 7FC803A6
3B39FEA4 83D819BC
2C1E0000 4182000C
3BC00001 9BD90004
83D806B0 2C1E0000
41820030 83D806B4
2C1E0000 40820024
3F208006 6339F190
7F2803A6 C022E864
7C791B78 7F03C378
4E800021 7F23CB78
C0228874 3B380064
7F1EC378 00000000
C237E24C 00000016
48000018 3C1000A0
00000000 00125000
00000000 00000000
7C0802A6 48000005
7CA802A6 7C0803A6
38A5FFE4 9065000C
90850010 3C808043
60842A2C 88050002
2C000000 40820010
80640008 90640000
48000044 88650004
2C030001 88650002
4182000C 3863FFFF
98650002 88050000
7C001800 80650008
41800010 7C6303D6
88050002 7C6301D6
80040008 7C630214
90640000 38000000
98050004 8065000C
80850010 80A3C05C
60000000 00000000
C20410E0 00000008
7D8802A6 48000005
7DE802A6 7D8803A6
39EFFF44 898F0001
8B6F0002 7D8CDA14
8B6F0003 7C0CD800
41810008 998F0002
7F4CD378 7F4FD378
83610024 00000000
C2069B64 0000000A
7C8802A6 48000005
7D4802A6 7C8803A6
394AFEFC 809A089C
3C60447A 7C041800
40A20020 888A0001
886A0002 7C841A14
886A0003 7C041800
41810008 988A0002
7F4AD378 7F43D378
60000000 00000000
C206B090 0000000E
7FC802A6 48000005
7F2802A6 7FC803A6
3B39FEA4 83D819BC
2C1E0000 4182000C
3BC00001 9BD90004
83D806B0 2C1E0000
41820030 83D806B4
2C1E0000 40820024
3F208006 6339F190
7F2803A6 C022E864
7C791B78 7F03C378
4E800021 7F23CB78
C0228874 3B380064
7F1EC378 00000000
$Slowmo Combo Concept B (Melee 1.02)[Punkline]
C237E24C 00000016
48000018 604000A0
00000000 002932E0
00000000 00000000
7C0802A6 48000005
7CA802A6 7C0803A6
38A5FFE4 9065000C
90850010 3C808043
60842A2C 88050002
2C000000 40820010
80640008 90640000
48000044 88650004
2C030001 88650002
4182000C 3863FFFF
98650002 88050000
7C001800 80650008
41800010 7C6303D6
88050002 7C6301D6
80040008 7C630214
90640000 38000000
98050004 8065000C
80850010 80A3C05C
60000000 00000000
C20410E0 00000008
7D8802A6 48000005
7DE802A6 7D8803A6
39EFFF44 898F0001
8B6F0002 7D8CDA14
8B6F0003 7C0CD800
41810008 998F0002
7F4CD378 7F4FD378
83610024 00000000
C206B090 0000000E
7FC802A6 48000005
7F2802A6 7FC803A6
3B39FEFC 83D819BC
2C1E0000 4182000C
3BC00001 9BD90004
83D806B0 2C1E0000
41820030 83D806B4
2C1E0000 40820024
3F208006 6339F190
7F2803A6 C022E864
7C791B78 7F03C378
4E800021 7F23CB78
C0228874 3B3A0064
7F1EC378 00000000
C237E24C 00000016
48000018 604000A0
00000000 002932E0
00000000 00000000
7C0802A6 48000005
7CA802A6 7C0803A6
38A5FFE4 9065000C
90850010 3C808043
60842A2C 88050002
2C000000 40820010
80640008 90640000
48000044 88650004
2C030001 88650002
4182000C 3863FFFF
98650002 88050000
7C001800 80650008
41800010 7C6303D6
88050002 7C6301D6
80040008 7C630214
90640000 38000000
98050004 8065000C
80850010 80A3C05C
60000000 00000000
C20410E0 00000008
7D8802A6 48000005
7DE802A6 7D8803A6
39EFFF44 898F0001
8B6F0002 7D8CDA14
8B6F0003 7C0CD800
41810008 998F0002
7F4CD378 7F4FD378
83610024 00000000
C206B090 0000000E
7FC802A6 48000005
7F2802A6 7FC803A6
3B39FEFC 83D819BC
2C1E0000 4182000C
3BC00001 9BD90004
83D806B0 2C1E0000
41820030 83D806B4
2C1E0000 40820024
3F208006 6339F190
7F2803A6 C022E864
7C791B78 7F03C378
4E800021 7F23CB78
C0228874 3B3A0064
7F1EC378 00000000
Original post with lots of info about early concepts:
(contains a lot of images, videos, documents, and codes exploring external player data offsets 0x904, 0x908, 0x444, and 0x44C)
Hey guys. First post here on smashboards.
Has anyone played around with Character Data offset 0x904 (from the RAM Address List?)
Poking it lets you cancel your action state on the next frame, and it isn’t picky about what actions it can be applied to. It’s a lot of fun, really unstable, and was virtually undocumented the last time I checked these forums.
Somebody left a note about it in the RAM Address List, but it just mentioned that poking it to “40c00000” (float “6.0”) will cancel the current action state. Indeed it does, and in fact any non-0 value will work--so for the sake of simplicity I’ve used “1” throughout my examples here.
Let me back up a little and explain that I’m not really much of a programmer, and I haven’t played a smash game in almost a decade.
I found this board sometime back in April 2015 while searching for how the Melee Impossible video was … possible. My search turned up very little, so I watched Dan’s excellent primer on writing Gecko Codes and meant to try and figure it out. (Thanks Dan!)
It didn’t take long for me to stumble upon 0x904 in the RAM Address List, so I’m assuming some of you have at least tried poking it.
I’ve spent some time experimenting with it and now I have a lot of information that doesn’t seem to be documented anywhere here. Admittedly, I don’t frequent these forums very often so I could be mistaken. I searched pretty fervently back in April-May for some answers however, and found no information.
Now that there’s a nice little sub-forum dedicated to posting codes, I figured this would be a great place to dump some info and maybe collaborate a little. My intention is to reproduce Melee Impossible in a form that is actually playable.
Here’s everything that I think I understand~
---
I remember reading a lot of speculation about how IASA frames couldn't be responsible for Melee Impossible. I know very little about IASA frames, or subactions for that matter. I think I remember people saying IASA frames would only work easily with normals, or something.
Ant-D claims in his video description (and paraphrases in his blog) that he “hacked/modified the game so that it is possible to L-Cancel any move whatsoever. This includes everything from running, B-specials, and grounded smash attacks.”
As far as I can tell, 0x904 has nothing to do with IASA frames. I could be wrong of course, but I don’t even know what it has to do with action states, really; as it appears to be a part of a function responsible for blending some animation keyframes.
Regardless, it can be applied to any action state just as Ant-D reported his hack could.
He also mentioned in his thread that he wouldn’t release his ASM code because his “‘mod is only stable when playing the game frame by frame. It will crash on different action states otherwise.”
This seemed to confuse some people, but makes perfect sense if you have the code to play around with.
I believe that by writing this 0x904 poke in a hook to the L/R button-press code in the main character loop, you end up with a cute little gecko code that perfectly resembles Melee Impossible. Verbatim! So try it yourself, if you’d like:
The problem is that canceling from many specific action states (referred to as “problem states” in my notes) causes a consistent freezing behavior. One of these problem states is the falling state--in any of its forms.
The function that writes the digitized analog values as button presses to char data offset 0x6BC every frame is where our hook is (light L/R presses are represented as an ‘8’ in the leftmost nibble of the value stored in 0x6BC); and so our cancel code (as it is currently handled) gets executed every frame that the button is held down.
This results in a really common freezing behavior while holding L/R for any longer than a single frame in the air--as the action state that follows a canceled aerial action is a problem state (falling.) Here’s a slightly fleshed out list of the states that cause freezing behavior:
---
So, what can we do about these instabilities? It would be fairly simple to add conditions to the poke that act like filters for problem states, but this presents a new issue where “blacklisted” problem states are then completely unaffected by the cancel code.
This means that every blacklisted state limits the scope of the Impossible Cancel. It does however stop the freezing behavior and make things playable as demonstrated by this slight modification to the last code:
This only covers a small bracket of action state IDs that account for falling states. You can try it in place of the 0.1.1 code though, and then not have to worry about freezing aerial attacks that wouldn’t otherwise be problematic.
For a long time, I used this code to test everything. I took a couple passes at each of the characters and searched for each of their problem states to be recorded in this document. This uncovered a second issue with this blacklist method, which is that there are simply too many problem states to individually blacklist using conditions like these.
You see, action states past 0x155 on the index are assigned uniquely by character, and so the conditions list becomes 2-dimensional. Specifying the characterID, actionstateID pairs necessary to identify each problem state results in an impractically long code because it has to account for (at least) each of the following:
I’m still learning how to read and write this stuff, but I made an ammature attempt to fit all of this information into a series of LONG variables and have a loop just read them off like normal conditional checks. I don’t recommend trying it, but I did leave some heavy comments in my dev journal if you’d like to review it.
It’s quite large, but actually pretty stable. Blacklisting every problem state by hand is obviously an accountability though, and the blacklist is far from perfect. In the end, I think this approach is too aggressive to be practical.
---
Let’s back up again. If we take a look at how 0x904 behaves, we can exploit something that provides a far more passive approach to fixing this problem.
0x904 appears to store a non-0 float value that determines the duration (in frames) of blended keyframe animations. These blending frames only seem to appear in some actions that revert back into standing or running states, as well as Peach’s float animation. They also appear in a select few B-specials.
0x908--the address just next-door--increments each frame (usually in whole float numbers) so long as 0x904 is not equal to 0. This increment stops once it is greater than or equal to the number stored in 0x904.
In other words, 0x904 defines a timer, and 0x908 increments it. This most commonly appears in the standing animation.
The timer appears to control a blending effect that uses a keyframe (which is probably stored somewhere) from a previous animation to blend with a frame from a new animation.
Canceling the “squat” action so far has provided the best demonstration of this effect that I’ve been able to reproduce. Normally a standing or running animation will use 6.0 frames (0x40c00000) for the blend duration. For some reason, the crouching animation uses 30.0 frames (0x41f00000) and applies it to the animation AFTER the squat (idle crouching.) By canceling the squat action with a 0x904 poke, you can apply those naturally occurring 30.0 frames of blending to the squat (transition) animation instead of the idle crouch--and the effect becomes very dramatic.
Poking 0x904 will have one of the 3 following effects:
When this keyframe blend function is not being used (which is most of the time,) setting the value of 0x904 to any non-0 value will set up the cancel effect on the following frame.
When poking 0x908 (the current blend frame) along with 0x904 (blend duration) so that 0x908 == 0x904; the effect of the cancel is exactly the same, however instead of freezing during problem states, the blend process will lock the animation of the state as the last blended frame (usually standing, running, or falling.) This removes the freezing behavior from all common problem states, including all* B-specials, and repairs itself on the next action state load--which appears to function as normal.
* Sheik is a sole exception from my playtesting; her chain whip freezes appear to be related to falling freezes.
Taking another pass at all of the characters using pokes to both 0x904 and 0x908 simultaneously, I made an updated document that accounts for the few remaining problem states (as I could find them.)
As you can see, the only remaining freezing behaviors are falling states and Sheik’s chain whip animations. Everything else is a state that I’ve identified as a “glitch” state.
It’s a little amusing to me that so many of the vanilla glitches can be accounted for by premature cancels. Many of them can be replicated via the Impossible Cancel, and so the game-breaking ones are represented in the above summary as purple.
It's interesting to note that with the 0x908 exploit, falling states can be canceled (and have a frozen animation, like other corrected problem states) only if there is absolutely no directional input. This doesn’t happen with a bare 0x904 poke, and I believe it may be an important clue.
If you don’t account for the glitch states, falling animations and Sheik’s weird input for her chain whip animation remain as the sole freezing behaviors that can be accounted for via a blacklist condition. A code written under these pretenses is consequently very small:
This small version accounts for none of the glitch states, so it can be pretty unplayable with some characters. There's also one more freezing behavior. It’s the only other one I could identify, and it can’t be covered up with the 0x908 exploit. It’s also kind of weird.
To reproduce it in the above code, pick a former problem state like Pikachu’s charge/thunder attack, or Bowser’s down-B attack. While holding shield (light or heavy,) without rolling or dodging, do a directional shield jump into an aerial containing the former problem state before canceling the jump as you leave the ground.
If you did this correctly, the game should freeze. It happens accidentally quite often with any characters that had former problem states, and I believe it has something to do with shield angle, or the directional jump animation.
The reason I suspect this is because the only other 2 remaining freezing behaviors are Sheik’s whip and directional falling animations. All of these 3 behaviors seem to have their animations permuted by directional inputs, and appear to use no subactions.
I don’t know how to fix this, but I do know that this commonality might help us eventually find a solution.
With the exception of this one freezing behavior; the loop written for 0.1.3b can be refined to include only falling, the glitch states, and Sheik’s chain states.
To ease the potential of accidentally canceling shields, as well as the general input problems with using L/R as the cancel button during ground attacks… all versions of 0.1.4 disable the light shield mechanic of the game in favor of the cancel function. Eventually, I hope to refine this into a more elegant solution.
In addition, I’ve attempted to create a semi-automatic input method. The cancel effect cycles action states along their natural progression rather than immediately jumping to a standing state. This means that by simply writing out the logic for semi-automatic inputs, you end up having to press the button several times to progress through a single move that uses multiple actions…
I thought this might be desirable in some aspects, but in others it’s very cumbersome. Instead, what I did was create a semi-automatic trigger that uses a flag to start automatically canceling every single frame; whether the button is held down or not. This involved changing the position of the hook slightly.
Then, by including all standing-like states in the blacklist, reaching a standing-like state will stop the automatic canceling every frame. The resulting effect is a semi-automatic trigger that progresses through entire moves rather than single actions. It’s experimental, and it does make some really technical cancels impossible; but I think it makes inputs less cumbersome in general.
Heavy shields still work; but they also may trigger the above mentioned freezing behavior. If you intend to use my sloppy ASM proof-of-concept in actual gameplay, be prepared with savestates and avoid canceling jumps out of your shield:
Here’s a dumb video I quickly made awhile back to demonstrate a few of the vanilla glitches I’ve kept in this version. They are mostly harmless, but there’s quite a lot of incredibly stupid things you can do with them. Note that the soulbreaker glitches are actually blacklisted; only demonstrated
---
That about wraps up everything I’ve done to make Ant-D’s concept more playable. I intend to further explore directional inputs and their effects on animations--as well as some other addresses referenced in the functions that use 0x904.
---
I have some remaining notes that are more or less just about the functionality of this cancel concept.
The above examples address the majority of freezing behaviors, but in a way that doesn’t allow for the involved actions to be canceled. This is preferable to freezing the game of course, but it limits the application of this cancel effect.
Earlier on--when I was still learning how to make sense of PPC instructions--I was searching for addresses in undocumented areas of the RAM Address List in Dolphin for any correlation to problem states. I found a relationship in address 0x444, which appears to be an address that stores floats for a timer function involved in queuing subaction commands using synchronous and asynchronous timers.
No new subactions are read while the timer in 0x444 is counting down. I didn’t really understand this at first, but I did see it counting down--so I experimented by poking the value to 0 on top of my 0x904 poke. It had very interesting effects on the character I was testing at the time:
Suddenly, with this extra poke, all of the former problem states for Pikachu responded to the cancel effect...
Lets take a quick look at 0x44C, a close neighbor of 0x444. I was quite excited to find this one.
0x44C points to a position in a list that looks like this:
After comparing this to some data in Crazy Hand, it dawned on me that 0x44C points to the next subaction command.
And indeed, you can manipulate subactions by manipulating this pointer. I tested this very briefly by identifying Marth’s down-a IASA frame command, and pointing to it in 0x44C after pausing in the middle of his smash attack. This resulted in allowing me to cleanly start a new smash attack like a natural IASA frame.
I believe this means that you could (in theory) save the address currently in 0x44C somewhere and replace it with a pointer to some fake subactions you’ve written in another area in memory; using the old 0x44C pointer as a return branch. It would allow you to append, skip, or replace subactions procedurally with code!
With an understanding of the syntax, it could also be used to monitor subactions as they are happening, perhaps utilizing them as complex conditions.
I imagine that with some knowledge of how subactions are encoded (which I expect a lot of you have) you could exploit this address for all kinds of beautiful things.
Anyway, I don’t want to stray too far from the topic. Brutishly exploiting 0x444 (specific to subaction timers) works wonders for some of the previously gimped Pikachu article spawns. It doesn’t however have the same effect on Link’s article spawns, for example.
In particular--hitboxes, article spawns, sound effects, particles and some character model flashes seem to rely on these “timer” subaction commands for progression through an action. Without any of these timers, all available subaction commands are read in the same frame; but they are still each individually read.
The number of these timer subactions present in a whole action determines how many "keyframes" are read at different times during the action. In other words, all subactions are instantaneous except for these timer subactions that write to 0x444.
It would appear that a single 0x444 poke is all Pikachu's moves need to skip the meat of their action, but not Link's. I'll have to confirm this after taking a long look at their subactions with 0x44C.
I would offer some documentation for this code, but it's the exact same code as 0.1.4b, except that it pokes 0x444 to 0 on top of the 0x904/0x908 pokes.
I wrote a really buggy variation of 0.1.4c that tries to use a second hook inside of the function responsible for this subaction timer. The idea was to use the flag I’d made for the semi-auto input as a trigger to poke 0x444 to 0 instead of 0x904 to 1. Then, the second hook inside the subaction function (within the same frame) recognizes the flag stored in the first hook code as a LONG variable, and continuously pokes 0x444 to 0 every time it is executed (once per subaction command.) This becomes self perpetuating because of the location of the second hook.
Once the subaction command “00000000” (padding between whole actions) is read in by 0x44C, the loop automatically terminates, so (in theory) this should completely “flush” subactions in a single frame. Once 0x44C is empty, only then does 0x904 get poked to 1 in order to invoke the cancel effect.
I remember trying this experiment very late at night and having abysmal results that I didn’t want to debug; so I ended up with a really buggy code that wasn’t playable. It did at least allow me to make this TAS video, where Link’s article spawns are NOT cut off by the cancel.
As always you’re welcome to try it… but it’s quite unplayable. Something about the way I wrote it breaks the logic for the blacklist, and so it makes aerials freeze the game all over again. I think it has to do with the 0x444 poke to 0 transition into the second hook, as subactions often seem to “disappear” in all actions before the next former-problem state triggers a freeze.
It’s interesting to see what actions are like without any subactions though (not really,) and it’s possible to test it with TAS if you’re interested.
While this obviously appears to work with Link, I guess it isn't quite the solution. Where a single 0x444 poke would allow for Pikachu to spam his thunder and Link to only cancel his bombs; a continuous 0x444 poke allows for Link to spam his bombs, and Pikachu to only cancel his thunder.
I think I'll soon try using 0x44C as a replacement for action state ID and character ID checks. I'd like to see if everything (with the exception of the falling states and Sheik's whip) can be covered by a subaction blacklist.
Also, this subaction “flush” might be refined with a more intelligent condition to allow for all article spawns to resolve before cancels; and possibly even other things like hitboxes.
--
Finally, a little footnote about the case where 0x904 is already storing a float number. I mentioned this early on, but normally 0x904 only stores a float value during something like a standing action; so there’s no real reason to cancel these as they’ll only start a new standing action. There are however a few B-specials that use blending frames like this, and poking 0x904 to 1 will not allow these states to be canceled like everything else can.
Off the top of my head, the only 3 B-special actions effected by this are Marth/Roy’s side-B swipes (2nd, 3rd, and 4th only,) Luigi’s Green Missile, and the Ice Climbers’ recovery animation.
Each of these cases can still be canceled by poking 0x904 to 0 instead of 1. I haven’t included this in any of my codes, but I think it could be very simply implemented. One weird thing to consider is that sometimes, doing this in a standing animation will blend with a T-pose.
---
Anyway, that’s about everything I’ve got so far. I have a lot to learn still, but I hope that this information is sufficient enough to compensate for the apparent lack thereof surrounding this cancel concept. If anyone has any comments, suggestions, or critiques I’ll do my best to respond in a helpful manner and edit any information accordingly.
Also, feel free to play around with any of these codes/concepts and discuss your own ideas. I think there’s a lot left to be discovered about this address and its related mechanics.
Has anyone played around with Character Data offset 0x904 (from the RAM Address List?)
Poking it lets you cancel your action state on the next frame, and it isn’t picky about what actions it can be applied to. It’s a lot of fun, really unstable, and was virtually undocumented the last time I checked these forums.
Somebody left a note about it in the RAM Address List, but it just mentioned that poking it to “40c00000” (float “6.0”) will cancel the current action state. Indeed it does, and in fact any non-0 value will work--so for the sake of simplicity I’ve used “1” throughout my examples here.
I found this board sometime back in April 2015 while searching for how the Melee Impossible video was … possible. My search turned up very little, so I watched Dan’s excellent primer on writing Gecko Codes and meant to try and figure it out. (Thanks Dan!)
It didn’t take long for me to stumble upon 0x904 in the RAM Address List, so I’m assuming some of you have at least tried poking it.
I’ve spent some time experimenting with it and now I have a lot of information that doesn’t seem to be documented anywhere here. Admittedly, I don’t frequent these forums very often so I could be mistaken. I searched pretty fervently back in April-May for some answers however, and found no information.
Now that there’s a nice little sub-forum dedicated to posting codes, I figured this would be a great place to dump some info and maybe collaborate a little. My intention is to reproduce Melee Impossible in a form that is actually playable.
Here’s everything that I think I understand~
---
I remember reading a lot of speculation about how IASA frames couldn't be responsible for Melee Impossible. I know very little about IASA frames, or subactions for that matter. I think I remember people saying IASA frames would only work easily with normals, or something.
Ant-D claims in his video description (and paraphrases in his blog) that he “hacked/modified the game so that it is possible to L-Cancel any move whatsoever. This includes everything from running, B-specials, and grounded smash attacks.”
As far as I can tell, 0x904 has nothing to do with IASA frames. I could be wrong of course, but I don’t even know what it has to do with action states, really; as it appears to be a part of a function responsible for blending some animation keyframes.
Regardless, it can be applied to any action state just as Ant-D reported his hack could.
He also mentioned in his thread that he wouldn’t release his ASM code because his “‘mod is only stable when playing the game frame by frame. It will crash on different action states otherwise.”
This seemed to confuse some people, but makes perfect sense if you have the code to play around with.
I believe that by writing this 0x904 poke in a hook to the L/R button-press code in the main character loop, you end up with a cute little gecko code that perfectly resembles Melee Impossible. Verbatim! So try it yourself, if you’d like:
$Impossible Cancel v0.1.1 (Melee 1.02) [Punkline]
C206B0A4 00000002
38600001 907E0904
901F065C 00000000
C206B0A4 00000002
38600001 907E0904
901F065C 00000000
The problem is that canceling from many specific action states (referred to as “problem states” in my notes) causes a consistent freezing behavior. One of these problem states is the falling state--in any of its forms.
The function that writes the digitized analog values as button presses to char data offset 0x6BC every frame is where our hook is (light L/R presses are represented as an ‘8’ in the leftmost nibble of the value stored in 0x6BC); and so our cancel code (as it is currently handled) gets executed every frame that the button is held down.
This results in a really common freezing behavior while holding L/R for any longer than a single frame in the air--as the action state that follows a canceled aerial action is a problem state (falling.) Here’s a slightly fleshed out list of the states that cause freezing behavior:
- Falling animations
- (these animations are permuted by DI inputs)
- (note that the index of action state IDs includes a forward/back falling ID for each type of falling; but these seem to be vestigial as they appear unused by any of the normal characters)
- Flinching and being grabbed.
- Looping animations.
- Animations with a “wait” state (some charge animations, or pikachu’s thunder)
- (note that this surprisingly doesn’t include charged smashes)
- GaW running animation (appears to have no blended keyframes in movement animations, as other characters do)
- Sheik’s Chain (DI inputs determine the animation and function of this action, just like falling animations)
- Mewtwo/Zelda/Sheik teleport landing on platform (with horizontal momentum)
- Probably a bunch of other stuff that might be difficult to test.
---
So, what can we do about these instabilities? It would be fairly simple to add conditions to the poke that act like filters for problem states, but this presents a new issue where “blacklisted” problem states are then completely unaffected by the cancel code.
This means that every blacklisted state limits the scope of the Impossible Cancel. It does however stop the freezing behavior and make things playable as demonstrated by this slight modification to the last code:
$Impossible Cancel v0.1.2 (Melee 1.02) [Punkline]
C206B0A4 00000005
807E0070 2C03001D
4180000C 2C030026
4081000C 38600001
907E0904 901F065C
60000000 00000000
C206B0A4 00000005
807E0070 2C03001D
4180000C 2C030026
4081000C 38600001
907E0904 901F065C
60000000 00000000
This only covers a small bracket of action state IDs that account for falling states. You can try it in place of the 0.1.1 code though, and then not have to worry about freezing aerial attacks that wouldn’t otherwise be problematic.
For a long time, I used this code to test everything. I took a couple passes at each of the characters and searched for each of their problem states to be recorded in this document. This uncovered a second issue with this blacklist method, which is that there are simply too many problem states to individually blacklist using conditions like these.
You see, action states past 0x155 on the index are assigned uniquely by character, and so the conditions list becomes 2-dimensional. Specifying the characterID, actionstateID pairs necessary to identify each problem state results in an impractically long code because it has to account for (at least) each of the following:
I’m still learning how to read and write this stuff, but I made an ammature attempt to fit all of this information into a series of LONG variables and have a loop just read them off like normal conditional checks. I don’t recommend trying it, but I did leave some heavy comments in my dev journal if you’d like to review it.
$Impossible Cancel v0.1.3b (Melee 1.02) [Punkline]
C206B0A4 0000003A
480000E0 00000015
001D0026 00300030
004B005B 00910093
009F009F 00A300A3
00B700B7 00BF00BF
00CE00E3 00ED00FD
010A0154 01166364
696C6E71 03037277
04045556 57585962
64666768 696A6B6C
6D6E7476 78798185
86898A8D 05055659
5E5F6064 65666A06
14595C5E 61070756
5A5D5E60 61080857
5A5D5E61 62676B70
75090962 65720A0B
55560C17 585B5D60
68696C6D 0D0D580E
0E5D6265 66696A6E
700F0F55 56575859
5C5D5E5F 6465666A
10105657 5B5C5F60
1111585B 5C5E6162
121A565A 1313585B
18185700 309700C8
00000000 00000000
00000000 00000000
7C6802A6 48000005
7FE802A6 7C6803A6
3BFFFFE4 929F0004
92DF0008 92FF000C
931F0010 82DE0070
82FE0064 8A9F0000
A31F0002 7F18F850
A0780000 7C161800
41800010 A0780002
7C161800 40810080
3A94FFFC 3B180004
2C140000 4181FFDC
8A9F0001 88780000
7C171800 40820030
8C780001 2C030020
4081FFF8 3A800000
2C030020 40810018
60630100 7C161800
8C780001 41820038
4BFFFFE8 3A94FFFF
3B180001 2C140000
4181FFBC 2C170009
4182000C 2C170013
4082000C 2C160027
4182000C 38600001
907E0904 829F0004
82DF0008 82FF000C
831F0010 3BFE0060
901F065C 38600000
987F065C 00000000
C206B0A4 0000003A
480000E0 00000015
001D0026 00300030
004B005B 00910093
009F009F 00A300A3
00B700B7 00BF00BF
00CE00E3 00ED00FD
010A0154 01166364
696C6E71 03037277
04045556 57585962
64666768 696A6B6C
6D6E7476 78798185
86898A8D 05055659
5E5F6064 65666A06
14595C5E 61070756
5A5D5E60 61080857
5A5D5E61 62676B70
75090962 65720A0B
55560C17 585B5D60
68696C6D 0D0D580E
0E5D6265 66696A6E
700F0F55 56575859
5C5D5E5F 6465666A
10105657 5B5C5F60
1111585B 5C5E6162
121A565A 1313585B
18185700 309700C8
00000000 00000000
00000000 00000000
7C6802A6 48000005
7FE802A6 7C6803A6
3BFFFFE4 929F0004
92DF0008 92FF000C
931F0010 82DE0070
82FE0064 8A9F0000
A31F0002 7F18F850
A0780000 7C161800
41800010 A0780002
7C161800 40810080
3A94FFFC 3B180004
2C140000 4181FFDC
8A9F0001 88780000
7C171800 40820030
8C780001 2C030020
4081FFF8 3A800000
2C030020 40810018
60630100 7C161800
8C780001 41820038
4BFFFFE8 3A94FFFF
3B180001 2C140000
4181FFBC 2C170009
4182000C 2C170013
4082000C 2C160027
4182000C 38600001
907E0904 829F0004
82DF0008 82FF000C
831F0010 3BFE0060
901F065C 38600000
987F065C 00000000
It’s quite large, but actually pretty stable. Blacklisting every problem state by hand is obviously an accountability though, and the blacklist is far from perfect. In the end, I think this approach is too aggressive to be practical.
---
Let’s back up again. If we take a look at how 0x904 behaves, we can exploit something that provides a far more passive approach to fixing this problem.
0x904 appears to store a non-0 float value that determines the duration (in frames) of blended keyframe animations. These blending frames only seem to appear in some actions that revert back into standing or running states, as well as Peach’s float animation. They also appear in a select few B-specials.
0x908--the address just next-door--increments each frame (usually in whole float numbers) so long as 0x904 is not equal to 0. This increment stops once it is greater than or equal to the number stored in 0x904.
In other words, 0x904 defines a timer, and 0x908 increments it. This most commonly appears in the standing animation.
The timer appears to control a blending effect that uses a keyframe (which is probably stored somewhere) from a previous animation to blend with a frame from a new animation.
Canceling the “squat” action so far has provided the best demonstration of this effect that I’ve been able to reproduce. Normally a standing or running animation will use 6.0 frames (0x40c00000) for the blend duration. For some reason, the crouching animation uses 30.0 frames (0x41f00000) and applies it to the animation AFTER the squat (idle crouching.) By canceling the squat action with a 0x904 poke, you can apply those naturally occurring 30.0 frames of blending to the squat (transition) animation instead of the idle crouch--and the effect becomes very dramatic.
Poking 0x904 will have one of the 3 following effects:
- If 0x904 is already 0--which is most of the time--poking it to ANY non-0 value has the effect of knocking the player out of their current action state
- If 0x904 is storing a value, poking it to any non-0 value changes the number of frames that the current animation gets blended with the last one. Action states are NOT canceled under these circumstances.
- Poking it to 0 in these cases cancels the animation.
When this keyframe blend function is not being used (which is most of the time,) setting the value of 0x904 to any non-0 value will set up the cancel effect on the following frame.
When poking 0x908 (the current blend frame) along with 0x904 (blend duration) so that 0x908 == 0x904; the effect of the cancel is exactly the same, however instead of freezing during problem states, the blend process will lock the animation of the state as the last blended frame (usually standing, running, or falling.) This removes the freezing behavior from all common problem states, including all* B-specials, and repairs itself on the next action state load--which appears to function as normal.
* Sheik is a sole exception from my playtesting; her chain whip freezes appear to be related to falling freezes.
Taking another pass at all of the characters using pokes to both 0x904 and 0x908 simultaneously, I made an updated document that accounts for the few remaining problem states (as I could find them.)
As you can see, the only remaining freezing behaviors are falling states and Sheik’s chain whip animations. Everything else is a state that I’ve identified as a “glitch” state.
It’s a little amusing to me that so many of the vanilla glitches can be accounted for by premature cancels. Many of them can be replicated via the Impossible Cancel, and so the game-breaking ones are represented in the above summary as purple.
It's interesting to note that with the 0x908 exploit, falling states can be canceled (and have a frozen animation, like other corrected problem states) only if there is absolutely no directional input. This doesn’t happen with a bare 0x904 poke, and I believe it may be an important clue.
If you don’t account for the glitch states, falling animations and Sheik’s weird input for her chain whip animation remain as the sole freezing behaviors that can be accounted for via a blacklist condition. A code written under these pretenses is consequently very small:
$Impossible Cancel v0.1.4a (Melee 1.02) [Punkline]
C206B090 0000000D
831E06B0 2C180000
41820054 831E0070
2C180026 40810040
2C18015D 41820020
2C18015E 41820018
2C180160 41820010
2C180161 41820008
48000010 831E0064
2C180007 41820010
3B000001 931E0904
931E0908 7FD8F378
3BFE0060 C0228874
60000000 00000000
C206B090 0000000D
831E06B0 2C180000
41820054 831E0070
2C180026 40810040
2C18015D 41820020
2C18015E 41820018
2C180160 41820010
2C180161 41820008
48000010 831E0064
2C180007 41820010
3B000001 931E0904
931E0908 7FD8F378
3BFE0060 C0228874
60000000 00000000
This small version accounts for none of the glitch states, so it can be pretty unplayable with some characters. There's also one more freezing behavior. It’s the only other one I could identify, and it can’t be covered up with the 0x908 exploit. It’s also kind of weird.
To reproduce it in the above code, pick a former problem state like Pikachu’s charge/thunder attack, or Bowser’s down-B attack. While holding shield (light or heavy,) without rolling or dodging, do a directional shield jump into an aerial containing the former problem state before canceling the jump as you leave the ground.
If you did this correctly, the game should freeze. It happens accidentally quite often with any characters that had former problem states, and I believe it has something to do with shield angle, or the directional jump animation.
The reason I suspect this is because the only other 2 remaining freezing behaviors are Sheik’s whip and directional falling animations. All of these 3 behaviors seem to have their animations permuted by directional inputs, and appear to use no subactions.
I don’t know how to fix this, but I do know that this commonality might help us eventually find a solution.
With the exception of this one freezing behavior; the loop written for 0.1.3b can be refined to include only falling, the glitch states, and Sheik’s chain states.
To ease the potential of accidentally canceling shields, as well as the general input problems with using L/R as the cancel button during ground attacks… all versions of 0.1.4 disable the light shield mechanic of the game in favor of the cancel function. Eventually, I hope to refine this into a more elegant solution.
In addition, I’ve attempted to create a semi-automatic input method. The cancel effect cycles action states along their natural progression rather than immediately jumping to a standing state. This means that by simply writing out the logic for semi-automatic inputs, you end up having to press the button several times to progress through a single move that uses multiple actions…
I thought this might be desirable in some aspects, but in others it’s very cumbersome. Instead, what I did was create a semi-automatic trigger that uses a flag to start automatically canceling every single frame; whether the button is held down or not. This involved changing the position of the hook slightly.
Then, by including all standing-like states in the blacklist, reaching a standing-like state will stop the automatic canceling every frame. The resulting effect is a semi-automatic trigger that progresses through entire moves rather than single actions. It’s experimental, and it does make some really technical cancels impossible; but I think it makes inputs less cumbersome in general.
Heavy shields still work; but they also may trigger the above mentioned freezing behavior. If you intend to use my sloppy ASM proof-of-concept in actual gameplay, be prepared with savestates and avoid canceling jumps out of your shield:
$Impossible Cancel v0.1.4b (Melee 1.02) [Punkline]
C206B090 0000002F
48000058 000E0016
001D0026 00CD00CE
00DB00DE 040F5556
57585986 8905055F
60656606 145E6107
075D5E60 610A0B55
560E0E5D 626E1010
5F600000 00000000
10260038 00000000
00000000 00000000
00000000 00000000
7F0802A6 48000005
7FE802A6 7F0803A6
3BFFFFDC 831E06B0
2C180000 418200F4
929F0008 92DF000C
92FF0010 933F0014
92BF0018 8ABE006C
7EB5FA14 831E06B4
2C180000 4082000C
3B000001 9B150000
8B150000 2C180001
408200A4 82DE0070
82FE0064 8A9F0004
A33F0006 7F39F850
A3190000 7C16C000
41800010 A3190002
7C16C000 40810070
3A94FFFC 3B390004
2C140000 4181FFDC
8A9F0005 8B190000
7C17C000 40820030
8F190001 2C180020
4081FFF8 3A800000
2C180020 40810018
63180100 7C16C000
8F190001 41820028
4BFFFFE8 3A94FFFF
3B390001 2C140000
4181FFBC 3B000001
931E0904 931E0908
4800000C 3B000000
9B1F0000 829F0008
82DF000C 82FF0010
833F0014 82BF0018
3BFE0060 7FD8F378
C0228874 00000000
C206B090 0000002F
48000058 000E0016
001D0026 00CD00CE
00DB00DE 040F5556
57585986 8905055F
60656606 145E6107
075D5E60 610A0B55
560E0E5D 626E1010
5F600000 00000000
10260038 00000000
00000000 00000000
00000000 00000000
7F0802A6 48000005
7FE802A6 7F0803A6
3BFFFFDC 831E06B0
2C180000 418200F4
929F0008 92DF000C
92FF0010 933F0014
92BF0018 8ABE006C
7EB5FA14 831E06B4
2C180000 4082000C
3B000001 9B150000
8B150000 2C180001
408200A4 82DE0070
82FE0064 8A9F0004
A33F0006 7F39F850
A3190000 7C16C000
41800010 A3190002
7C16C000 40810070
3A94FFFC 3B390004
2C140000 4181FFDC
8A9F0005 8B190000
7C17C000 40820030
8F190001 2C180020
4081FFF8 3A800000
2C180020 40810018
63180100 7C16C000
8F190001 41820028
4BFFFFE8 3A94FFFF
3B390001 2C140000
4181FFBC 3B000001
931E0904 931E0908
4800000C 3B000000
9B1F0000 829F0008
82DF000C 82FF0010
833F0014 82BF0018
3BFE0060 7FD8F378
C0228874 00000000
Here’s a dumb video I quickly made awhile back to demonstrate a few of the vanilla glitches I’ve kept in this version. They are mostly harmless, but there’s quite a lot of incredibly stupid things you can do with them. Note that the soulbreaker glitches are actually blacklisted; only demonstrated
---
That about wraps up everything I’ve done to make Ant-D’s concept more playable. I intend to further explore directional inputs and their effects on animations--as well as some other addresses referenced in the functions that use 0x904.
---
I have some remaining notes that are more or less just about the functionality of this cancel concept.
The above examples address the majority of freezing behaviors, but in a way that doesn’t allow for the involved actions to be canceled. This is preferable to freezing the game of course, but it limits the application of this cancel effect.
Earlier on--when I was still learning how to make sense of PPC instructions--I was searching for addresses in undocumented areas of the RAM Address List in Dolphin for any correlation to problem states. I found a relationship in address 0x444, which appears to be an address that stores floats for a timer function involved in queuing subaction commands using synchronous and asynchronous timers.
No new subactions are read while the timer in 0x444 is counting down. I didn’t really understand this at first, but I did see it counting down--so I experimented by poking the value to 0 on top of my 0x904 poke. It had very interesting effects on the character I was testing at the time:
Suddenly, with this extra poke, all of the former problem states for Pikachu responded to the cancel effect...
Lets take a quick look at 0x44C, a close neighbor of 0x444. I was quite excited to find this one.
0x44C points to a position in a list that looks like this:
After comparing this to some data in Crazy Hand, it dawned on me that 0x44C points to the next subaction command.
And indeed, you can manipulate subactions by manipulating this pointer. I tested this very briefly by identifying Marth’s down-a IASA frame command, and pointing to it in 0x44C after pausing in the middle of his smash attack. This resulted in allowing me to cleanly start a new smash attack like a natural IASA frame.
I believe this means that you could (in theory) save the address currently in 0x44C somewhere and replace it with a pointer to some fake subactions you’ve written in another area in memory; using the old 0x44C pointer as a return branch. It would allow you to append, skip, or replace subactions procedurally with code!
With an understanding of the syntax, it could also be used to monitor subactions as they are happening, perhaps utilizing them as complex conditions.
I imagine that with some knowledge of how subactions are encoded (which I expect a lot of you have) you could exploit this address for all kinds of beautiful things.
Anyway, I don’t want to stray too far from the topic. Brutishly exploiting 0x444 (specific to subaction timers) works wonders for some of the previously gimped Pikachu article spawns. It doesn’t however have the same effect on Link’s article spawns, for example.
In particular--hitboxes, article spawns, sound effects, particles and some character model flashes seem to rely on these “timer” subaction commands for progression through an action. Without any of these timers, all available subaction commands are read in the same frame; but they are still each individually read.
The number of these timer subactions present in a whole action determines how many "keyframes" are read at different times during the action. In other words, all subactions are instantaneous except for these timer subactions that write to 0x444.
It would appear that a single 0x444 poke is all Pikachu's moves need to skip the meat of their action, but not Link's. I'll have to confirm this after taking a long look at their subactions with 0x44C.
I would offer some documentation for this code, but it's the exact same code as 0.1.4b, except that it pokes 0x444 to 0 on top of the 0x904/0x908 pokes.
$Impossible Cancel v0.1.4c (Melee 1.02) [Punkline]
C206B090 00000030
48000058 000E0016
001D0026 00CD00CE
00DB00DE 040F5556
57585986 8905055F
60656606 145E6107
075D5E60 610A0B55
560E0E5D 626E1010
5F600000 00000000
10260038 00000000
00000000 00000000
00000000 00000000
7F0802A6 48000005
7FE802A6 7F0803A6
3BFFFFDC 831E06B0
2C180000 418200FC
929F0008 92DF000C
92FF0010 933F0014
92BF0018 8ABE006C
7EB5FA14 831E06B4
2C180000 4082000C
3B000001 9B150000
8B150000 2C180001
408200AC 82DE0070
82FE0064 8A9F0004
A33F0006 7F39F850
A3190000 7C16C000
41800010 A3190002
7C16C000 40810078
3A94FFFC 3B390004
2C140000 4181FFDC
8A9F0005 8B190000
7C17C000 40820030
8F190001 2C180020
4081FFF8 3A800000
2C180020 40810018
63180100 7C16C000
8F190001 41820030
4BFFFFE8 3A94FFFF
3B390001 2C140000
4181FFBC 3B000001
931E0904 931E0908
3B000000 931E0444
4800000C 3B000000
9B1F0000 829F0008
82DF000C 82FF0010
833F0014 82BF0018
3BFE0060 7FD8F378
C0228874 00000000
C206B090 00000030
48000058 000E0016
001D0026 00CD00CE
00DB00DE 040F5556
57585986 8905055F
60656606 145E6107
075D5E60 610A0B55
560E0E5D 626E1010
5F600000 00000000
10260038 00000000
00000000 00000000
00000000 00000000
7F0802A6 48000005
7FE802A6 7F0803A6
3BFFFFDC 831E06B0
2C180000 418200FC
929F0008 92DF000C
92FF0010 933F0014
92BF0018 8ABE006C
7EB5FA14 831E06B4
2C180000 4082000C
3B000001 9B150000
8B150000 2C180001
408200AC 82DE0070
82FE0064 8A9F0004
A33F0006 7F39F850
A3190000 7C16C000
41800010 A3190002
7C16C000 40810078
3A94FFFC 3B390004
2C140000 4181FFDC
8A9F0005 8B190000
7C17C000 40820030
8F190001 2C180020
4081FFF8 3A800000
2C180020 40810018
63180100 7C16C000
8F190001 41820030
4BFFFFE8 3A94FFFF
3B390001 2C140000
4181FFBC 3B000001
931E0904 931E0908
3B000000 931E0444
4800000C 3B000000
9B1F0000 829F0008
82DF000C 82FF0010
833F0014 82BF0018
3BFE0060 7FD8F378
C0228874 00000000
I wrote a really buggy variation of 0.1.4c that tries to use a second hook inside of the function responsible for this subaction timer. The idea was to use the flag I’d made for the semi-auto input as a trigger to poke 0x444 to 0 instead of 0x904 to 1. Then, the second hook inside the subaction function (within the same frame) recognizes the flag stored in the first hook code as a LONG variable, and continuously pokes 0x444 to 0 every time it is executed (once per subaction command.) This becomes self perpetuating because of the location of the second hook.
Once the subaction command “00000000” (padding between whole actions) is read in by 0x44C, the loop automatically terminates, so (in theory) this should completely “flush” subactions in a single frame. Once 0x44C is empty, only then does 0x904 get poked to 1 in order to invoke the cancel effect.
I remember trying this experiment very late at night and having abysmal results that I didn’t want to debug; so I ended up with a really buggy code that wasn’t playable. It did at least allow me to make this TAS video, where Link’s article spawns are NOT cut off by the cancel.
As always you’re welcome to try it… but it’s quite unplayable. Something about the way I wrote it breaks the logic for the blacklist, and so it makes aerials freeze the game all over again. I think it has to do with the 0x444 poke to 0 transition into the second hook, as subactions often seem to “disappear” in all actions before the next former-problem state triggers a freeze.
It’s interesting to see what actions are like without any subactions though (not really,) and it’s possible to test it with TAS if you’re interested.
$Impossible Cancel v0.1.4d (Melee 1.02) [Punkline]
C206B090 00000032
48000058 000E0016
001D0026 00CD00CE
00DB00DE 040F5556
57585986 8905055F
60656606 145E6107
075D5E60 610A0B55
560E0E5D 626E1010
5F600000 00000000
10260038 00000000
00000000 00000000
00000000 00000000
7F0802A6 48000005
7FE802A6 7F0803A6
3BFFFFDC 831E06B0
2C180000 41820108
929F0008 92DF000C
92FF0010 933F0014
92BF0018 8ABE006C
7EB5FA14 831E06B4
2C180000 4082000C
3B000001 9B150000
8B150000 2C180001
408200B8 82DE0444
2C160000 408200AC
82DE0070 82FE0064
8A9F0004 A33F0006
7F39F850 A3190000
7C16C000 41800010
A3190002 7C16C000
40810078 3A94FFFC
3B390004 2C140000
4181FFDC 8A9F0005
8B190000 7C17C000
40820030 8F190001
2C180020 4081FFF8
3A800000 2C180020
40810018 63180100
7C16C000 8F190001
41820030 4BFFFFE8
3A94FFFF 3B390001
2C140000 4181FFBC
3B000001 931E0904
931E0908 3F000000
931E0444 4800000C
3B000000 9B1F0000
829F0008 82DF000C
82FF0010 833F0014
82BF0018 3BFE0060
7FD8F378 C0228874
60000000 00000000
C20732B0 0000000A
7FC802A6 48000005
7FA802A6 7FC803A6
3BBDFE9C 939D0008
8BDB006C 2C1E0003
4181001C 7FDEEA14
8B9E0000 2C1C0001
4082000C 3F800000
939B0444 3BDB0060
839D0008 3BBB0444
28000000 00000000
C206B090 00000032
48000058 000E0016
001D0026 00CD00CE
00DB00DE 040F5556
57585986 8905055F
60656606 145E6107
075D5E60 610A0B55
560E0E5D 626E1010
5F600000 00000000
10260038 00000000
00000000 00000000
00000000 00000000
7F0802A6 48000005
7FE802A6 7F0803A6
3BFFFFDC 831E06B0
2C180000 41820108
929F0008 92DF000C
92FF0010 933F0014
92BF0018 8ABE006C
7EB5FA14 831E06B4
2C180000 4082000C
3B000001 9B150000
8B150000 2C180001
408200B8 82DE0444
2C160000 408200AC
82DE0070 82FE0064
8A9F0004 A33F0006
7F39F850 A3190000
7C16C000 41800010
A3190002 7C16C000
40810078 3A94FFFC
3B390004 2C140000
4181FFDC 8A9F0005
8B190000 7C17C000
40820030 8F190001
2C180020 4081FFF8
3A800000 2C180020
40810018 63180100
7C16C000 8F190001
41820030 4BFFFFE8
3A94FFFF 3B390001
2C140000 4181FFBC
3B000001 931E0904
931E0908 3F000000
931E0444 4800000C
3B000000 9B1F0000
829F0008 82DF000C
82FF0010 833F0014
82BF0018 3BFE0060
7FD8F378 C0228874
60000000 00000000
C20732B0 0000000A
7FC802A6 48000005
7FA802A6 7FC803A6
3BBDFE9C 939D0008
8BDB006C 2C1E0003
4181001C 7FDEEA14
8B9E0000 2C1C0001
4082000C 3F800000
939B0444 3BDB0060
839D0008 3BBB0444
28000000 00000000
While this obviously appears to work with Link, I guess it isn't quite the solution. Where a single 0x444 poke would allow for Pikachu to spam his thunder and Link to only cancel his bombs; a continuous 0x444 poke allows for Link to spam his bombs, and Pikachu to only cancel his thunder.
I think I'll soon try using 0x44C as a replacement for action state ID and character ID checks. I'd like to see if everything (with the exception of the falling states and Sheik's whip) can be covered by a subaction blacklist.
Also, this subaction “flush” might be refined with a more intelligent condition to allow for all article spawns to resolve before cancels; and possibly even other things like hitboxes.
--
Finally, a little footnote about the case where 0x904 is already storing a float number. I mentioned this early on, but normally 0x904 only stores a float value during something like a standing action; so there’s no real reason to cancel these as they’ll only start a new standing action. There are however a few B-specials that use blending frames like this, and poking 0x904 to 1 will not allow these states to be canceled like everything else can.
Off the top of my head, the only 3 B-special actions effected by this are Marth/Roy’s side-B swipes (2nd, 3rd, and 4th only,) Luigi’s Green Missile, and the Ice Climbers’ recovery animation.
Each of these cases can still be canceled by poking 0x904 to 0 instead of 1. I haven’t included this in any of my codes, but I think it could be very simply implemented. One weird thing to consider is that sometimes, doing this in a standing animation will blend with a T-pose.
---
Anyway, that’s about everything I’ve got so far. I have a lot to learn still, but I hope that this information is sufficient enough to compensate for the apparent lack thereof surrounding this cancel concept. If anyone has any comments, suggestions, or critiques I’ll do my best to respond in a helpful manner and edit any information accordingly.
Also, feel free to play around with any of these codes/concepts and discuss your own ideas. I think there’s a lot left to be discovered about this address and its related mechanics.
Update with information about how to use animation interrupts and player updates to make previous exploits much safer:
There is a table at and around external player data offset 0x2200 that contains function pointers which--collectively--describe a lot of the variable functionality of the current action state for an allocated player in game.
They look like this, and there are likely a lot more of them than I’ve listed:
- 0x21F0 - player grab? function pointer (I don’t know much about these)
- 0x21F4 - (somehow related to grabbing)
- 0x21F8 - held player behavior? function pointer
- 0x21FC - IASA function pointer (Input-based interrupts)
- >0x2200 - Animation interrupt function pointer (expiring state interrupts)
- 0x2204 - Player physics function pointer
- 0x2208 - Collisions function pointer
- 0x220C - Camera function pointer
Each and every one of these are used by an update function in order to link the functions they point to for the frame update via BLRL. The corresponding blrl locations, with screenshots:
- 0x221C - Character Article Function pointer? - appears to handle character article behavior of some kind… including the projectile flag set by player event 60.
- 0x21F0 - 8006cb00 (PlayerThink)
- 0x21F4 - 8006cb78 (PlayerThink)
- 0x21F8 - 8006cb14 (PlayerThink) This and the first one share a little structure
- 0x21FC - 8006b808 (PlayerThink_ControllerInputsToDataOffset)
- >0x2200 - 8006ab74 (PlayerThinkAgain)
- 0x2204 - 8006b8ac (PlayerThink_Physics)
- 0x2208 - 8006c3a4 (zz_006c27c)
- 0x220C - 8006da30 (zz_006d9ec)
In particular, for this code, the animation interrupt function used by the BLRL in “PlayerThinkAgain” is responsible for the “cancel” behavior. Knowing about this structure allows us to intercept the decision that the game makes about making action state transitions when a state expires.
- 0x221C - 8006c9c0 (zz_006c80c) (the projectile flag is located in 0x80 of byte 0x2270)
---
From what little I’ve been able to surmise, the only reason that the 0x904 exploit (gone over in some detail at the spoilered bottom of my update op) seems to work at all is because of this check:
![]()
This function gets called from most animation interrupt functions (and many other functions) presumably as a way of defining a frame at which the transition is supposed to happen. It compares player 0x904 to 0, and if it isn’t equal to 0 then it skips the normal check routine and is conditionally handled (by the procedure branched to in yellow.)
This appears to involve flags used by the same table the game uses to look up player bones when assigning hitboxes... I don’t really know what that’s all about, but it seems like an interesting thing to investigate later.
---
The animation speed cancel introduced by Achilles presumably does an effectively similar thing; as the resulting return from this function is usually the same. It artificially increases the frame multiplier; which logically passes a similar result by sort of “flooding the gate.”
So, despite their slightly different approaches-- In each case, the animation interrupt runs its designated action state call immediately rather than on time.
The animation speed cancel however is particularly handy, because it has an effect on many other functions, and effects many frame-dependent behaviors. While this is slightly impeding in the scope of what it fully includes, it is also extremely useful for preserving action state data between action states. In this regard, I’ve done what I can to really take advantage of its ability to artificially progress through a move in my “soft” cancel.
---
I’ve adapted each of these former concepts into individual standalone functions that may be used to cancel an action state very simply in different (default) ways:
These are simplified versions of the core mechanics used in the “soft” cancel code. They may be used from many contexts without issue; though are each problematic under certain circumstances.
<ICNCv020_AnimationSpeedCancel>
#r3= external player data offset
#--sets animation speed to x1000
#--updates player frame stuff, which uses animation speed to create frame unit size
#--updates player article stuff (mask out bit 0x80 of player.0x2270 if you want to avoid projectiles)
#--attempts player animation interrupt check (cancels action state)
#--sets animation speed back to old speed if still == x1000 (cases where cancel fails)
7C0802A6 90010004
9421FFE8 BFC10010
83E3002C C03F089C
D021FFF8 C022E864
807F0000
bl 0x8006f190
807F0000
bl 0x8006eba4
807F0000
bl 0x8006C80C
807F0000
bl 0x8006A360
C002E864 C03F089C
FC010000 40820010
C021FFF8 807F0000
bl 0x8006f190
BBC10010 38210018
80010004 7C0803A6
4E800020
<ICNCv020_abortActionState>
#r3= external player data offset
#--if player is in air, set animation interrupt to transition into falling
#--else, set animation interrupt to transition into standing
#--set 0x904 to a non-0 value (bypasses frame logic check for next call)
#--attempt player animation interrupt check (cancels action state)
7C0802A6 90010004
9421FFF0 93E10008
83E3002C 887F2073
2C030000 4182003C
807F00E0 2C030000
41820014 3C60800C
6063B2F8 907F21A0
48000010 3C608009
60639E80 907F21A0
38600001 907F08A4
807F0000
bl 0x8006A360
83E10008 38210010
80010004 7C0803A6
4E800020
The full cancel that I’ve called “soft cancel” utilizes both of these simple cancels in tandem, but they can be used on their own in most circumstances without any fatal issues.
The animation speed cancel is set up to preserve the next action state when canceling, but can also be used like the second cancel to less candidly “abort” an action state transition by preemptively setting the player’s animation interrupt pointer to a proper wait state (for standing, falling, special falling, etc.)
The “abort action state” cancel almost completely nullifies the rest of the action state by forcing an animation interrupt with only a call to PlayerThinkAgain to soften up the transition into a wait state.
---
By making a call to the function that this animation interrupt gets linked via BLRL (PlayerThinkAgain) we can update the player so that the transition is immediate within our code.
From the old animation speed cancel code--after making a call to the frame speed changing function--we can update the player’s frame data by calling this function (8006eba4) If we do this, we’ll get all subaction events to resolve like they normally would--but within the context of our call.
This is because the function is just an array of calls to these 4 other functions:
- (8006e9b4) - ? -- I recall this being important to 0x904 and 0x908. I’ll try to take better notes about it later.
- (80073240) - player subaction event parsing loop -- reads an animation frame’s worth of data
- (800707b0) - ??
- (800db500) - ??
To only surmise based on what little I know; this call seems to be important for updating data in a player’s current frame, and is called at least once every frame for ever character.
This call we’re making doesn’t account for any flags that may only have been initialized for other update functions to read through its execution--as many subaction events (and other things?) seem to create. This isn’t too much of a problem, but weird cancels account for a great number of the game’s normal vanilla glitches (like ice climbers’ icicles not despawning, link’s hookshot superjump, mewtwo’s “soulbreaker” grab, etc.)
Many of these will pop up while using “hard” (simple) cancels.
“Soulbreaker” glitches are easily handled by setting the grabbed player’s breakout timer to 0.
In addition, this function (8006c80c) may be called with the player frame update in order to handle what appears to be character-related article spawns; including a BLRL that links to the pointer in 0x221C, and seems to handle the projectile flag set by player event 60. The only exception I’ve witnessed is fox’s throw-laser projectiles; which might be handled by a special function elsewhere.
Making the above calls together appears to fix a number of glitches, and makes projectiles spawn before a cancel. Interesting to note that you can set this 0x221C pointer and then manually set the flag (bit 0x80 of player.0x2270) in order to spawn some character projectiles that are appropriate to your current character when this update function gets called.
Some known article functions used by the blrl link in this character article update function:
Code:Character Projectile/Article spawning functions (Melee v1.02) Update function 8006c80c -- (uses blrl to link to the following) internal player data offset 0x22BC -- contains pointer used by blrl Projectile "spawn flag" is a flag used by player subaction event 60 to denote spawn timing for projectiles internal player data offset 0x2210 (bit 0x80) -- projectile spawn flag Function Uses spawn flag? Description Extra Notes 80095efc 0 (all thrown items) universal throw, excludes aerial z-drops 800ce1d4 1 Use Ray Gun 800d80f4 1 Use Super Scope (small bullets) 800d86b8 1 Use Super Scope (charged bullet, any amount) 800cd914 1 Use Star Rod 800cdb9c 1 Use Lip Stick 800ce650 1 Use Fire Flower appears to have special interrupt logic 800e0ee0 1 Mario/Doc fireball/pill 800e1248 0 Mario/Doc cape 8014295c 1 Luigi fireball 8011d018 1 Peach turnip 8011d424 0 Peach parasol (startup) 8011e174 0 Peach Toad 8011e230 0 Peach Toad spore cloud 80134590 0 Bowser down-B fall 80134518 0 Bowser down-B land 8012e110 1 Yoshi egg 8012e644 0 Yoshi down-B land 800bbcc0 0 Become Yoshi Egg (after grab) 8010db3c 0 DK down-B 8010e0cc 0 DK side-B 800e5f28 0 Fox/Falco Laser N/A? 1 Fox/Falco Laser throw These throws use projectile flag, but with some other function; not our common blrl 800e845c 0 Fox/Falco shine start 800e83e0 0 Fox/Falco shine hold 800e9df8 0 Fox/Falco side-B 800e719c 0 Fox/Falco up-B start 800e7100 0 Fox/Falco up-B launch 80116b70 1 Ness PK fire 80114eb8 0 Ness yoyo used in both up and down smash 8011f500 0 IC icicle 80122d2c 0 IC frost 80123218 0 Nana up-B rope only used by nana (subplayer) 800f21e8 0 Kirby up-B projectile only on landing 800f53ac 0 Kiry Hammer 800f9260 1 Kirby-Mario/Doc 800f98f4 1 Kirby-Luigi 8010c288 0 Kirby-Peach (toad) 8010c344 0 Kirby-Peach (toad spore) 800fe0e0 0 Kirby-Fox/Falco 801090d4 0 Kirby-Popo/Nana 80105a34 0 Kirby-Zelda 80106db0 0 Kirby-Sheik 8010cfb0 0 Kirby-GAW start notable that GAW has no start/finish like this 8010ce5c 0 Kirby-GAW finish 8010b1f4 0 Kirby-Marth/Roy 800be6ac 0 Escape from Kirby star (after grab) projectile 8012a074 1 Samus rocket 8012adf0 1 Samus bomb 8013a830 0 Zelda B 801396ac 0 Zelda teleport start 8013979c 0 Zelda teleport finish 8013adb4 0 Zelda -> Sheik start 8013aeac 0 Zelda -> Sheik finish 80112d44 0 Sheik needle(s) throw 8011097c 0 Sheik chain 80112ed8 0 Sheik teleport start 80113038 0 Sheik teleport finish 80114034 0 Sheik -> Zelda start 8011412c 0 Sheik -> Zelda finish 800ec210 1 (y)Link boomerang 800eb7c8 1 (y)Link bomb 800eba4c 0 (y)Link up-B 801277ac 1 Pi(ka)chu thunder flag is near start of animation 80124c90 0 Pi(ka)chu start up charge 80124d2c 0 Pi(ka)chu launch from charge 8013c94c 0 JP sing 8013ce7c 0 JP rest 80146c08 0 Mewtwo side-B 80146594 0 Mewtwo down-B 801450a0 0 Mewtwo teleport start 80145164 0 Mewtwo teleport finish 80146594 0 GAW jab many of GAW normals use models (articles?) that trigger these 8014ab48 0 GAW down-A 8014a848 0 GAW f-smash 8014b3a8 0 GAW u-air 8014b45c 0 GAW u-air landing 8014b1b4 0 GAW b-air 8014b268 0 GAW b-air landing other normals do not appear to use this blrl despite spawning models... 8014e4f0 0 GAW B 8014c46c 0 GAW judgement 8014def0 0 GAW up-B 8014cbf4 0 GAW oil spill only when used on full bucket 801365a8 0 Marth/Roy B on attack Noteworthy projectile moves that do not seem to appear on this list: Bowser flame Ness PK thunder Ness PK flash Samus (un)charge shot Zelda fire/explode (y)Link arrow Pi(ka)chu B Mewtwo shadowball
---
If an animation interrupt can’t be achieved from a state without some kind of special expiration condition (like, letting go of the B button, or often something more subtle than that) then the pointer in 0x2200 may be manually set to an animation interrupt function of your choice before attempting the call to PlayerThinkAgain.
By doing this with an interrupt function that transitions into a wait state like stand, fall, or special fall; you can usually safely use either cancel method to manually assign these action states and “skip” the remaining actionstate data.
I’ve opted to call PlayerThinkAgain for the interrupt to be handled in my code by a more natural context than calling the interrupt function directly; just to be safe.
Last edited: