header lorom ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Von Fahrenheit's Cape to NSMBU Acorn patch ;; ;; -V 1.3 ;; ;; ;; ;; Requested by Luigiguy42 ;; ;; GFX by Luigiguy42 ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; --Changelog-- ; - 1.0: ; ; - First functioning version! Yay! ; ; -1.1: ; ; - Fixed a bug with floating on top of enemies hurting the player. ; - The player can now double jump out of a spin jump. The direction is determined by Xspeed. ; ; -1.2: ; ; - Floating now looks a lot better (and more similiar to NSMBU). ; - If the player is not holding left/right, Xspeed determines what direction to double jump in. ; - Double jumping out of a spinjump now works like any other double jump. ; - The player can now cling to walls when they have a cape. ; - Sticking to a wall lets the player walljump off of it and grants an extra double jump. ; ; -1.3: ; ; - The feather sprite now acts like a NSMBU acorn (comes out of the box, then rolls a bit). ; - There are now a bunch of conditionals that can be set for more flexibility. ; - If you apply the hijacks, then change your mind and apply the patch without them, the original code is restored. ; - A bunch of print commands are used to tell you where stuff is inserted. ; - Now includes GFX by Luigiguy42! (No, it's not compatible with dsx.asm. It uses way too much CPU power, anyway.) ; --Features-- ; - Pushing A in midair will make Mario (or Luigi) perform a double jump. ; - Removed cape spin. ; - Cape no longer has a hitbox while spinjumping. ; - While the player has a cape, they can cling to walls. ; - While clinging to a wall, the player can jump off of it by pushing B. ; - Feather sprites will act like NSMBU acorns. ; - Acorn GFX will dynamically be uploaded to VRAM (overwrites the feather tile). ;==========================; ;DEFINES AND CONFIGURATIONS; ;==========================; ; --BOOLEANS-- ; Don't mess with these! !True = 1 !False = 0 ; --CONDITIONALS-- !BounceJump = !True ; Gives Mario an extra double jump after bouncing on an enemy. !WallJump = !False ; Gives Mario an extra double jump after clinging to a wall. !AcornSprite = !True ; Inserts Acorn.bin and makes the feather sprite act like an Acorn. !NewGFX = !True ; Inserts GFX32.bin and load graphics from it when Mario has an Acorn. ; --RAM-- ; Point these to FREE addresses! Otherwise you will get conflicts! !AcornTimer = $58 !DoubleJump = $60 !WallCling = $61 !JoypadLock = $62 !LockTimer = $63 ; --VALUES-- !ExtraFloat = $08 ; How long Mario will float after letting go of B. 00 is invalid. !FloatSpeed = $10 ; How fast Mario descends while floating. Currently same as cape. !YSpeed = $B0 ; How high Mario will double jump. !Xleft = $E8 ; How much Xspeed is added when double jumping left. !Xright = $18 ; How much Xspeed is added when double jumping right. !WallLeft = $D8 ; What Xspeed is set to when wall jumping left. !WallRight = $28 ; What Xspeed is set to when wall jumping right. !TimeToLock = $10 ; Determines how long the joypad will be disabled after walljumping. print " " if !True else assert !True print "Don't mess with the booleans, please." print " " endif ;=================; ;HIJACKS AND STUFF; ;=================; org $008655 ; Controller update routine (this is actually a lazy hijack) autoclean JSL UPDATE_JUMP ; Set up double jump org $0086C1 ; The end of the controller update routine autoclean JSL LOCK_JOYPAD ; Hijack this NOP ; Clean up garbage org $00D044 ; Cape collision routine RTS ; Disable this org $00D062 ; Cape spin routine LDA $19 ; Disable this BRA $1B ; Skip to shoot fire routine org $00D674 ; Flight initialization routine (kind of) db $80 ; Disable this org $00D7B9 ; Data table with a bunch of Yspeeds db !FloatSpeed ; Change first byte (floating with cape) org $00D908 ; Some physics routine LDA #!ExtraFloat STA $14A5 freecode : prot CapeImg,XSpeed,XSpeedWall,Acorn_GFX,GFX_Source print "Code starts at $", pc, "." ;=================; ;CLEAR DOUBLE JUMP; ;=================; UPDATE_JUMP: STA $0DA4 ;\ Overwritten code TAY ;/ if !AcornSprite ;\ LDA !AcornTimer ; | BEQ + ; | DEC A ; | Decrement Acorn Timer STA !AcornTimer ; | + ; | endif ;/ JSR WALL_CLING ; I split these up for readability LDA !WallCling BEQ .NoWallJump if !WallJump ; > Conditional start STZ !DoubleJump print "Clinging to a wall will give an additional double jump." else print "Clinging to a wall will not give an additional double jump." endif ; > Conditional end LDA $16 ;\ Check if jump was pressed this frame BPL .NoWallJump ;/ PHX ;\ LDX $76 ; | LDA.l XSpeedWall,x ; | PLX ; | Execute wall jump STA $7B ; | LDA #$B0 ; | STA $7D ;/ LDA #$01 ;\ Play jump sound STA $1DFA ;/ LDA $76 ;\ INC A ; | STA !JoypadLock ; | Lock joypad LDA #!TimeToLock ; | STA !LockTimer ;/ .NoWallJump LDA $13E0 ;\ CMP #$0C ; | Set cape if pose is 0C BEQ ++ ;/ CMP #$24 ;\ BEQ + ; | Only overwrite poses 24 and 3C CMP #$3C ; | BNE .NoImage ;/ LDA $7D ;\ Only overwrite pose 3C while descending BMI .NoImage ;/ + LDA $14A5 ;\ Only set pose and cape image while floating BEQ .NoImage ;/ LDA #$0C ;\ Set Mario's double jump pose STA $13E0 ;/ LDA $7D CMP #$20 BCS .NoImage ++ PHX ;\ LDA $14 ; | LSR A ; | LSR A ; | AND #$03 ; | Set Mario's double jump cape image TAX ; | LDA.l CapeImg,x ; | STA $13DF ; | PLX ;/ .NoImage LDA $74 ;\ Clear double jump while climbing BNE + ;/ LDA $77 AND #$04 BEQ DOUBLE_JUMP + STZ !DoubleJump LDA $4218 ;\ Overwritten code AND #$F0 ;/ RTL if !BounceJump ; > Conditional start pushpc org $01AA33 ; Give an extra double jump when bouncing off of an enemy autoclean JSL UPDATE_JUMP_Bounce pullpc .Bounce STZ !DoubleJump LDA #$A8 ;\ Overwritten code STA $7D ;/ RTL print "Bouncing on an enemy will give an additional double jump." else pushpc if read1($01AA33) == $A9 print "The bouncing on enemy routine has not been modified." else org $01AA33 LDA #$A8 STA $7D print "Repaired 4 bytes of the bouncing on enemy routine at $01AA33." endif pullpc endif ; > Conditional end ;===================; ;DOUBLE JUMP ROUTINE; ;===================; DOUBLE_JUMP: LDA $19 ;\ CMP #$02 ; | Only allow double jumping with a cape BNE .Return ;/ LDA $18 ;\ Only double jump when pushing A BPL .Return ;/ LDA !DoubleJump ;\ Only double jump once each jump BNE .Return ;/ LDA !WallCling ;\ Don't allow double jump while clinging to a wall BNE .Return ;/ LDA $15 ; Load controller data 1 AND #$03 BEQ .NoDirection ROR A BCC .NotRight LDA #$01 ;\ STA $76 ; | Direction = right BRA .WriteSpeed ;/ .NotRight ROR A BCC .NoDirection STZ $76 ;\ Direction = left BRA .WriteSpeed ;/ .NoDirection LDA $7B ;\ ROL A ; | ROL A ; | If left/right are not held.. AND #$01 ; | ..use highest bit of Xspeed to determine direction EOR #$01 ; | STA $76 ;/ .WriteSpeed PHX LDX $76 ; X = player direction LDA.l XSpeed,x CLC ADC $7B ;\ Update player Xspeed STA $7B ;/ PLX STZ $140D ; Clear spin jump LDA #!YSpeed ;\ Give player some Yspeed STA $7D ;/ LDA #$01 ;\ Play jump sound STA $1DFA ;/ INC !DoubleJump ; Disable infinite double jumping .Return LDA $4218 ;\ Overwritten code AND #$F0 ;/ RTL ;==================; ;WALL CLING ROUTINE; ;==================; WALL_CLING: LDA $19 ;\ CMP #$02 ; | Can only cling to wall with a Acorn BNE .Return ;/ LDA $77 ;\ AND #$04 ; | Can only cling to wall in midair BNE .Return ;/ LDA $7D ;\ Can only cling to wall while descending BMI .Return ;/ LDA !WallCling ROR A BCS .ClingRight LDA $77 ROR A BCC .NotRight .ClingRight LDA $15 ROR A BCC .NotRight STZ $7D ;\ STZ $76 ; | Set pose LDA #$0D ; | STA $13E0 ;/ LDA #$01 STA !WallCling BRA .ClingToWall .NotRight LDA !WallCling ROR A ROR A BCS .ClingLeft LDA $77 ROR A ROR A BCC .Return .ClingLeft LDA $15 ROR A ROR A BCC .Return STZ $7D ;\ LDA #$01 ; | STA $76 ; | Set pose LDA #$0D ; | STA $13E0 ;/ LDA #$02 STA !WallCling .ClingToWall LDA $D1 ;\ STA $94 ; | LDA $D2 ; | STA $95 ; | Don't update position for next frame LDA $D3 ; | STA $96 ; | LDA $D4 ; | STA $97 ;/ STZ $140D ; Clear spinjump RTS .Return STZ !WallCling RTS ;===================; ;LOCK JOYPAD ROUTINE; ;===================; LOCK_JOYPAD: LDA !LockTimer ;\ BEQ .ClearLock ; | DEC !LockTimer ; | Decrement timer and clear flags BRA .UpdateJoypad ; | .ClearLock STZ !JoypadLock ; | .UpdateJoypad LDA !JoypadLock ;/ BEQ .NoLock TRB $15 ;\ Lock joypad TRB $16 ;/ STZ !WallCling ; Don't cling to wall .NoLock LDA $0DA8,x ;\ Overwritten code STA $18 ;/ RTL ;====================; ;ACORN SPRITE ROUTINE; ;====================; if !AcornSprite pushpc org $00816A ; Start of SMW's NMI routine autoclean JML DYNAMIC_ACORN ; Hijack this org $01C6CE ; Powerup GFX routine NOP ; Clean up garbage autoclean JSL FEATHER_GFX ; Hijack this org $01C701 ; Feather sprite main routine NOP ; Clean up garbage autoclean JML HANDLE_FEATHER ; Hijack this org $01C73B ; Feather physics routine NOP ;\ Clean up garbage NOP ;/ autoclean JSL FEATHER_PHYSICS ; Hijack this pullpc DYNAMIC_ACORN: SEI ;\ PHP ; | REP #$30 ; | PHA ; | PHX ; | Set up NMI routine PHY ; | PHB ; | SEP #$30 ; | LDA #$00 ; | PHA ;/ LDA $0100 CMP #$07 BEQ .Upload CMP #$14 BNE .Return .Upload REP #$20 ; A 16 bit LDA !AcornTimer LSR A LSR A AND #$0007 ASL #6 CLC ADC #Acorn_GFX PHA LDY #$80 ;\ Word writes STY $2115 ;/ LDA #$1801 ;\ Register 2118, two registers write once alternate STA $4300 ;/ ; FIRST LINE LDA #$60E0 ;\ Destination VRAM STA $2116 ;/ PLA : PHA ;\ Location of GFX within bank STA $4302 ;/ LDY #Acorn_GFX>>16 ;\ Source bank STY $4304 ;/ LDA #$0040 ;\ Amount of bytes to transfer STA $4305 ;/ LDY #$01 ;\ Activate DMA STY $420B ;/ ; SECOND LINE LDA #$61E0 ;\ Destination VRAM STA $2116 ;/ PLA : CLC : ADC #$0200 ;\ Location of GFX within bank STA $4302 ;/ LDY #Acorn_GFX>>16 ;\ Source bank STY $4304 ;/ LDA #$0040 ;\ Amount of bytes to transfer STA $4305 ;/ LDY #$01 ;\ Activate DMA STY $420B ;/ .Return PLB ;\ Return to NMI routine JML $008174 ;/ FEATHER_GFX: STA $0303,y LDA $9E,x CMP #$77 BNE .Return PHA LDA $154C,x BEQ + LDA $0303,y ;\ AND #$CF ; | Clear priority STA $0303,y ;/ + LDA $0303,y ;\ AND #$7F ; | Clear xflip STA $0303,y ;/ PLA .Return RTL HANDLE_FEATHER: LDA #$01 STA $157C,x ; Make feather face the right way LDA $14C8,x CMP #$0C BEQ .CODE_01C744 LDA $154C,x BEQ .HandleSpeed LDA #$FA STA $AA,x LDA #$10 ;\ How long the thing will "roll" STA $1540,x : STA !AcornTimer ;/ .CODE_01C70D JML $01C70D .HandleSpeed LDA $1588,x AND #$04 BEQ + LDA #$10 ; Falling Yspeed STA $AA,x + LDA $1588,x AND #$03 BEQ + STZ $1540,x + LDA #$10 STA $B6,x LDA $1540,x BNE .CODE_01C738 STZ $B6,x .CODE_01C738 JML $01C738 .CODE_01C744 JML $01C744 FEATHER_PHYSICS: LDA $14C8,x STA $0DBF CMP #$0C BNE .Default .Falling LDA #$01 ;\ Enable blinking STA $1534,x ;/ LDA #$08 STA $AA,x PLA ; Get bank byte off the stack (so RTS will work) JML $01C371 ; Use mushroom physics .Default JML $01802A ; Use default physics routine print "Feather sprites will act and look like NSMBU Acorns." print "Acorn GFX inserted at $", hex(Acorn_GFX), "." else pushpc if read1($01C6CE) == $99 print "The powerup GFX routine has not been modified." else org $01C6CE STA $0303,y ;\ Original code from all.log LDA $9E,x ;/ print "Repaired 5 bytes of the powerup GFX routine at $01C6CE." endif if read1($01C701) == $BD print "The feather sprite routine has not been modified." else org $01C701 LDA $14C8,x ;\ Original code from all.log CMP #$0C ;/ print "Repaired 5 bytes of the feather sprite routine at $01C701." endif if read1($01C73B) == $20 print "The feather physics routine has not been modified." else org $01C73B JSR $ABCC ;\ Original code from all.log JSR $ABD8 ;/ print "Repaired 6 bytes of the feather physics routine at $01C73B." endif pullpc endif ;===============; ;NEW GFX ROUTINE; ;===============; if !NewGFX pushpc org $00A32D ; Player GFX DMA routine NOP ;\ Clean up garbage NOP ;/ autoclean JSL NEW_GFX ; Hijack this pullpc NEW_GFX: PHP ; I'm positive A is 16-bit at the point of the hijack SEP #$20 LDA $19 CMP #$02 ;;;;;;;;;;;;;;;;;;;;;;;; BRA .OrgCode ;; THIS IS NOT FINISHED YET!! ;;;;;;;;;;;;;;;;;;;;;;;; BNE .OrgCode PLP ; > Restore processor flags PLA : PLY ; > Get RTL address off the stack LDA #$1801 ;\ Register 2118, transfer mode 001 STA $4320 ;/ LDA #GFX_Source ;\ Location of GFX within bank STA $4322 ;/ LDY #GFX_Source>>16 ;\ Source bank of GFX STY $4324 ;/ .OrgCode PLP LDA #$1801 ;\ Overwritten code STA $4320 ;/ RTL print "Player GFX inserted at $", hex(GFX_Source), "." else pushpc if read1($00A32D) == $A9 print "The player GFX DMA routine has not been modified." else org $00A32D LDA #$1801 ;\ Original code from all.log STA $4320 ;/ print "Repaired 6 bytes of the player GFX DMA routine at $00A32D." endif pullpc endif freedata ;===============; ;TABLES AND DATA; ;===============; CapeImg: db $03,$04,$05,$06 ; Needed if you don't use the player GFX patch XSpeed: db !Xleft,!Xright XSpeedWall: db !WallLeft,!WallRight Acorn_GFX: if !AcornSprite incbin "Acorn.bin" endif GFX_Source: if !NewGFX incbin "GFX32.bin" endif print bytes, " bytes where inserted." if !AcornSprite print "Note that the GFX make the insert size significanly larger." else if !NewGFX print "Note that the GFX make the insert size significanly larger." endif endif print " "