In Progress Custom Subaction Event - Action State Logic

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
384
#1
Made a note in the Crazy Hand thread that I'd post a WIP of this when I had something interesting.
@Achilles1515 @Ampers @Tater @tatatat0 Tyadran Tyadran Guesmu Guesmu @Itaru @MagicScrumpy

This is an extension of the existing subaction event 5C (IASA flag)
The original syntax is barren; just an opcode that tells the system to set a flag.

I’ve added 4 new features to this event in a way that preserves its original functionality.
These shouldn't change much, but more may be added to it later:


5C xx FLAG - IASA flag toggle
FLAG - IASA flag -- 0 flags the IASA bit on (default 5C behavior) -- not-0 flags the IASA bit off​

5D xx ASID - IASA function override
ASID - 11-bit ASID for defining a specific IASA function
-- makes IASA logic match another action state by ID​

5E xx ASID - Animation interrupt function override
ASID - 11-bit ASID for defining a specific animation interrupt
-- set the logic to match another action state by ID​

5F EE ASID - Force immediate action state transition
EE - number of events to continue reading from the old action state. (ignores timer events, but counts them)
-- these events are read after the transition, but before the new subaction event data is read; all in 1 frame.​
ASID - 11-bit ASID for action state transition. New action state starts on frame 0.
bit 0x8000 of ASID - a flag that tells the new action state to only use subaction events from the old action state
-- using this flag will cause EE to be ignored.​

---

You may use this new syntax (in Crazy Hand!) to set interrupt logic for moves with a single line of data, and you can use it to transition into action states in a way that allows you to pair the new state with unique subaction event data that it doesn't normally go with.

If used in a way that extends the space available for subaction events, it's very easy to chain several action state changes over the course of a single (large) subaction read.

I'll post some examples soon.

Code:
    -==-


Custom Subaction Event -- Actionstate Logic
Adds the ability to change IASA and animation interrupt logic from the default 5C syntax.
Also allows for forcing new action state transitions.

5C - IASA flag toggle
5D - IASA function override
5E - Animation interrupt function override
5F - Force new action state
[Punkline]
Version -- DOL Offset ------ Hex to Replace ---------- ASM Code
1.02 ----- 0x80071960 --- 98032218 -> Branch

80A40008 80A50000
54A501BF 41A20024
7C0802A6 90010004
9421FFF8
bl <EX5C_parseExtension>
38210008 80010004
7C0803A6 48000008
98032218 00000000

<EX5C_parseExtension>
7C0802A6 90010004
9421FF80 BC610008
7C7F1B78 80840008
54BB47BF 54BC863E
54BD053E 41820108
83DF0018 7C1DF040
41800010 7C1EE850
83DF0020 4800000C
7FA0EB78 83DF001C
54002834 7FDE0214
281B0001 418200CC
281B0002 418200B8
7C9E2378 3BDE0004
54BB8FFE 7FA4EB78
807F0000 3D808008
618CCFAC 7D8803A6
4E800021 281B0000
4182000C 93DF03EC
480000A8 83BF03EC
93DF03EC 281C0000
41820060 807F03EC
28030000 41820054
48000045 7C0802A6
90010004 9421FFC8
DBE10030 DBC10028
BF610014 C3C288D4
D3DF03E4 3BBF03E4
837F0000 3FE0803C
63FF06E8 C3C288D8
3D808007 618C32EC
7D8803A6 4E800021
3B9CFFFF 4BFFFFA0
3BBDFFFC C00288D8
D01F03E4 93BF03EC
48000028 837E000C
937F21A0 4800001C
837E0010 937F219C
48000010 8B7F2218
737B007F 9B7F2218
B8610008 38210080
80010004 7C0803A6
4E800020
Code:
$Custom Subaction Event -- Action State Logic 1.0 [Punkline]
C2071960 00000031
80A40008 80A50000
54A501BF 41A20024
7C0802A6 90010004
9421FFF8 4800001D
38210008 80010004
7C0803A6 48000158
98032218 48000150
7C0802A6 90010004
9421FF80 BC610008
7C7F1B78 80840008
54BB47BF 54BC863E
54BD053E 41820108
83DF0018 7C1DF040
41800010 7C1EE850
83DF0020 4800000C
7FA0EB78 83DF001C
54002834 7FDE0214
281B0001 418200CC
281B0002 418200B8
7C9E2378 3BDE0004
54BB8FFE 7FA4EB78
807F0000 3D808008
618CCFAC 7D8803A6
4E800021 281B0000
4182000C 93DF03EC
480000A8 83BF03EC
93DF03EC 281C0000
41820060 807F03EC
28030000 41820054
48000045 7C0802A6
90010004 9421FFC8
DBE10030 DBC10028
BF610014 C3C288D4
D3DF03E4 3BBF03E4
837F0000 3FE0803C
63FF06E8 C3C288D8
3D808007 618C32EC
7D8803A6 4E800021
3B9CFFFF 4BFFFFA0
3BBDFFFC C00288D8
D01F03E4 93BF03EC
48000028 837E000C
937F21A0 4800001C
837E0010 937F219C
48000010 8B7F2218
737B007F 9B7F2218
B8610008 38210080
80010004 7C0803A6
4E800020 00000000
Code:
#Custom Subaction Event -- Action State Logic 0.1 [Punkline]
#ASM is for MCM, use labels to assemble in ASM<>Wiird
#@80071960: stb r0, 0x2218(r3)          (storing modified IASA flag for normal attacks)
#r5-12, cr0, are safe; LR is not safe
#r3 == internal player data pointer
#r4 == player event data
#--this injection simply checks if the original syntax is used (blank) and if not, runs the code
lwz r5, 0x8(r4)
lwz r5, 0(r5)
rlwinm. r5, r5, 0, 6, 31                #mask out opcode, check if remaining argument is == 0
beq+ default                            #if it is (5C000000) then just use the default instruction

#else, branch link into standalone function that adds new parse features to vanilla event 5C
mflr r0
stw r0, 0x4(sp)
stwu sp, -0x8(sp)
bl <EX5C_parseExtension>        #standalone function, can work with labels in gecko code
addi sp, sp, 0x8
lwz r0, 0x4(sp)
mtlr r0
b return

default:
stb r0, 0x2218(r3)
return: #from injection code
.long 0x00000000



/*EX5C_parseExtension
r3 == internal player data pointer
r4 == player event table        -- only used to continue reading event data after action state transition
r5 == incoming event data       -- this is what's actually interpreted.
--You can pass r5 from other codes outside of the subaction event system. the opcode is ignored.

This adds the following extra capabilities to the 5C event:
-- can toggle IASA flag on or off
-- can override IASA function pointer with new logic
-- can override animation interrupt function pointer with new logic
-- can force an action state transition in a way that continues reading subaction events from the old action state (before the next frame)

Functionality is determined by a 2-bit ID checked in the passed r5 value. See other comments for more details behind the syntax*/

mflr r0
stw r0, 0x4(sp)
stwu sp, -0x80(sp)
stmw r3, 0x8(sp)                        #keep the context safe while we potentially go crazy with registers
mr r31, r3
lwz r4, 0x8(r4)

rlwinm. r27, r5, 8, 30, 31       #r27 = extracted 2-bit function id
rlwinm r28, r5, 16, 24, 31       #r28 = extracted 8-bit AA (determined by function)
rlwinm r29, r5, 0, 20, 31        #r29 = extracted 11-bit BBBB (determined by function) (5 leftmost bits are ignored in hword, can be used as flags)

beq- option5C                           #if id is 0 (5C) then input was a non-0 argument for the default 5C syntax.

#else, we're going to find the move logic from the input ASID in r29
lwz r30, 0x18(r31)                        #ASID division between common/character dependent moves
cmplw r29, r30                            #is input ID < threshold? (usually 0x155)
blt- commonASID
uncommonASID:
sub r0, r29, r30                          #uncommon ASIDs use their own 0-based character move index
lwz r30, 0x20(r31)                        #if ASID >= (0x155) then load uncommon move table
b loadMoveData
commonASID:
mr r0, r29
lwz r30, 0x1C(r31)                        #if ASID < (0x155) then load common move table

loadMoveData:
rlwinm r0, r0, 5, 0, 26                 #shift left 5 (1 = 32)
add r30, r30, r0                  #r30 = pointer to move data (from ASID)

cmplwi r27, 1
beq option5D                            #if id is 1 (5D) then the syntax is used to override IASA logic.
cmplwi r27, 2
beq option5E                            #if id is 2 (5E) then the syntax is used to override animation interrupt logic.

#else, id is 3 (max 2-bit range)         if id is 3 (5F) then use syntax to force an immediate action state transition.
option5F:       /*force action state transition
5F AA BBBB
AA   - 8-bit number of events to continue reading without timer events post-transition
BBBB - 11-bit ASID ID
flag - bit 0x8000 of BBBB is a flag that tells the new action state to use the old subaction event data. Ignores AA

-- this option is complicated by the fact that changing an action state cuts off the remaining subaction event data from the state it came from.
-- with the AA argument, you can specify a number of subaction events to read before returning to handle the new action state's subaction event data.
-- see the SSBM Data sheet for more info on actionstate IDs*/

mr r30, r4                              #use the passed event pointer in r4
addi r30, r30, 0x4              #r30 = next event from old subaction event data (passed in r4)
rlwinm r27, r5, 17, 31, 31      #r27 = subaction event override flag. If true, the old subaction events will continue in new action state

#first, pass the id over to the aerial attack transition function to force an action state
mr r4, r29                              #pass the ID
lwz r3, 0(r31)                          #pass external player data pointer
lis r12, 0x8008
ori r12, r12, 0xcfac                    #pointer to function
mtlr r12
blrl                                    #change action state

#now that the action state has transitioned, subaction event data from the previous state is gone
#so we take advantage of the fact that we still have the pointer in a saved register and continue reading event data.
#the number of events that we read is determined by the parsed AA value, stored in r28. A value of 00 reads no extra events

cmplwi r27, 0
beq- storeNewData
storeOldData:
stw r30, 0x3EC(r31)                     #if subaction event override flag was detected, just continue the old event data
b return
storeNewData:                           #otherwise, we're going to attempt to sandwich as much data specified by AA between states
lwz r29, 0x3EC(r31)             #r29 = new data start
stw r30, 0x3EC(r31)                     #set event pointer to continue reading our old data


eventReadLoop:
cmplwi r28, 0                           #if AA value is 0, then exit loop
beq- exitLoop
lwz r3, 0x3EC(r31)
cmplwi r3, 0                            #if event pointer contains no data, exit loop
beq- exitLoop

#else, read a single event and decrement AA by -1
bl returnFromEventRead                  #set up the lr to continue our event reading loop
mflr r0                                 #reconstruct the prolog of the SAEvent parsing loop function
stw r0, 0x4(sp)
stwu sp, -0x38(sp)
stfd f31, 0x30(sp)
stfd f30, 0x28(sp)
stmw r27, 0x14(sp)

lfs f30, -0x772C(rtoc)
stfs f30, 0x3E4(r31)                    #set event timer to 1.0

#reconstruct context for our intrusive branch
addi r29, r31, 0x3E4            #r29 = pointer to player data offset 0x3E4
lwz r27, 0(r31)                 #r27 = external player data pointer
lis r31, 0x803c
ori r31, r31, 0x06e8            #r31 = 803C06E8 (lookup table for player events)
lfs f30, -0x7728(rtoc)          #f30 = 0

#branch into player event parsing loop
lis r12, 0x8007
ori r12, r12, 0x32ec                    #this is a very specific part of the subaction event parsing loop
mtlr r12                                #branching here with non-0 float in internal player data offset 0x3E4 will read a single event
returnFromEventRead:
blrl    #a super tricky blrl
#a return from here has collapsed the above stack frame
decrementAA:
subi r28, r28, 1                        #else, decrement AA by -1 and read a single event
b eventReadLoop

exitLoop:
subi r29, r29, 4                        #the hook context will try and move this on return, so correct for it
lfs f0, -0x7728(rtoc)
stfs f0, 0x3E4(r31)                     #set event timer back to 0
stw r29, 0x3EC(r31)                     #set event pointer back to new data start
b return
#end of option5F

option5E:       /*override animation interrupt logic
5E xx BBBB
BBBB - 11-bit ASID -- looks up animation interrupt associated with this ASID*/
lwz r27, 0xC(r30)       #this is where we make use of that r30 calculation
stw r27, 0x21A0(r31)    #apply target animation interrupt to 0x21A0 playerThinkAnimation blrl
b return

option5D:       /*override IASA logic
5E xx BBBB
BBBB - 11-bit ASID -- looks up IASA associated with this ASID*/
lwz r27, 0x10(r30)      #target IASA pointer
stw r27, 0x219C(r31)    #apply to 0x219C playerThinkController blrl
b return

option5C:       /*toggle off IASA flag
5C xx BBBB
BBBB - 11-bit flag; if not 0 then this extension turns off the IASA flag bit in the middle of an action state
r27 and r28 are freed up
-- the following is executed only if the function ID was 0 (5C) and the argument was not equal to 0
-- so 0 = IASA flag toggled on, !0 = IASA flag toggled off */
lbz r27, 0x2218(r31)
andi. r27, r27, 0x7F                    #bit 0x80 is the IASA flag, so mask it out
stb r27, 0x2218(r31)                    #store it, like the original instruction does

return: #from standalone function
lmw r3, 0x8(sp)
addi sp, sp, 0x80
lwz r0, 0x4(sp)
mtlr r0
blr

Here are a bunch of IDs that may be used:
Code:
Known Common Action State IDs--
These IDs are the same for each player character movset.
Copied from the SSBM Data Sheet:

ID        Name                    Description
0000    DeadDown                Standard downward death
0001    DeadLeft                Standard leftward death
0002    DeadRight                Standard rightward death
0003    DeadUp                    Upward death used in 1P "Team Kirby", etc.
0004    DeadUpStar                Standard Star KO
0005    DeadUpStarIce            Star KO while encased in ice (Freezie)
0006    DeadUpFall                64-esque front fall, unused, I believe
0007    DeadUpFallHitCamera
0008    DeadUpFallHitCameraFlat
0009    DeadUpFallIce
000A    DeadUpFallHitCameraIce
000B    Sleep                    "Nothing" state, probably - it is the state Shiek/Zelda is in when their counterpart is the one currently playing
000C    Rebirth                    Entering on halo
000D    RebirthWait                Waiting on halo
000E    Wait                    Standing state
000F    WalkSlow
0010    WalkMiddle
0011    WalkFast
0012    Turn
0013    TurnRun
0014    Dash
0015    Run
0016    RunDirect
0017    RunBrake
0018    KneeBend                Pre-jump animation
0019    JumpF                    First jump forward
001A    JumpB                    First jump backward
001B    JumpAerialF                Aerial jump forward
001C    JumpAerialB                Aerial jump backward
001D    Fall                    Falling straight down
001E    FallF                    Falling with forward DI
001F    FallB                    Falling with backward DI
0020    FallAerial                Falling after the second jump
0021    FallAerialF                Falling after the second jump with forward DI
0022    FallAerialB                Falling after the second jump with backward DI
0023    FallSpecial                Special fall after UpB or airdodge
0024    FallSpecialF            Special fall with forward DI
0025    FallSpecialB            Special fall with backward DI
0026    DamageFall                Tumbling
0027    Squat                    Going from stand to crouch
0028    SquatWait                Crouching
0029    SquatRv                    Going from crouch to stand
002A    Landing                    Landing state, can be cancelled
002B    LandingFallSpecial        Landing from special fall
002C    Attack11                Standard attack 1
002D    Attack12                Standard attack 2
002E    Attack13                Standard attack 3
002F    Attack100Start            Start of a looping standard attack
0030    Attack100Loop            Middle of a looping standard attack
0031    Attack100End            End of a looping standard attack
0032    AttackDash                Dash attack
0033    AttackS3Hi                High Ftilt
0034    AttackS3HiS                High-mid Ftilt
0035    AttackS3S                Mid Ftilt
0036    AttackS3LwS                Low-mid Ftilt
0037    AttackS3Lw                Low Ftilt
0038    AttackHi3                Uptilt
0039    AttackLw3                Downtilt
003A    AttackS4Hi                High Fsmash
003B    AttackS4HiS                High-mid Fsmash
003C    AttackS4S                Mid Fsmash
003D    AttackS4LwS                Low-mid Fsmash
003E    AttackS4Lw                Low Fsmash
003F    AttackHi4                Upsmash
0040    AttackLw4                Downsmash
0041    AttackAirN                Nair
0042    AttackAirF                Fair
0043    AttackAirB                Bair
0044    AttackAirHi                Uair
0045    AttackAirLw                Dair
0046    LandingAirN                Landing during Nair
0047    LandingAirF                Landing during Fair
0048    LandingAirB                Landing during Bair
0049    LandingAirHi            Landing during Uair
004A    LandingAirLw            Landing during Dair
004B    DamageHi1
004C    DamageHi2
004D    DamageHi3
004E    DamageN1
004F    DamageN2
0050    DamageN3
0051    DamageLw1
0052    DamageLw2
0053    DamageLw3
0054    DamageAir1
0055    DamageAir2
0056    DamageAir3
0057    DamageFlyHi
0058    DamageFlyN
0059    DamageFlyLw
005A    DamageFlyTop
005B    DamageFlyRoll
005C    LightGet                Picking up an item
005D    HeavyGet                Picking up a heavy item (barrel)
005E    LightThrowF                Throwing items at standard speed
005F    LightThrowB
0060    LightThrowHi
0061    LightThrowLw
0062    LightThrowDash
0063    LightThrowDrop
0064    LightThrowAirF
0065    LightThrowAirB
0066    LightThrowAirHi
0067    LightThrowAirLw
0068    HeavyThrowF
0069    HeavyThrowB
006A    HeavyThrowHi
006B    HeavyThrowLw
006C    LightThrowF4            Throwing items at Smash speed
006D    LightThrowB4
006E    LightThrowHi4
006F    LightThrowLw4
0070    LightThrowAirF4
0071    LightThrowAirB4
0072    LightThrowAirHi4
0073    LightThrowAirLw4
0074    HeavyThrowF4
0075    HeavyThrowB4
0076    HeavyThrowHi4
0077    HeavyThrowLw4
0078    SwordSwing1                Beam sword swings
0079    SwordSwing3
007A    SwordSwing4
007B    SwordSwingDash
007C    BatSwing1                Home Run Bat swings
007D    BatSwing3
007E    BatSwing4
007F    BatSwingDash
0080    ParasolSwing1            Parasol swings
0081    ParasolSwing3
0082    ParasolSwing4
0083    ParasolSwingDash
0084    HarisenSwing1            Fan swings
0085    HarisenSwing3
0086    HarisenSwing4
0087    HarisenSwingDash
0088    StarRodSwing1            Star Rod swings
0089    StarRodSwing3
008A    StarRodSwing4
008B    StarRodSwingDash
008C    LipStickSwing1            Lip's Stick swings
008D    LipStickSwing3
008E    LipStickSwing4
008F    LipStickSwingDash
0090    ItemParasolOpen
0091    ItemParasolFall
0092    ItemParasolFallSpecial
0093    ItemParasolDamageFall
0094    LGunShoot                Raygun shots
0095    LGunShootAir
0096    LGunShootEmpty
0097    LGunShootAirEmpty
0098    FireFlowerShoot
0099    FireFlowerShootAir
009A    ItemScrew
009B    ItemScrewAir
009C    DamageScrew
009D    DamageScrewAir
009E    ItemScopeStart
009F    ItemScopeRapid
00A0    ItemScopeFire
00A1    ItemScopeEnd
00A2    ItemScopeAirStart
00A3    ItemScopeAirRapid
00A4    ItemScopeAirFire
00A5    ItemScopeAirEnd
00A6    ItemScopeStartEmpty
00A7    ItemScopeRapidEmpty
00A8    ItemScopeFireEmpty
00A9    ItemScopeEndEmpty
00AA    ItemScopeAirStartEmpty
00AB    ItemScopeAirRapidEmpty
00AC    ItemScopeAirFireEmpty
00AD    ItemScopeAirEndEmpty
00AE    LiftWait
00AF    LiftWalk1
00B0    LiftWalk2
00B1    LiftTurn
00B2    GuardOn
00B3    Guard                    Holding shield
00B4    GuardOff
00B5    GuardSetOff                Shield stun
00B6    GuardReflect
00B7    DownBoundU                The "failed to tech" bounce, facing up
00B8    DownWaitU                Laying on ground facing up
00B9    DownDamageU                Getting hit laying on ground facing up
00BA    DownStandU
00BB    DownAttackU                Get up attack from ground face up
00BC    DownFowardU
00BD    DownBackU
00BE    DownSpotU
00BF    DownBoundD                The "failed to tech" bounce, facing down
00C0    DownWaitD
00C1    DownDamageD                Lying on the ground
00C2    DownStandD                Neutral getup
00C3    DownAttackD
00C4    DownFowardD
00C5    DownBackD
00C6    DownSpotD
00C7    Passive                    Neutral tech
00C8    PassiveStandF            Forward tech
00C9    PassiveStandB            Backward tech
00CA    PassiveWall                Wall tech
00CB    PassiveWallJump            Walljump tech/plain walljump
00CC    PassiveCeil                Ceiling tech
00CD    ShieldBreakFly
00CE    ShieldBreakFall
00CF    ShieldBreakDownU
00D0    ShieldBreakDownD
00D1    ShieldBreakStandU
00D2    ShieldBreakStandD
00D3    FuraFura                Shield-break tottering
00D4    Catch                    Grab
00D5    CatchPull                Successfully grabbing a character - pulling them in
00D6    CatchDash
00D7    CatchDashPull
00D8    CatchWait                Grabbing and holding a character
00D9    CatchAttack                Pummel
00DA    CatchCut                When opponent breaks of a character's grab
00DB    ThrowF
00DC    ThrowB
00DD    ThrowHi
00DE    ThrowLw
00DF    CapturePulledHi
00E0    CaptureWaitHi
00E1    CaptureDamageHi
00E2    CapturePulledLw            Becoming grabbed
00E3    CaptureWaitLw            When grabbed
00E4    CaptureDamageLw            Pummeled
00E5    CaptureCut                Grab release
00E6    CaptureJump
00E7    CaptureNeck
00E8    CaptureFoot
00E9    EscapeF
00EA    EscapeB
00EB    Escape
00EC    EscapeAir                airdodge
00ED    ReboundStop
00EE    Rebound
00EF    ThrownF
00F0    ThrownB
00F1    ThrownHi
00F2    ThrownLw
00F3    ThrownLwWomen
00F4    Pass                    Drop through platform
00F5    Ottotto                    Ledge teeter
00F6    OttottoWait
00F7    FlyReflectWall
00F8    FlyReflectCeil
00F9    StopWall
00FA    StopCeil
00FB    MissFoot
00FC    CliffCatch                Catching the ledge
00FD    CliffWait                Hanging on the ledge
00FE    CliffClimbSlow            Climbing the ledge, >100%
00FF    CliffClimbQuick            Climbing the ledge, <100%
0100    CliffAttackSlow            Ledge attack, >100%
0101    CliffAttackQuick        Ledge attack, <100%
0102    CliffEscapeSlow            Ledge roll, >100%
0103    CliffEscapeQuick        Ledge roll, <100%
0104    CliffJumpSlow1
0105    CliffJumpSlow2
0106    CliffJumpQuick1
0107    CliffJumpQuick2
0108    AppealR                    Taunt right
0109    AppealL                    Taunt left
010A    ShoulderedWait
010B    ShoulderedWalkSlow
010C    ShoulderedWalkMiddle
010D    ShoulderedWalkFast
010E    ShoulderedTurn
010F    ThrownFF
0110    ThrownFB
0111    ThrownFHi
0112    ThrownFLw
0113    CaptureCaptain
0114    CaptureYoshi
0115    YoshiEgg
0116    CaptureKoopa
0117    CaptureDamageKoopa
0118    CaptureWaitKoopa
0119    ThrownKoopaF
011A    ThrownKoopaB
011B    CaptureKoopaAir
011C    CaptureDamageKoopaAir
011D    CaptureWaitKoopaAir
011E    ThrownKoopaAirF
011F    ThrownKoopaAirB
0120    CaptureKirby
0121    CaptureWaitKirby
0122    ThrownKirbyStar
0123    ThrownCopyStar
0124    ThrownKirby
0125    BarrelWait
0126    Bury
0127    BuryWait
0128    BuryJump
0129    DamageSong
012A    DamageSongWait
012B    DamageSongRv
012C    DamageBind
012D    CaptureMewtwo
012E    CaptureMewtwoAir
012F    ThrownMewtwo
0130    ThrownMewtwoAir
0131    WarpStarJump
0132    WarpStarFall
0133    HammerWait
0134    HammerWalk
0135    HammerTurn
0136    HammerKneeBend
0137    HammerFall
0138    HammerJump
0139    HammerLanding
013A    KinokoGiantStart        Super/Poison mushroom states
013B    KinokoGiantStartAir
013C    KinokoGiantEnd
013D    KinokoGiantEndAir
013E    KinokoSmallStart
013F    KinokoSmallStartAir
0140    KinokoSmallEnd
0141    KinokoSmallEndAir
0142    Entry                    Warp in at beginning of match.
0143    EntryStart
0144    EntryEnd
0145    DamageIce
0146    DamageIceJump
0147    CaptureMasterhand
0148    CapturedamageMasterhand
0149    CapturewaitMasterhand
014A    ThrownMasterhand
014B    CaptureKirbyYoshi
014C    KirbyYoshiEgg
014D    CaptureLeadead
014E    CaptureLikelike
014F    DownReflect
0150    CaptureCrazyhand
0151    CapturedamageCrazyhand
0152    CapturewaitCrazyhand
0153    ThrownCrazyhand
0154    BarrelCannonWait
Code:
Known Uncommon Action State IDs--
These IDs are unique for each player character movset:

-Mario/Dr Mario
Ground ASID        Air ASID    Description
157                158            B - all
159                15a            sideB - all
15b                15c            upB - all
15d                15e            downB - all

-Luigi
Ground ASID        Air ASID    Description
155                156            B - all
157                15d            sideB - startup
158                15e            sideB - charge
---                15f            sideB - movement
15a                ---            sideB - land
---                160            sideB - hit
15b                161            sideB - launch
15c                162            sideB - misfire
163                164            upB - all
165                166            downB - all

-Bowser
Ground ASID        Air ASID    Description
155                158            B - startup
156                159            B - fire
157                15a            B - recovery
15b                161            sideB - "klaw"
15c                162            sideB - capture startup
15d                163            sideB - capture bite
15e                164            sideB - capture wait
15f                165            sideB - throw (forward)
160                166            sideB - throw (back)
167                168            upB - all
169                ---            downB - ground start
---                16a            downB - air drop
16b                ---            downB - landing

-Peach
Ground ASID        Air ASID    Description
16d                16f            B - counter
16e                170            B - riposte
162                165            sideB - startup
163                166            sideB - recovery
---                168            sideB - attack
---                167            sideB - hit
169                16b            upB - jump
---                171            upB - open parasol
---                172            upB - float
160                ---            downB - pluck

-Yoshi
Ground ASID        Air ASID    Description
15a                15f            B - grab
15b                160            B - catch
15d                162            B - egg
164                168            sideB - startup
---                169            sideB - roll air idle?
165                16a            sideB - roll
166                ---            sideB - change direction
167                16b            sideB - recovery
16c                16d            upB - all
16e                ---            downB - ground startup
---                170            downB - air drop
16f                ---            downB - landing

-Donkey Kong
Ground ASID        Air ASID    Description
171                176            B - startup
172                177            B - charge
173                178            B - cancel
174                179            B - attack
175                17a            B - attack full
17b                17c            sideB - all
17d                17e            upB - all
17f                ---            downB - startup
180                ---            downB - ground slap
181                ---            downB - recovery

-CFalcon/GDorf
Ground ASID        Air ASID    Description
15b                15c            B - all
15d                15f            sideB - dash
15e                160            sideB - attack
161                162            upB - startup
---                163            upB - catch
---                164            upB - dive
165                167            downB - kick
166                16a            downB - ground kick recovery
168                169            downB - air kick recovery
16b                ---            downB - ground kick wall

-Fox/Falco
Ground ASID        Air ASID    Description
155                158            B - startup
156                159            B - shoot
157                15a            B - recovery
15b                15e            sideB - startup
15c                15f            sideB - dash
15d                160            sideB - recovery
161                162            upB - startup
163                164            upB - dash
165                166            upB - recovery
168                16d            downB - startup
169                16e            downB - shield wait
16a                16f            downB - reflect
16b                170            downB - recovery
16c                171            downB - turn

-Ness
Ground ASID        Air ASID    Description
156                ---            USmash - normal smash
157                ---            USmash - charge
158                ---            USmash - charged smash
159                ---            DSmash - normal smash
15a                ---            DSmash - charge
15b                ---            DSmash - charged smash
15c                160            B - startup
15d                161            B - charge
15e                162            B - attack delay
15f                163            B - recovery
164                165            sideB - all
166                16a            upB - startup
167                16b            upB - control
168                16c            upB - recovery
169                16d            upB - dash
16f                174            downB - startup
170                175            downB - shield
171                176            downB - absorb
172                177            downB - recovery

Ice Climbers
Ground ASID        Air ASID    Description
155                156            B - all
157                159            sideB - single
158                15a            sideB - double
15b                160            upB - summon
15c                161            upB - throw
15d                162            upB - jump
15e                163            upB - (single) throw
15f                164            upB - (single) jump
165                166            downB - all

-Kirby
Ground ASID        Air ASID    Description
---                155            jump 2
---                156            jump 3
---                157            jump 4
---                158            jump 5
---                159            jump 6
161                173            B - startup
162                174            B - vacuum cleaner
163                175            B - recovery
164                176            B - capture
166                178            B - fat startup
167                179            B - fat idle
168                ---            B - fat walk startup
169                ---            B - fat walk
16a                ---            B - fat run
16b                ---            B - fat turn around
16c                ---            B - fat jump startup
16d                ---            B - fat jump
16e                ---            B - fat land
16f                ---            B - swallow
171                ---            B - spit
17f                180            sideB - all
181                185            upB - startup
---                186            upB - rise/fall
184                187            upB - landing
189                18c            downB - startup
18a                18d            downB - stone
18b                18e            downB - recovery

-Samus
Ground ASID        Air ASID    Description
155                156            bomb jump
157                15b            B - startup
158                ---            B - charge
159                ---            B - cancel
15a                15c            B - shoot
15d                15f            sideB - Homing
15e                160            sideB - Power
161                162            upB - all
163                164            downB - all

-Zelda
Ground ASID        Air ASID    Description
155                156            B - all
157                15a            sideB - startup
158                15b            sideB - charge
159                15c            sideB - attack
15d                160            upB - startup
15e                161            upB - movement
15f                162            upB - recovery
163                165            downB - transform out
164                166            downB - transform back

-Sheik
Ground ASID        Air ASID    Description
155                159            B - startup
156                15a            B - charge
157                15b            B - cancel
158                15c            B - throw
15d                160            sideB - startup
15e                161            sideB - chain
15f                162            sideB - recovery
163                166            upB - startup
164                167            upB - movement
165                168            upB - recovery
169                16b            downB - transform out
16a                16c            downB - transform back

-Link/Young Link
Ground ASID        Air ASID    Description
158                15b            B - startup charge
159                15c            B - fully charged
15a                15d            B - shoot
15e                161            sideB - throw
15f                162            sideB - catch
160                163            sideB - throw (empty)
164                165            upB - all
166                167            downB - all

-Pikachu/Pichu
Ground ASID        Air ASID    Description
155                156            B - all
157                15c            sideB - startup
158                15d            sideB - charge
15a                15e            sideB - landing/air movement
---                15f            sideB - falling
15b                160            sideB - launch
161                164            upB - startup
162                165            upB - movement
163                166            upB - recovery
167                16b            downB - startup
168                16c            downB - lightning call
169                16d            downB - lightning hit
16a                16e            downB - recovery

-Jigglypuff
Ground ASID        Air ASID    Description
---                155            jump 2
---                156            jump 3
---                157            jump 4
---                158            jump 5
---                159            jump 6
15a                162            B - startup (face right)
15b                163            B - startup (face left)
15c                164            B - charge
15d                165            B - full charge
15e                166            B - roll forward
15f                ---            B - change direction
160                168            B - recovery (face right)
161                169            B - recovery (face left)
---                16a            B - float after attack
16b                16c            sideB - all
16d                16e            upB - all (face left)
16f                170            upB - all (face right)
171                172            downB - all (face left)
173                174            downB - all (face right)

-Mewtwo
Ground ASID        Air ASID    Description
155                15a            B - startup
156                15b            B - charge
157                15c            B - full charging
158                15d            B - recovery
159                15e            B - attack
15f                160            sideB - all
161                164            upB - startup
162                165            upB - movement
163                166            upB - recovery
167                168            downB - all

-Game and Watch
Ground ASID        Air ASID    Description
156                ---            Rapid Jab - start
157                ---            Rapid Jab - loop
158                ---            Rapid Jab - end
161                162            B - all
163                16c            sideB - 1
164                16d            sideB - 2
165                16e            sideB - 3
166                16f            sideB - 4
167                170            sideB - 5
168                171            sideB - 6
169                172            sideB - 7
16a                173            sideB - 8
16b                174            sideB - 9
175                176            upB - all
177                17a            downB - bucket wait
178                17b            downB - bucket catch
179                17c            downB - bucket attack

-Marth/Roy
Ground ASID        Air ASID    Description
155                159            B - startup
156                15a            B - charge (loop)
157                15b            B - attack
158                15c            B - attack (full)
15d                166            sideB - hit 1
15e                167            sideB - hit 2 (up)
15f                168            sideB - hit 2
160                169            sideB - hit 3 (up)
161                16a            sideB - hit 3
162                16b            sideB - hit 3 (down)
163                16c            sideB - hit 4 (up)
164                16d            sideB - hit 4
165                16e            sideB - hit 4 (down)
16f                170            upB - all
171                173            downB - counter
172                174            downB - riposte
 
Last edited:

Guesmu

Smash Apprentice
Joined
Aug 20, 2015
Messages
76
#2
Wow! This must have taken forever to compile! Hmm... you cover pretty much every IASA flag, but what about continuation control?
like if you were trying to make something like Project Melee turbo mode. Wouldn't it be a continuation control code?
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
384
#3
Wow! This must have taken forever to compile! Hmm... you cover pretty much every IASA flag, but what about continuation control?
like if you were trying to make something like Project Melee turbo mode. Wouldn't it be a continuation control code?
I actually don't know much about the continuation control event. Do you?

There is only 1 IASA flag, and it's used only in some of the IASA functions to toggle broader interrupts for the move (IASA frames.) In other moves, IASA frames are no different than non-IASA frames--it's all determined by the IASA function being used by the action state.

Similar to what I mentioned in this post, if you set the IASA logic function of an action state to match that of standing or falling, you can allow for interrupts of any kind from any state.

So to do that with this code, you'd use the new 5D event syntax to override IASA logic with either the standing (ASID: E) or falling (ASID: 1D)

In other words, you can make a "turbo" character (that can cancel B-specials) by putting 5D00000E on a grounded move, or 5D00001D on an aerial move. Because they are events, they can be paired with timer events to create interrupt windows, if desired.

As for a Project Melee Turbo Mode; assuming you're talking about an analogy to Project M and not just referring to "turbo" characters that can cancel anything at any time--the tricky part is preventing stale interrupts (which would have to be handled by code, I think)
 
Last edited:

Guesmu

Smash Apprentice
Joined
Aug 20, 2015
Messages
76
#4
I actually don't know much about the continuation control event. Do you?
I know some codes, not enough to make a awesome thread like this.

As for a Project Melee Turbo Mode; assuming you're talking about an analogy to Project M and not just referring to "turbo" characters that can cancel anything at any time--the tricky part is preventing stale interrupts (which would have to be handled by code, I think)
I have a text file somewhere on this computer full of continuation controls, maybe I can find one that makes it so that when you hit someone it would allow an IASA only on collide.
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
384
#5
I know some codes, not enough to make a awesome thread like this.

I have a text file somewhere on this computer full of continuation controls, maybe I can find one that makes it so that when you hit someone it would allow an IASA only on collide.
That would be very interesting.

If you don't mind sharing, what notes do you have on the continuation control event? (I have literally never touched the thing, so I don't even really know how it behaves.)
 

Tyadran

Smash Cadet
Joined
Jan 21, 2016
Messages
25
Location
Ohio
NNID
Tyadran
#7
This is fascinating, definitely going to keep an eye on it. I like where this is going, especially if we find a way to IASA only on hit.
 

Dodoshian

Smash Rookie
Joined
Jul 7, 2016
Messages
20
Location
Mobile, Alabama
#9
Punkline Punkline Wait... so does this mean I could, for example, make Link's Down-smash jump cancellable? I can easily treat any action state as if it is an action state with values in the move-logic table in Crazy Hand? Surely this isn't so. I see this was posted in mid/late 2016. How close to completion are you?
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
384
#10
Dodoshian Dodoshian This code will only copy the move logic for actions specified by ID -- so unfortunately that would only be be possible for characters that already have a jump-cancel within their moveset (like fox/falco)

To give link’s down-smash (a non-special move) new move logic, you can more directly reference the jump-cancel IASA function (800CAED0) using Achilles’s Character Data Modifications event with the following line:
Code:
F8 FF 00 0C 02 00 21 9C 80 0C AE D0
I tested this by replacing Link’s GFX event:
https://i.imgur.com/EqnfMGF.png


I have some more information about move logic functions here.
 
Last edited:

Dodoshian

Smash Rookie
Joined
Jul 7, 2016
Messages
20
Location
Mobile, Alabama
#11
Dodoshian Dodoshian This code will only copy the move logic for actions specified by ID -- so unfortunately that would only be be possible for characters that already have a jump-cancel within their moveset (like fox/falco)

To give link’s down-smash (a non-special move) new move logic, you can more directly reference the jump-cancel IASA function (800CAED0) using Achilles’s Character Data Modifications event with the following line:
Code:
F8 FF 00 0C 02 00 21 9C 80 0C AE D0
I tested this by replacing Link’s GFX event:
https://i.imgur.com/EqnfMGF.png


I have some more information about move logic functions here.
Wow thank you! Now I finally know what is actually happening in the move logic table and how to change the interrupt pointers for action states that are not in the move logic table section in Crazy Hand.

On another note...I have been trying to do something very specific. I notice that the IASI functions are all functions that tell the game how a move CAN be cancelled through a controller input, but not how it CANNOT (or so I gathered from the link you gave me about move logic functions). Is there a way to make an IASI function that is not already in the game? For example, at one point I gave Ganondorf a 'float' similar to PM. It was his aerial neutral B. Everything worked perfectly except that I wanted his float to be cancellable into everything EXCEPT aerial neutral B. To my knowledge, there is no such IASI function that does this, and the closest one, "ac_Interrupt_AS_Fall," is not what I desire. Could I write a gecko code that would add a new IASI function, or would I have to edit the existing "ac_Interrupt_AS_Fall" function so that it will be what I desire. Doesn't the "ac_Interrupt_AS_Fall" function just work by linking every IASI function that can be done in the air (ac_Interrupt_AerialJump, ac_Interrupt_AS_AttackAirN, etc) into one? So couldn't I just remove or branch past the part about the aerial neutral B being one of them? But I don't see an aerial neutral-B IASI function, so maybe I am wrong. I know that this will potentially have side effects with vanilla actions states that use this function (if any do).
Alternatively, an option would be to only allow the aerial neutral be to be cancellable into any aerial attack, but I notice that there are 5 different IASI functions for the 5 different aerials, and not one for ALL aerials. Would it be possible to 'add' or link the 5 of them together by branching in the assembly code? Man, I am confused.

Also, I am interested in learning more about how I would go about using Animation Interrupt Functions to make one actions state lead directly into another one. The only AIFs I could find are the ones for standing, falling, and fallspecial. Could I use AIFs in some way to, for example, make Ganondorf's aerial side-b interrupt into his aerial down-B? Is there another method used for this, if it is at all possible?

==================

On yet another, unrelated note...How would I make it so that an action state can only be used once before landing on the ground? There are not any action states like this in vanilla (with the exception of Peach's float), but certain ones have a similar mechanic (namely the "stalling" moves such as Marth's and Mario's Side-Bs, or Luigi's Down-B). I am just starting to get into writing assembly code, so would this be too advanced for me to do? I think that UnclePunch did something similar with this.

But, yeah. Thank you TONS for the information and the example code you wrote about the move logic tables. I will definitely begin playing around with this. Sorry if I make little to no sense; I am still relatively new to this.
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
384
#12
I like your creativity~

Sorry for the delayed response, but you had a lot of good questions and I wanted to take the time to elaborate in my responses:

Edit: typos and stuff



“Is there a way to make an IASI function that is not already in the game?”
Absolutely! Studying and manipulating move logic is actually a really great way to learn about how callback functions work in PPC.

Here’s how the game does it:

There’s a routine in the game that runs once per frame/character in order to update the controller button and analog values in a player’s internal data table. At the end of this routine however is a special “blrl” instruction -- which is where IASA functions are executed.

blrl” is a lot like a “bl” instruction. They're both used to call functions with a return link in the link register (lr) . With a bit of extra work though, a blrl instruction can be a lot more powerful than a bl instruction.

A bl instruction is used to make calls to functions in a direct, hardcoded way. The blrl however uses a variable instead of a hardcoded constant, meaning that you can tell it to branch to a function that has been determined by a separate process, or mapped to an index.

https://i.imgur.com/WARnOaY.png


In the case of this IASA blrl, the game is loading in a pre-calculated address from a player’s internal data offset 0x219C. Each action state transition updates this 0x219C offset by copying in an address specified in the file data you can modify with Crazy Hand.

In other words, the action state transition function "writes" an IASA function address to this offset once per player action, and the button update "reads" the function once per frame.

---

This all means that, essentially, an “IASA function” is just a set of “extra instructions” to execute at the end of a button update routine that runs for each frame/character. Changing what those “extra instructions” are is simply a matter of intercepting the pointer address at player data offset 0x219C and pointing it to your custom code.

This is exactly how Link is made able to jump-cancel his down-smash with Achilles’s event syntax:

F8 FF 00 0C 02 00 21 9C 80 0C AE D0
F8 FF = internal player data memory write
00 0C = event data string is 12 bytes long
02 00 = overwrite word
21 9C = write to player data offset 0x219C
800CAED0 = address of jump cancel interrupt instructions

By writing “800CAED0” to this player data offset, the natural button update is forced to run fox’s jump-cancel IASA even though it isn’t specified as Link’s default move logic behavior. This is because we’ve managed to intercept the pointer, which only resets to file data defaults on new actions.

(With this in mind, you can also use 2 of these events to sandwich a timer event in order to create keyframes that define a window of IASA logic. Likewise, you may simply use the end of an action as your second keyframe. )

---

The overriding function doesn’t necessarily have to be an “IASA function” -- though it will take the place of one. So long as it has a compatible argument interface (r3 = player entity) and is otherwise formatted correctly (stack frame, register usage, etc) it should execute any instructions you give it without issues.

For example, this other game function (8007E2FC) appears to be used to nullify all velocity in a player entity when they are catching a cliff edge. It only requires the argument “r3 = player entity” and thus has a compatible interface with the IASA blrl context.

https://i.imgur.com/6GERVM3.png


Placing it in Mewtwo’s shadow ball charge IASA function slot (using crazy hand) will run the foreign function in place of button checks, and cause him to have nullified velocity while charging his shadow ball:

https://gfycat.com/NegligibleGrossGraywolf

As you can see, the function nullifies the velocity, but does not stop the increment of gravity caused by the action physics function. If we replace the action physics function, we'll override that blrl context instead.

https://i.imgur.com/TbyUnMe.png


https://gfycat.com/FinishedGenerousIndusriverdolphin

When adding this function to the action physics, the natural IASA is returned and the gravity velocity is replaced entirely.



“Could I write a gecko code that would add a new IASI function, or would I have to edit the existing "ac_Interrupt_AS_Fall" function so that it will be what I desire?”
There are a number of ways you could go about it. Here are my suggestions to get you started:


If you want to be able to use your custom IASA function like any other IASA function...


-- then you’ll need an address to reference it by. Your most pressing problem is going to be designating some static location in the DOL to place (a branch to) your custom code.

You want to figure this out first, because if there is no static or “global” way of referencing it, then you won’t be able to input it into any file data. This is a sticky concept, because while it’s easy enough to work around -- doing so without some care often ends up breaking other codes.

Here’s a fairly low-impact method that I like to use to make a small amount of open space. It’s unlikely to cause compatibility issues, and is easy enough to implement using one (or sometimes, a handful of) static overwrites. These take the form of 04 codes if you’re writing a gecko code:

0435C5D8 CBA280A0

Adding this 04 line to your gecko code will free up 8 bytes from the static rtoc data section in start.dol by making them unused in the game -- allowing you to use them for your own purposes. This will let you to store executable branch-in "pointers" to your custom functions in a static address, in turn allowing you to write references to them with crazy hand.

For more information about this method of creating space, see my post about mytoc.

---

8 bytes is enough for 2 branch instructions, which you may create using C2 codes:

0435C5D8 CBA280A0
C24DE3C0 --------
# code for custom IASA function 1 goes here
C24DE3C4 --------
# code for custom IASA function 2 goes here


Containing your functions in a format like this will make them accessible later via the following function addresses:

804DE3C0 - custom function 1
804DE3C4 - custom function 2

https://i.imgur.com/rC5b9Cm.png



If you want to follow the game's way of creating interrupts...

then it's as you suggested -- you'll need to add the necessary interrupts together with a sequence of function calls. Following the game's example is usually the best practice, but there are almost always opportunities to improvise, and potentially optimize.

The game does it like this:

https://i.imgur.com/hUJNuaw.png


Since gecko codes are placed in an arbitrary location in the code handler, you can't make bl calculations like the game code does unless you’re writing a DOL mod.

If you’re using MCM, you can make static overwrite mods and injection mods in place of 04 and C2 codes. Doing so will allow you to make use of MCM’s special branch syntax -- which lets you use bl instructions similarly to the way the game does.

If you're making a gecko code, you must instead exploit blrls to get around this:

Code:
lis  r3, 0x800C     # r3 = 0x800C0000
ori  r3, r3, 0xAED0 # r3 = 0x800CAED0 = address
mtlr r3             # lr = r3
blrl                # branch link to lr
---

Here's an example gecko code that adds 2 custom IASA functions using this method.

Gecko Code:

Code:
$Custom IASA function Test 1 [Punkline]
0435C5D8 CBA280A0
C24DE3C0 0000000A
7C0802A6 90010004
9421FFF0 93E1000C
7C7F1B78 3D80800C
618CA094 7D8803A6
4E800021 7FE3FB78
3D80800C 618CAED0
7D8803A6 4E800021
83E1000C 38210010
80010004 7C0803A6
4E800020 00000000
C24DE3C4 0000000C
7C0802A6 90010004
9401FFF0 93E1000C
7C7F1B78 3D80800C
618CAED0 7D8803A6
4E800021 2C030000
4182001C C022805C
7FE3FB78 3D808006
618CF190 7D8803A6
4E800021 83E1000C
38210010 80010004
7C0803A6 4E800020
60000000 00000000
ASM:
Code:
-==-
Custom IASA function Test 1
adds the following new IASA functions for Crazy Hand:
804DE3C0 - jump/dash cancel
- this IASA combines the standard jump and turn/dash interrupts

804DE3C4 - slow jump cancel
- this IASA causes a slowed "kneebend" transition to create jump cancels
[Punkline]
1.02 ----- 0x8035C5D8 --- CBA2E9E0 -> CBA280A0
1.02 ----- 0x804DE3C0 --- 43300000 -> Branch
# This injection point is not ever executed by the game.
# This allows us to utilize its address for our function.

# <Interrupt_Jump_or_Dash>
# r3 = player entity, passed from blrl context

# the GPR r1 has a special name, "sp"
# and is dedicated to the purpose of manipulating what is
# called the "runtime stack" through a "stack pointer."

# when writing out a proper function that makes calls,
# it's important to create a "stack frame" like this:

mflr r0            # r0 = link register (fresh from call)
stw  r0, 0x4(sp)   # store the lr at offset 0x4(sp)
stwu sp, -0x10(sp) # store current sp at offset -0x10(sp)

# the 'u' stwu makes it so that the sp is "updated"
# to point at the place we just stored to; so the "stack
# pointer" has now been moved by 0x10 bytes.

# what this means is that we now have 0x10 bytes of
# volatile space that will remain persistent for the
# duration of our function; and any calls we make in it.

# We'll use this to store a register, so that we can
# make use of it without overwriting any data it might be
# storing for another function in the callstack.

stw r31, 0xC(sp) # save r31 in the stack; 0xC(sp)
mr  r31, r3      # store our r3 argument (player entity)

# now we're ready to call our IASA interrupts.
# we're passing r3 = player entity to this function:
lis  r12, 0x800C      # r12 = 0x800C0000
ori  r12, r12, 0xA094 # r12 = 0x800CA094
mtlr r12              # lr = r12
blrl # call 800CA094 - turn/dash cancel

mr   r3, r31       # pass r3 = player entity
lis  r12, 0x800C
ori  r12, r12, 0xAED0
mtlr r12
blrl # call 800CAED0 - jump cancel

# That's it for the IASA functionality!
# Now we just need to restore r31 and return safely.
lwz  r31, 0xC(sp)

# before returning, we collapse the stack frame we made:
addi sp, sp, 0x10 # move the sp back up 0x10 bytes
lwz  r0, 0x4(sp)  # recover our return link
mtlr r0           # put return link back in lr
blr               # branch to link register (return)
nop

1.02 ----- 0x804DE3C4 --- 80000000 -> Branch
# This IASA function is formatted like the previous one.
# It uses an injection to contain its code.

# this one causes a jump cancel that's slower than usual

mflr r0            # r0 = return link
stw  r0, 0x4(sp)   # store in stack
stwu r0, -0x10(sp) # atomic activation record (stwu)
stw  r31, 0xC(sp)  # store r31
mr   r31, r3       # r31 = saved player entity

# in this function, we only call the jump cancle IASA
lis  r12, 0x800C
ori  r12, r12, 0xAED0
mtlr r12
blrl # call 800CAED0 - jump cancel
# r3 will return either a 0 or 1
# if r3 == 1, then a successful jump cancel has occurred
cmpwi r3, 0           # if jump bool == FALSE
beq _return           # then return without change
                      # else, we set framespeed to half
lfs f1, -0x7FA4(rtoc) # f1 = 0.5
mr r3, r31            # pass r3 = player entity
                      # pass f1 = frame speed
lis  r12, 0x8006
ori  r12, r12, 0xf190
mtlr r12
blrl # call frame speed modifier func

_return:
lwz  r31, 0xC(sp)
addi sp, sp, 0x10
lwz  r0, 0x4(sp)
mtlr r0
blr
nop
In this example,
804DE3C0 = jump cancel + dash cancel
804DE3C4 = half-speed jump cancel


If you want to instead try altering the behavior of an existing interrupt...

you may be able to do so without destroying the original interrupt by framing it with a new function.

Most of these IASA functions check an array of bits stored in the player data that represent controller button presses for the current frame. By modifying these bits to temporarily mask out the B-button, you can frame a call to the falling interrupt check and prevent a successful aerial B interrupt -- even if the B button is being pressed.

Here’s where the falling IASA function checks for B-button presses:

https://i.imgur.com/6laAl1I.png


If you follow the call, you can see exactly how this B-button press is checked for:

https://i.imgur.com/xgTGgKK.png


So, with this we know that bit 0x00000200 in player data offset 0x668 represents the B button. We can mask this out by using an inverted rlwinm instruction.

The following example code creates a sequence of operations that loads the controller bits, stores them in a register we’ve saved to the stack frame, masks out the B-button bit in the player data, calls the falling IASA function, and then restores the original controller bits on return.

Gecko Code:
Code:
042EF654 C8228000
C24DD960 0000000A
7C0802A6 90010004
9401FFE0 BFC1000C
83C3002C 83FE0668
57E405EA 909E0668
3D80800C 618CCAAC
7D8803A6 4E800021
93FE0688 BBC1000C
38210020 80010004
7C0803A6 4E800020
60000000 00000000
ASM:
Code:
-==-

Custom IASA function Test 2
adds the following new IASA function for Crazy Hand:
804DD960 - falling IASA with no B-interrupts
[Punkline]
1.02 ----- 0x802EF654 --- C822DF80 -> C8228000
1.02 ----- 0x804DD960 --- 43300000 -> Branch
mflr r0
stw  r0, 0x4(sp)
stwu r0, -0x20(sp)
stmw r30, 0xC(sp)    # store r31 and r30

lwz  r30, 0x2C(r3)   # r30 = internal player data address
lwz  r31, 0x668(r30) # r31 = controller bits

rlwinm r4, r31, 0, 23, 21 # mask out the B button
stw  r4, 0x668(r30) # update buttons for duration of call

lis  r12, 0x800c
ori  r12, r12, 0xcaac
mtlr r12
blrl

stw r31, 0x688(r30) # restore buttons before returning

lmw  r30, 0xC(sp)
addi sp, sp, 0x20
lwz  r0, 0x4(sp)
mtlr r0
blr
nop


“Could I use AIFs in some way to, for example, make Ganondorf's aerial side-b interrupt into his aerial down-B? Is there another method used for this, if it is at all possible?”
I believe so, yes.

Action state transitions are all handled by a common function (800693ac) and every interrupt in the game can be broken down into a condition for calling it. I’m not familiar with any vanilla AIFs that do this for aerial down-B, but it would be a very simple matter to write one.

Alternatively, you could use the event syntax provided by the code in this thread to force an action state transition with a timed event.

Putting this at the end of dorf’s side-b events should do the trick:

5F 00 01 67

https://gfycat.com/SecondhandTidyElectriceel

---

5F 00 01 67

If you replace the highlighted 0 there with an ‘8’, you can also tell the game to continue reading subactions from the previous action, while continuing into the animation of the new action. If you are extending the space available for subaction event data, this can allow you to create very complex action chains that have unique moveset data for all involved actions:

I’ve made extensive use of Achilles’ custom character data modification subaction event and custom projectile subaction event along with my custom action state logic subaction event in my example to make a ridiculous modification to Luigi’s taunt (Appeal)

Code:
04 00 00 08 A2 00 08 01 04 00 00 26 F4 FF 00 2F 41 00 40 00 3F 80 40 20 3F 80 42 00 04 00 00 06 5F 80 81 65 04 00 00 08 2C 00 50 01 03 84 00 00 00 00 00 00 2D 00 00 13 00 9C 00 8B 0C 00 00 06 28 80 00 00 00 27 00 00 00 00 00 00 00 00 00 00 05 00 05 00 B8 50 00 04 04 00 00 06 10 00 00 00 F8 FF 00 0C 03 80 00 2C BF 80 00 00 5F 80 81 08 04 00 00 08 A2 00 08 01 04 00 00 26 F8 FF 00 0C 02 00 21 BC 80 14 29 5C 60 00 00 00 04 00 00 06 5F 80 81 65 04 00 00 08 2C 00 50 08 03 84 00 00 00 00 00 00 2D 14 00 13 19 04 00 8B 0C 00 00 06 28 80 00 00 00 37 00 00 00 00 00 00 00 00 00 00 05 00 05 00 B8 30 00 04 04 00 00 06 10 00 00 00 F8 FF 00 0C 03 80 00 2C BF 80 00 00 5F 80 81 08 04 00 00 08 A2 00 08 01 04 00 00 26 0C 00 00 04 F4 FF 00 CC 41 00 40 00 3F 80 00 00 3F 80 42 00 04 00 00 02 10 00 00 00 5F 80 80 C8 04 00 00 16 F8 FF 00 14 02 00 00 E0 00 00 00 01 02 00 00 84 40 20 00 00 5F 80 81 66 04 00 00 02 0C 00 00 0A 28 00 00 00 00 1E 00 00 00 00 00 00 00 00 00 00 05 00 05 00 F4 FF 00 C7 00 00 00 00 00 00 40 40 40 80 42 00 04 00 00 05 10 00 00 00 04 00 00 05 5F 00 00 5B
Try fitting THAT over Luigi’s taunt hitbox~

https://gfycat.com/BoilingGrouchyGrebe

---

Unclepunch also has a custom event that he posted recently which lets you force action state transitions in ways that can blend animations together by utilizing float arguments passed into the transition function. It has a lot of potential for creating unique-looking animations by blending 2 target frames from different actions together:

https://gfycat.com/GraciousPerfectAustralianshelduck



“How would I make it so that an action state can only be used once before landing on the ground? There are not any action states like this in vanilla (with the exception of Peach's float), but certain ones have a similar mechanic (namely the "stalling" moves such as Marth's and Mario's Side-Bs, or Luigi's Down-B).”
This requires that you have your code persistently memorize the state of your custom mechanic for each character on the screen. Only a single bit is required to mark the state as “TRUE” or “FALSE” -- but there must be one for each active player entity.

There are an arbitrary number of players that can be loaded at once, and you must account for the worst case if you want to make a stable code. This problem is complicated by the fact that, though there are only 6 player “slots” there seems to be no obvious limit on the number of player “entities” that can be active at a time. Player entities double as display models, so their use is not restricted to just the standard player slots.

Thankfully the game includes a 2-way link structure between all player entities, which allows you to put them in an order (and thus create an index of player entities.) Achilles explains more about it here.

External player entity data offsets 0x8 and 0xC point to the next and previous player entities, respectively. This is true of all entities of a common class (player, item, stage, camera, etc) -- they are linked together to form grouped chains.

---

So here’s a snippet of code that takes r3 = player entity, and then spits out an ID based on its place in this entity chain. It simply counts the “previous entity” links it finds and creates an ID:

Code:
# r3 = player entity

li r4, 0              # load ID 0 to start count
mr r5, r3             # copy entity for loop

_loop:
lwz r5, 0xC(r5)       # load previous entity
cmpwi r5, 0           # end of chain will be 0 for null
beq- _ID_is_now_in_r4 # if previous entity == 0 then finish
addi r4, r4, 1        # else add 1 to count
b _loop               # and check for another link

_ID_is_now_in_r4:
# code that does something with ID goes here
So let's pretend you have a game going with P1 = Fox, P2 = Ice Climbers, P3 = Zelda, and P4 = Ganondorf.

When running the above code snippet for a given player entity, it will return the following IDs:

0 = Fox
1 = Popo
2 = Nana
3 = Zelda
4 = Sheik
5 = Ganondorf

So, now imagine you have a word of space to store memory with. That’s 4 bytes, or 32 bits.

If you wanted to flag a player character as true, you could just use the Nth bit of the word to represent an indexed player entity. Having players organized in an ordered list like this lets us use their corresponding IDs as a variable for the number of bits we want to shift.

---

Let’s say that (still using the above example player entity chain) you’ve modded both Fox and Ganondorf to have a landing-based mechanic.

An injection point somewhere that runs once per frame per character would need to be responsible for checking if the player is on the ground or in the air. If the player is grounded, then it finds the Nth bit representing that player entity, and resets it to 0.

Another injection point intended to be used by Ganondorf in the air would then need to check its Nth bit for a 1. If it is 0 (has been reset) then it will execute your custom mechanic and set the bit to 1 -- preventing it from executing again until it has been reset from landing.

Another injection point does the same to create Fox’s custom mechanic.

---

Fox and Ganondorf in this hypothetical context are entity IDs 0 and 5. The code’s memory is encoded by translating those into shifted bits.

If you paused the match after both characters used their mechanic, and have not yet hit the ground -- it would create a mask that looks like this:

33 = as dec

0x00000021 = as hex
0000 0000 0000 0000 0000 0000 0010 0001 = as bin; array of bools
---- ---- ---- ---- ---- ---- --54 3210 = as bit IDs


When your code needs to READ one of these bits, it would do something like this:
Code:
# r3 = address of memory
# r4 = player entity ID = N

lwz  r5, 0x0(r3) # r5 = memory
li   r6, 1       # r6 = unshifted mask
slw  r6, r6, r4  # r6 = Nth bit mask
and. r6, r6, r5  # r6 = Nth bit
beq _FALSE       # if bit == 0, it’s FALSE
_TRUE:           # else, it’s TRUE
# execute code here if Nth bit is true
_FALSE:
# execute code here if Nth bit is false

To write a TRUE bit to memory, it would do something like this:
Code:
# r3 = address of memory
# r4 = player entity ID = N

lwz  r5, 0x0(r3) # r5 = memory
li   r6, 1       # r6 = unshifted mask 0x00000001
slw  r6, r6, r4  # r6 = Nth bit mask (r6 << N)
or   r6, r6, r5  # r6 = Memory with Nth bit as TRUE
stw  r5, 0x0(r3) # update memory

To write a FALSE bit to memory, it would do something like this:
Code:
# r3 = address of memory
# r4 = player entity ID = N

lwz  r5, 0x0(r3) # r5 = memory
li   r6, 1       # r6 = unshifted mask
slw  r6, r6, r4  # r6 = Nth bit mask
li   r0, -1      # r0 = 0xFFFFFFFF
xor  r6, r6, r0  # r6 = inverted Nth bit mask
and  r6, r6, r5  # r6 = Memory with Nth bit as FALSE
stw  r5, 0x0(r3) # update memory


“I am just starting to get into writing assembly code, so would this be too advanced for me to do?”
I speak for myself, but learning PPC only seemed like an impossibly daunting task up to the point that I decided to dive into it. Getting your feet wet is a fine way to learn, but when there isn’t really any danger of drowning, sometimes it’s worth it to challenge yourself. In my experience, the real trick is just knowing what you can tolerate, and to take breaks when you need to digest information. Pace yourself, and you can eat a whole elephant so long as you do it one bite at a time.

It takes a little while to become familiar with the way things work in PPC, and that can be an obstacle when starting out -- so don't let yourself get overwhelmed. If you want to, you can always try and tackle your more complicated project ideas after you've learned more about what you're trying to do. Study Melee’s code, and other people’s codes if they provide ASM. You’ll learn new secrets.

I'd like to encourage you to give these ideas a try. If you feel so inclined, make a WIP thread and ask any questions you have about things you need help with while posting your progress.
 
Last edited:

Dodoshian

Smash Rookie
Joined
Jul 7, 2016
Messages
20
Location
Mobile, Alabama
#13
I like your creativity~

Sorry for the delayed response, but you had a lot of good questions and I wanted to take the time to elaborate in my responses:

Edit: typos and stuff





Absolutely! Studying and manipulating move logic is actually a really great way to learn about how callback functions work in PPC.

Here’s how the game does it:

There’s a routine in the game that runs once per frame/character in order to update the controller button and analog values in a player’s internal data table. At the end of this routine however is a special “blrl” instruction -- which is where IASA functions are executed.

blrl” is a lot like a “bl” instruction. They're both used to call functions with a return link in the link register (lr) . With a bit of extra work though, a blrl instruction can be a lot more powerful than a bl instruction.

A bl instruction is used to make calls to functions in a direct, hardcoded way. The blrl however uses a variable instead of a hardcoded constant, meaning that you can tell it to branch to a function that has been determined by a separate process, or mapped to an index.

https://i.imgur.com/WARnOaY.png


In the case of this IASA blrl, the game is loading in a pre-calculated address from a player’s internal data offset 0x219C. Each action state transition updates this 0x219C offset by copying in an address specified in the file data you can modify with Crazy Hand.

In other words, the action state transition function "writes" an IASA function address to this offset once per player action, and the button update "reads" the function once per frame.

---

This all means that, essentially, an “IASA function” is just a set of “extra instructions” to execute at the end of a button update routine that runs for each frame/character. Changing what those “extra instructions” are is simply a matter of intercepting the pointer address at player data offset 0x219C and pointing it to your custom code.

This is exactly how Link is made able to jump-cancel his down-smash with Achilles’s event syntax:

F8 FF 00 0C 02 00 21 9C 80 0C AE D0
F8 FF = internal player data memory write
00 0C = event data string is 12 bytes long
02 00 = overwrite word
21 9C = write to player data offset 0x219C
800CAED0 = address of jump cancel interrupt instructions

By writing “800CAED0” to this player data offset, the natural button update is forced to run fox’s jump-cancel IASA even though it isn’t specified as Link’s default move logic behavior. This is because we’ve managed to intercept the pointer, which only resets to file data defaults on new actions.

(With this in mind, you can also use 2 of these events to sandwich a timer event in order to create keyframes that define a window of IASA logic. Likewise, you may simply use the end of an action as your second keyframe. )

---

The overriding function doesn’t necessarily have to be an “IASA function” -- though it will take the place of one. So long as it has a compatible argument interface (r3 = player entity) and is otherwise formatted correctly (stack frame, register usage, etc) it should execute any instructions you give it without issues.

For example, this other game function (8007E2FC) appears to be used to nullify all velocity in a player entity when they are catching a cliff edge. It only requires the argument “r3 = player entity” and thus has a compatible interface with the IASA blrl context.

https://i.imgur.com/6GERVM3.png


Placing it in Mewtwo’s shadow ball charge IASA function slot (using crazy hand) will run the foreign function in place of button checks, and cause him to have nullified velocity while charging his shadow ball:

https://gfycat.com/NegligibleGrossGraywolf

As you can see, the function nullifies the velocity, but does not stop the increment of gravity caused by the action physics function. If we replace the action physics function, we'll override that blrl context instead.

https://i.imgur.com/TbyUnMe.png


https://gfycat.com/FinishedGenerousIndusriverdolphin

When adding this function to the action physics, the natural IASA is returned and the gravity velocity is replaced entirely.





There are a number of ways you could go about it. Here are my suggestions to get you started:


If you want to be able to use your custom IASA function like any other IASA function...


-- then you’ll need an address to reference it by. Your most pressing problem is going to be designating some static location in the DOL to place (a branch to) your custom code.

You want to figure this out first, because if there is no static or “global” way of referencing it, then you won’t be able to input it into any file data. This is a sticky concept, because while it’s easy enough to work around -- doing so without some care often ends up breaking other codes.

Here’s a fairly low-impact method that I like to use to make a small amount of open space. It’s unlikely to cause compatibility issues, and is easy enough to implement using one (or sometimes, a handful of) static overwrites. These take the form of 04 codes if you’re writing a gecko code:

0435C5D8 CBA280A0

Adding this 04 line to your gecko code will free up 8 bytes from the static rtoc data section in start.dol by making them unused in the game -- allowing you to use them for your own purposes. This will let you to store executable branch-in "pointers" to your custom functions in a static address, in turn allowing you to write references to them with crazy hand.

For more information about this method of creating space, see my post about mytoc.

---

8 bytes is enough for 2 branch instructions, which you may create using C2 codes:

0435C5D8 CBA280A0
C24DE3C0 --------
# code for custom IASA function 1 goes here
C24DE3C4 --------
# code for custom IASA function 2 goes here


Containing your functions in a format like this will make them accessible later via the following function addresses:

804DE3C0 - custom function 1
804DE3C4 - custom function 2

https://i.imgur.com/rC5b9Cm.png



If you want to follow the game's way of creating interrupts...

then it's as you suggested -- you'll need to add the necessary interrupts together with a sequence of function calls. Following the game's example is usually the best practice, but there are almost always opportunities to improvise, and potentially optimize.

The game does it like this:

https://i.imgur.com/hUJNuaw.png


Since gecko codes are placed in an arbitrary location in the code handler, you can't make bl calculations like the game code does unless you’re writing a DOL mod.

If you’re using MCM, you can make static overwrite mods and injection mods in place of 04 and C2 codes. Doing so will allow you to make use of MCM’s special branch syntax -- which lets you use bl instructions similarly to the way the game does.

If you're making a gecko code, you must instead exploit blrls to get around this:

Code:
lis  r3, 0x800C     # r3 = 0x800C0000
ori  r3, r3, 0xAED0 # r3 = 0x800CAED0 = address
mtlr r3             # lr = r3
blrl                # branch link to lr
---

Here's an example gecko code that adds 2 custom IASA functions using this method.

Gecko Code:

Code:
$Custom IASA function Test 1 [Punkline]
0435C5D8 CBA280A0
C24DE3C0 0000000A
7C0802A6 90010004
9421FFF0 93E1000C
7C7F1B78 3D80800C
618CA094 7D8803A6
4E800021 7FE3FB78
3D80800C 618CAED0
7D8803A6 4E800021
83E1000C 38210010
80010004 7C0803A6
4E800020 00000000
C24DE3C4 0000000C
7C0802A6 90010004
9401FFF0 93E1000C
7C7F1B78 3D80800C
618CAED0 7D8803A6
4E800021 2C030000
4182001C C022805C
7FE3FB78 3D808006
618CF190 7D8803A6
4E800021 83E1000C
38210010 80010004
7C0803A6 4E800020
60000000 00000000
ASM:
Code:
-==-
Custom IASA function Test 1
adds the following new IASA functions for Crazy Hand:
804DE3C0 - jump/dash cancel
- this IASA combines the standard jump and turn/dash interrupts

804DE3C4 - slow jump cancel
- this IASA causes a slowed "kneebend" transition to create jump cancels
[Punkline]
1.02 ----- 0x8035C5D8 --- CBA2E9E0 -> CBA280A0
1.02 ----- 0x804DE3C0 --- 43300000 -> Branch
# This injection point is not ever executed by the game.
# This allows us to utilize its address for our function.

# <Interrupt_Jump_or_Dash>
# r3 = player entity, passed from blrl context

# the GPR r1 has a special name, "sp"
# and is dedicated to the purpose of manipulating what is
# called the "runtime stack" through a "stack pointer."

# when writing out a proper function that makes calls,
# it's important to create a "stack frame" like this:

mflr r0            # r0 = link register (fresh from call)
stw  r0, 0x4(sp)   # store the lr at offset 0x4(sp)
stwu sp, -0x10(sp) # store current sp at offset -0x10(sp)

# the 'u' stwu makes it so that the sp is "updated"
# to point at the place we just stored to; so the "stack
# pointer" has now been moved by 0x10 bytes.

# what this means is that we now have 0x10 bytes of
# volatile space that will remain persistent for the
# duration of our function; and any calls we make in it.

# We'll use this to store a register, so that we can
# make use of it without overwriting any data it might be
# storing for another function in the callstack.

stw r31, 0xC(sp) # save r31 in the stack; 0xC(sp)
mr  r31, r3      # store our r3 argument (player entity)

# now we're ready to call our IASA interrupts.
# we're passing r3 = player entity to this function:
lis  r12, 0x800C      # r12 = 0x800C0000
ori  r12, r12, 0xA094 # r12 = 0x800CA094
mtlr r12              # lr = r12
blrl # call 800CA094 - turn/dash cancel

mr   r3, r31       # pass r3 = player entity
lis  r12, 0x800C
ori  r12, r12, 0xAED0
mtlr r12
blrl # call 800CAED0 - jump cancel

# That's it for the IASA functionality!
# Now we just need to restore r31 and return safely.
lwz  r31, 0xC(sp)

# before returning, we collapse the stack frame we made:
addi sp, sp, 0x10 # move the sp back up 0x10 bytes
lwz  r0, 0x4(sp)  # recover our return link
mtlr r0           # put return link back in lr
blr               # branch to link register (return)
nop

1.02 ----- 0x804DE3C4 --- 80000000 -> Branch
# This IASA function is formatted like the previous one.
# It uses an injection to contain its code.

# this one causes a jump cancel that's slower than usual

mflr r0            # r0 = return link
stw  r0, 0x4(sp)   # store in stack
stwu r0, -0x10(sp) # atomic activation record (stwu)
stw  r31, 0xC(sp)  # store r31
mr   r31, r3       # r31 = saved player entity

# in this function, we only call the jump cancle IASA
lis  r12, 0x800C
ori  r12, r12, 0xAED0
mtlr r12
blrl # call 800CAED0 - jump cancel
# r3 will return either a 0 or 1
# if r3 == 1, then a successful jump cancel has occurred
cmpwi r3, 0           # if jump bool == FALSE
beq _return           # then return without change
                      # else, we set framespeed to half
lfs f1, -0x7FA4(rtoc) # f1 = 0.5
mr r3, r31            # pass r3 = player entity
                      # pass f1 = frame speed
lis  r12, 0x8006
ori  r12, r12, 0xf190
mtlr r12
blrl # call frame speed modifier func

_return:
lwz  r31, 0xC(sp)
addi sp, sp, 0x10
lwz  r0, 0x4(sp)
mtlr r0
blr
nop
In this example,
804DE3C0 = jump cancel + dash cancel
804DE3C4 = half-speed jump cancel


If you want to instead try altering the behavior of an existing interrupt...

you may be able to do so without destroying the original interrupt by framing it with a new function.

Most of these IASA functions check an array of bits stored in the player data that represent controller button presses for the current frame. By modifying these bits to temporarily mask out the B-button, you can frame a call to the falling interrupt check and prevent a successful aerial B interrupt -- even if the B button is being pressed.

Here’s where the falling IASA function checks for B-button presses:

https://i.imgur.com/6laAl1I.png


If you follow the call, you can see exactly how this B-button press is checked for:

https://i.imgur.com/xgTGgKK.png


So, with this we know that bit 0x00000200 in player data offset 0x668 represents the B button. We can mask this out by using an inverted rlwinm instruction.

The following example code creates a sequence of operations that loads the controller bits, stores them in a register we’ve saved to the stack frame, masks out the B-button bit in the player data, calls the falling IASA function, and then restores the original controller bits on return.

Gecko Code:
Code:
042EF654 C8228000
C24DD960 0000000A
7C0802A6 90010004
9401FFE0 BFC1000C
83C3002C 83FE0668
57E405EA 909E0668
3D80800C 618CCAAC
7D8803A6 4E800021
93FE0688 BBC1000C
38210020 80010004
7C0803A6 4E800020
60000000 00000000
ASM:
Code:
-==-

Custom IASA function Test 2
adds the following new IASA function for Crazy Hand:
804DD960 - falling IASA with no B-interrupts
[Punkline]
1.02 ----- 0x802EF654 --- C822DF80 -> C8228000
1.02 ----- 0x804DD960 --- 43300000 -> Branch
mflr r0
stw  r0, 0x4(sp)
stwu r0, -0x20(sp)
stmw r30, 0xC(sp)    # store r31 and r30

lwz  r30, 0x2C(r3)   # r30 = internal player data address
lwz  r31, 0x668(r30) # r31 = controller bits

rlwinm r4, r31, 0, 23, 21 # mask out the B button
stw  r4, 0x668(r30) # update buttons for duration of call

lis  r12, 0x800c
ori  r12, r12, 0xcaac
mtlr r12
blrl

stw r31, 0x688(r30) # restore buttons before returning

lmw  r30, 0xC(sp)
addi sp, sp, 0x20
lwz  r0, 0x4(sp)
mtlr r0
blr
nop



I believe so, yes.

Action state transitions are all handled by a common function (800693ac) and every interrupt in the game can be broken down into a condition for calling it. I’m not familiar with any vanilla AIFs that do this for aerial down-B, but it would be a very simple matter to write one.

Alternatively, you could use the event syntax provided by the code in this thread to force an action state transition with a timed event.

Putting this at the end of dorf’s side-b events should do the trick:

5F 00 01 67

https://gfycat.com/SecondhandTidyElectriceel

---

5F 00 01 67

If you replace the highlighted 0 there with an ‘8’, you can also tell the game to continue reading subactions from the previous action, while continuing into the animation of the new action. If you are extending the space available for subaction event data, this can allow you to create very complex action chains that have unique moveset data for all involved actions:



https://gfycat.com/BoilingGrouchyGrebe

---

Unclepunch also has a custom event that he posted recently which lets you force action state transitions in ways that can blend animations together by utilizing float arguments passed into the transition function. It has a lot of potential for creating unique-looking animations by blending 2 target frames from different actions together:

https://gfycat.com/GraciousPerfectAustralianshelduck





This requires that you have your code persistently memorize the state of your custom mechanic for each character on the screen. Only a single bit is required to mark the state as “TRUE” or “FALSE” -- but there must be one for each active player entity.

There are an arbitrary number of players that can be loaded at once, and you must account for the worst case if you want to make a stable code. This problem is complicated by the fact that, though there are only 6 player “slots” there seems to be no obvious limit on the number of player “entities” that can be active at a time. Player entities double as display models, so their use is not restricted to just the standard player slots.

Thankfully the game includes a 2-way link structure between all player entities, which allows you to put them in an order (and thus create an index of player entities.) Achilles explains more about it here.

External player entity data offsets 0x8 and 0xC point to the next and previous player entities, respectively. This is true of all entities of a common class (player, item, stage, camera, etc) -- they are linked together to form grouped chains.

---

So here’s a snippet of code that takes r3 = player entity, and then spits out an ID based on its place in this entity chain. It simply counts the “previous entity” links it finds and creates an ID:

Code:
# r3 = player entity

li r4, 0              # load ID 0 to start count
mr r5, r3             # copy entity for loop

_loop:
lwz r5, 0xC(r5)       # load previous entity
cmpwi r5, 0           # end of chain will be 0 for null
beq- _ID_is_now_in_r4 # if previous entity == 0 then finish
addi r4, r4, 1        # else add 1 to count
b _loop               # and check for another link

_ID_is_now_in_r4:
# code that does something with ID goes here
So let's pretend you have a game going with P1 = Fox, P2 = Ice Climbers, P3 = Zelda, and P4 = Ganondorf.

When running the above code snippet for a given player entity, it will return the following IDs:

0 = Fox
1 = Popo
2 = Nana
3 = Zelda
4 = Sheik
5 = Ganondorf

So, now imagine you have a word of space to store memory with. That’s 4 bytes, or 32 bits.

If you wanted to flag a player character as true, you could just use the Nth bit of the word to represent an indexed player entity. Having players organized in an ordered list like this lets us use their corresponding IDs as a variable for the number of bits we want to shift.

---

Let’s say that (still using the above example player entity chain) you’ve modded both Fox and Ganondorf to have a landing-based mechanic.

An injection point somewhere that runs once per frame per character would need to be responsible for checking if the player is on the ground or in the air. If the player is grounded, then it finds the Nth bit representing that player entity, and resets it to 0.

Another injection point intended to be used by Ganondorf in the air would then need to check its Nth bit for a 1. If it is 0 (has been reset) then it will execute your custom mechanic and set the bit to 1 -- preventing it from executing again until it has been reset from landing.

Another injection point does the same to create Fox’s custom mechanic.

---

Fox and Ganondorf in this hypothetical context are entity IDs 0 and 5. The code’s memory is encoded by translating those into shifted bits.

If you paused the match after both characters used their mechanic, and have not yet hit the ground -- it would create a mask that looks like this:

33 = as dec

0x00000021 = as hex
0000 0000 0000 0000 0000 0000 0010 0001 = as bin; array of bools
---- ---- ---- ---- ---- ---- --54 3210 = as bit IDs


When your code needs to READ one of these bits, it would do something like this:
Code:
# r3 = address of memory
# r4 = player entity ID = N

lwz  r5, 0x0(r3) # r5 = memory
li   r6, 1       # r6 = unshifted mask
slw  r6, r6, r4  # r6 = Nth bit mask
and. r6, r6, r5  # r6 = Nth bit
beq _FALSE       # if bit == 0, it’s FALSE
_TRUE:           # else, it’s TRUE
# execute code here if Nth bit is true
_FALSE:
# execute code here if Nth bit is false

To write a TRUE bit to memory, it would do something like this:
Code:
# r3 = address of memory
# r4 = player entity ID = N

lwz  r5, 0x0(r3) # r5 = memory
li   r6, 1       # r6 = unshifted mask 0x00000001
slw  r6, r6, r4  # r6 = Nth bit mask (r6 << N)
or   r6, r6, r5  # r6 = Memory with Nth bit as TRUE
stw  r5, 0x0(r3) # update memory

To write a FALSE bit to memory, it would do something like this:
Code:
# r3 = address of memory
# r4 = player entity ID = N

lwz  r5, 0x0(r3) # r5 = memory
li   r6, 1       # r6 = unshifted mask
slw  r6, r6, r4  # r6 = Nth bit mask
li   r0, -1      # r0 = 0xFFFFFFFF
xor  r6, r6, r0  # r6 = inverted Nth bit mask
and  r6, r6, r5  # r6 = Memory with Nth bit as FALSE
stw  r5, 0x0(r3) # update memory




I speak for myself, but learning PPC only seemed like an impossibly daunting task up to the point that I decided to dive into it. Getting your feet wet is a fine way to learn, but when there isn’t really any danger of drowning, sometimes it’s worth it to challenge yourself. In my experience, the real trick is just knowing what you can tolerate, and to take breaks when you need to digest information. Pace yourself, and you can eat a whole elephant so long as you do it one bite at a time.

It takes a little while to become familiar with the way things work in PPC, and that can be an obstacle when starting out -- so don't let yourself get overwhelmed. If you want to, you can always try and tackle your more complicated project ideas after you've learned more about what you're trying to do. Study Melee’s code, and other people’s codes if they provide ASM. You’ll learn new secrets.

I'd like to encourage you to give these ideas a try. If you feel so inclined, make a WIP thread and ask any questions you have about things you need help with while posting your progress.
Thank you. You have given me a goldmine of information! I will definitely take some time to study what you have posted, as a lot of it is really complicated. I think I should just stick to the basic stuff for now. PPC code-stuff seems much harder to write than to pick apart and understand, and I think this is where I am having trouble; I just don't know where or how to begin.
 
Last edited:
Top