Archipelago/worlds/mm2/src/mm2_basepatch.asm

861 lines
14 KiB
NASM

norom
!headersize = 16
!controller_mirror = $23
!controller_flip = $27 ; only on first frame of input, used by crash man, etc
!current_stage = $2A
!received_stages = $8A
!completed_stages = $8B
!received_item_checks = $8C
!last_wily = $8D
!deathlink = $8F
!energylink_packet = $90
!rbm_strobe = $91
!received_weapons = $9A
!received_items = $9B
!current_weapon = $A9
!stage_completion = $0F70
!consumable_checks = $0F80
!CONTROLLER_SELECT = #$04
!CONTROLLER_SELECT_START = #$0C
!CONTROLLER_ALL_BUTTON = #$0F
!PpuControl_2000 = $2000
!PpuMask_2001 = $2001
!PpuAddr_2006 = $2006
!PpuData_2007 = $2007
!LOAD_BANK = $C000
macro org(address,bank)
if <bank> == $0F
org <address>-$C000+($4000*<bank>)+!headersize ; org sets the position in the output file to write to (in norom, at least)
base <address> ; base sets the position that all labels are relative to - this is necessary so labels will still start from $8000, instead of $0000 or somewhere
else
org <address>-$8000+($4000*<bank>)+!headersize
base <address>
endif
endmacro
%org($8400, $08)
incbin "mm2font.dat"
%org($A900, $09)
incbin "mm2titlefont.dat"
%org($807E, $0B)
FlashFixes:
CMP #$FF
BEQ FlashFixTarget1
CMP #$FF
BNE FlashFixTarget2
%org($8086, $0B)
FlashFixTarget1:
%org($808D, $0B)
FlashFixTarget2:
%org($8015, $0D)
ClearRefreshHook:
; if we're already doing a fresh load of the stage select
; we don't need to immediately refresh it
JSR ClearRefresh
NOP
%org($802B, $0D)
PatchFaceTiles:
LDA !received_stages
%org($8072, $0D)
PatchFaceSprites:
LDA !received_stages
%org($80CC, $0D)
CheckItemsForWily:
LDA !received_items
CMP #$07
%org($80D2, $0D)
LoadWily:
JSR GoToMostRecentWily
NOP
%org($80DC, $0D)
CheckAccessCodes:
LDA !received_stages
%org($8312, $0D)
HookStageSelect:
JSR RefreshRBMTiles
NOP
%org($A315, $0D)
RemoveWeaponClear:
NOP
NOP
NOP
NOP
;Adjust Password select flasher
%org($A32A, $0D)
LDX #$68
;Block password input
%org($A346, $0D)
EOR #$00
;Remove password text
%org($AF3A, $0D)
StartHeight:
db $AC ; set Start to center
%org($AF49, $0D)
PasswordText:
db $40, $40, $40, $40, $40, $40, $40, $40
%org($AF6C, $0D)
ContinueHeight:
db $AB ; split height between 2 remaining options
%org($AF77, $0D)
StageSelectHeight:
db $EB ; split between 2 remaining options
%org($AF88, $0D)
GameOverPasswordText:
db $40, $40, $40, $40, $40, $40, $40, $40
%org($AFA5, $0D)
GetEquippedPasswordText:
db $40, $40, $40, $40, $40, $40, $40, $40
%org($AFAE, $0D)
GetEquippedStageSelect:
db $26, $EA
%org($B195, $0D)
GameOverPasswordUp:
LDA #$01 ; originally 02, removing last option
%org($B19F, $0D)
GameOverPassword:
CMP #$02 ; originally 03, remove the last option
%org($B1ED, $0D)
FixupGameOverArrows:
db $68, $78
%org($BB74, $0D)
GetEquippedStage:
JSR StageGetEquipped
NOP #13
%org($BBD9, $0D)
GetEquippedDefault:
LDA #$01
%org($BC01, $0D)
GetEquippedPasswordRemove:
ORA #$01 ; originally EOR #$01, we always want 1 here
%org($BCF1, $0D)
GetEquippedItem:
ADC #$07
JSR ItemGetEquipped
JSR LoadItemsColor
NOP ; !!!! This is a load-bearing NOP. It gets branched to later in the function
LDX $FF
%org($BB08, $0D)
WilyProgress:
JSR StoreWilyProgress
NOP
%org($BF6F, $0D)
GetEquippedStageSelectHeight:
db $B8
%org($805B, $0E)
InitalizeStartingRBM:
LDA #$FF ; this does two things
STA !received_stages ; we're overwriting clearing e-tanks and setting RBM available to none
%org($8066, $0E)
BlockStartupAutoWily:
; presumably this would be called from password?
LDA #$00
%org($80A7, $0E)
StageLoad:
JMP CleanWily5
NOP
%org($8178, $0E)
Main1:
JSR MainLoopHook
NOP
%org($81DE, $0E)
Wily5Teleporter:
LDA $99
CMP #$01
BCC SkipSpawn
%org($81F9, $0E)
SkipSpawn:
; just present to fix the branch, if we try to branch raw it'll get confused
%org($822D, $0E)
Main2:
; believe used in the wily 5 refights?
JSR MainLoopHook
NOP
%org($842F, $0E)
Wily5Hook:
JMP Wily5Requirement
NOP
%org($C10D, $0F)
Deathlink:
JSR KillMegaMan
%org($C1BC, $0F)
RemoveETankLoss:
NOP
NOP
%org($C23C, $0F)
WriteStageComplete:
ORA !completed_stages
STA !completed_stages
%org($C243, $0F)
WriteReceiveItem:
ORA !received_item_checks
STA !received_item_checks
%org($C254, $0F)
BlockAutoWily:
; and this one is on return from stage?
LDA #$00
%org($C261, $0F)
WilyStageCompletion:
JSR StoreWilyStageCompletion
NOP
%org($E5AC, $0F)
NullDeathlink:
STA $8F ; we null his HP later in the process
NOP
%org($E5D1, $0F)
EnergylinkHook:
JSR Energylink
NOP #2 ; comment this out to enable item giving their usual reward alongside EL
%org($E5E8, $0F)
ConsumableHook:
JSR CheckConsumable
%org($F2E3, $0F)
CheckConsumable:
STA $0140, Y
TXA
PHA
LDA $AD ; the consumable value
CMP #$7C
BPL .Store
print "Consumables (replace 7a): ", hex(realbase())
CMP #$76
BMI .Store
LDA #$00
.Store:
STA $AD
LDA $2A
ASL
ASL
TAX
TYA
.LoopHead:
CMP #$08
BMI .GetFlag
INX
SBC #$08
BNE .LoopHead
.GetFlag:
TAY
LDA #$01
.Loop2Head:
CPY #$00
BEQ .Apply
ASL
DEY
BNE .Loop2Head
.Apply:
ORA !consumable_checks, X
STA !consumable_checks, X
PLA
TAX
RTS
GoToMostRecentWily:
LDA !controller_mirror
CMP !CONTROLLER_SELECT_START
BEQ .Default
LDA !last_wily
BNE .Store
.Default:
LDA #$08 ; wily stage 1
.Store:
STA !current_stage
RTS
StoreWilyStageCompletion:
LDA #$01
STA !stage_completion, X
INC !current_stage
LDA !current_stage
STA !last_wily
RTS
ReturnToGameOver:
LDA #$10
STA !PpuControl_2000
LDA #$06
STA !PpuMask_2001
JMP $C1BE ; specific code that loads game over
MainLoopHook:
LDA !controller_mirror
CMP !CONTROLLER_ALL_BUTTON
BNE .Next
JMP ReturnToGameOver
.Next:
LDA !deathlink
CMP #$01
BNE .Next2
JMP $E5A8 ; this kills the Mega Man
.Next2:
print "Quickswap:", hex(realbase())
LDA #$00 ; slot data, write in enable for quickswap
CMP #$01
BNE .Finally
LDA !controller_flip
AND !CONTROLLER_SELECT
BEQ .Finally
JMP Quickswap
.Finally:
LDA !controller_flip
AND #$08 ; this is checking for menu
RTS
StoreWilyProgress:
STA !current_stage
TXA
PHA
LDX !current_stage
LDA #$01
STA !stage_completion, X
PLA
TAX
print "Get Equipped Music: ", hex(realbase())
LDA #$17
RTS
KillMegaMan:
JSR $C051 ; this kills the mega man
LDA #$00
STA $06C0 ; set HP to zero so client can actually detect he died
RTS
Wily5Requirement:
LDA #$01
LDX #$08
LDY #$00
.LoopHead:
BIT $BC
BEQ .Skip
INY
.Skip:
DEX
ASL
CPX #$00
BNE .LoopHead
print "Wily 5 Requirement:", hex(realbase())
CPY #$08
BCS .SpawnTeleporter
JMP $8450
.SpawnTeleporter:
LDA #$FF
STA $BC
LDA #$01
STA $99
JMP $8433
CleanWily5:
LDA #$00
STA $BC
STA $99
JMP $80AB
LoadString:
STY $00
ASL
ASL
ASL
ASL
TAY
LDA $DB
ADC #$00
STA $C8
LDA #$40
STA $C9
LDA #$F6
CLC
ADC $C8
STA $CA
LDA ($C9), Y
STA $03B6
TYA
CLC
ADC #$01
TAY
LDA $CA
ADC #$00
STA $CA
LDA ($C9), Y
STA $03B7
TYA
CLC
ADC #$01
TAY
LDA $CA
ADC #$00
STA $CA
STY $FE
LDA #$0E
STA $FD
.LoopHead:
JSR $BD34
LDY $FE
CPY #$40
BNE .NotEqual
LDA $0420
BNE .Skip
.NotEqual:
LDA ($C9), Y
.Skip:
STA $03B8
INC $47
INC $03B7
LDA $FE
CLC
ADC #$01
STA $FE
LDA $CA
ADC #$00
STA $CA
DEC $FD
BNE .LoopHead
LDY $00
JSR $C0AB
RTS
StageGetEquipped:
LDA !current_stage
LDX #$00
BCS LoadGetEquipped
ItemGetEquipped:
LDX #$02
LoadGetEquipped:
STX $DB
ASL
ASL
PHA
SEC
JSR LoadString
PLA
ADC #$00
PHA
SEC
JSR LoadString
PLA
ADC #$00
PHA
SEC
JSR LoadString
LDA #$00
SEC
JSR $BD3E
PLA
ADC #$00
SEC
JSR LoadString
RTS
LoadItemsColor:
LDA #$7D
STA $FD
LDA $0420
AND #$0F
ASL
SEC
ADC #$1A
STA $FF
RTS
Energylink:
LSR $0420, X
print "Energylink: ", hex(realbase())
LDA #$00
BEQ .ApplyDrop
LDA $04E0, X
BEQ .ApplyDrop ; This is a stage pickup, and not an enemy drop
STY !energylink_packet
SEC
BCS .Return
.ApplyDrop:
STY $AD
.Return:
RTS
Quickswap:
LDX #$0F
.LoopHead:
LDA $0420, X
BMI .Return1 ; return if we have any weapon entities spawned
DEX
CPX #$01
BNE .LoopHead
LDX !current_weapon
BNE .DoQuickswap
LDX #$00
.DoQuickswap:
TYA
PHA
LDX !current_weapon
INX
CPX #$09
BPL .Items
LDA #$01
.Loop2Head:
DEX
BEQ .FoundTarget
ASL
CPX #$00
BNE .Loop2Head
.FoundTarget:
LDX !current_weapon
INX
.Loop3Head:
PHA
AND !received_weapons
BNE .CanSwap
PLA
INX
CPX #$09
BPL .Items
ASL
BNE .Loop3Head
.CanSwap:
PLA
SEC
BCS .ApplySwap
.Items:
TXA
PHA
SEC
SBC #$08
TAX
LDA #$01
.Loop4Head:
DEX
BEQ .CheckItem
ASL
CPX #$00
BNE .Loop4Head
.CheckItem:
TAY
PLA
TAX
TYA
.Loop5Head:
PHA
AND !received_items
BNE .CanSwap
PLA
INX
ASL
BNE .Loop5Head
LDX #$00
SEC
BCS .ApplySwap
.Return1:
RTS
.ApplySwap: ; $F408 on old rom
LDA #$0D
JSR !LOAD_BANK
; this is a bunch of boiler plate to make the swap work
LDA $B5
PHA
LDA $B6
PHA
LDA $B7
PHA
LDA $B8
PHA
LDA $B9
PHA
LDA $20
PHA
LDA $1F
PHA
;but wait, there's more
STX !current_weapon
JSR $CC6C
LDA $1A
PHA
LDX #$00
.Loop6Head:
STX $FD
CLC
LDA $52
ADC $957F, X
STA $08
LDA $53
ADC #$00
STA $09
LDA $08
LSR $09
ROR
LSR $09
ROR
STA $08
AND #$3F
STA $1A
CLC
LDA $09
ADC #$85
STA $09
LDA #$00
STA $1B
LDA $FD
CMP #$08
BCS .Past8
LDX $A9
LDA $9664, X
TAY
CPX #$09
BCC .LessThanNine
LDX #$00
BEQ .Apply
.LessThanNine:
LDX #$05
BNE .Apply
.Past8:
LDY #$90
LDX #$00
.Apply:
JSR $C760
JSR $C0AB ; iirc this is loading graphics?
LDX $FD
INX
CPX #$0F
BNE .Loop6Head
STX $FD
LDY #$90
LDX #$00
JSR $C760
JSR $D2ED
; two sections redacted here, might need to look at what they actually do?
PLA
STA $1A
PLA
STA $1F
PLA
STA $20
PLA
STA $B9
PLA
STA $B8
PLA
STA $B7
PLA
STA $B6
PLA
STA $B5
LDA #$00
STA $AC
STA $2C
STA $0680
STA $06A0
LDA #$1A
STA $0400
LDA #$03
STA $AA
LDA #$30
JSR $C051
.Finally:
LDA #$0E
JSR !LOAD_BANK
PLA
TAY
.Return:
RTS
RefreshRBMTiles:
; primarily just a copy of the startup RBM setup, we just do it again
; can't jump to it as it leads into the main loop
LDA !rbm_strobe
BNE .Update
JMP .NoUpdate
.Update:
LDA #$00
STA !rbm_strobe
LDA #$10
STA $F7
STA !PpuControl_2000
LDA #$06
STA $F8
STA !PpuMask_2001
JSR $847E
JSR $843C
LDX #$00
LDA $8A
STA $01
.TileLoop:
STX $00
LSR $01
BCC .SkipTile
LDA $8531,X
STA $09
LDA $8539,X
STA $08
LDX #$04
LDA #$00
.ClearBody:
LDA $09
STA !PpuAddr_2006
LDA $08
STA !PpuAddr_2006
LDY #$04
LDA #$00
.ClearLine:
STA !PpuData_2007
DEY
BNE .ClearLine
CLC
LDA $08
ADC #$20
STA $08
DEX
BNE .ClearBody
.SkipTile:
LDX $00
INX
CPX #$08
BNE .TileLoop
LDX #$1F
JSR $829E
JSR $8473
LDX #$00
LDA $8A
STA $02
LDY #$00
.SpriteLoop:
STX $01
LSR $02
BCS .SkipRBM
LDA $8605,X
STA $00
LDA $85FD,X
TAX
.WriteSprite:
LDA $8541,X
STA $0200,Y
INY
INX
DEC $00
BNE .WriteSprite
.SkipRBM:
LDX $01
INX
CPX #$08
BNE .SpriteLoop
JSR $A51D
LDA #$0C
JSR $C051
LDA #$00
STA $2A
STA $FD
JSR $C0AB
.NoUpdate:
LDA $1C
AND #$08
RTS
ClearRefresh:
LDA #$00
STA !rbm_strobe
LDA #$10
STA $F7
RTS
assert realbase() <= $03F650 ; This is the start of our text data, and we absolutely cannot go past this point (text takes too much room).
%org($F640, $0F)
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
db $25, $4B, "PLACEHOLDER_L1"
db $25, $6B, "PLACEHOLDER_L2"
db $25, $8B, "PLACEHOLDER_L3"
db $25, $EB, "PLACEHOLDER_PL"
%org($FFB0, $0F)
db "MM2_BASEPATCH_ARCHI "