ApplyMovement Function

󰃭 2025-03-29

CFF978 is the function that handles applying enemy movement to the enemies X/Y coordinates.

ApplyMovement:
; Initialize loop, we're looping through all 8 enemies (0-7)
CFF978    LDX #$0007
CFF97B    STX $C6

; Start of loop - Checking movement flag and movement timer
; The movement flag gets set when an enemy needs to move
; The movement timer limits how often the enemy moves.
CFF97D    LDX $C6     ; The currently active enemy
CFF97F    LDA $98A7,X ; Load the Xth enemy's active movement flag
CFF982    BEQ $CFF9CE ; If the enemy isn't actively moving, skip
CFF984    DEC $9819,X ; Decrement the Xth enemy's movement timer
CFF987    BNE $CFF9CE ; If it's not 0 skip

; Reset the movement timer to the max value
CFF989    LDA $9742,X ; Load the max value of the movement timer
CFF98C    STA $9819,X ; Store it to the current movement timer

; Movement between A and B is broken down into 8 smaller steps
CFF98F    CLC
CFF990    LDA $9737,X ; This is a counter keeping track of how far into the movement we are
CFF993    ADC $98C2,X ; Add the movement speed to the counter
CFF996    STA $9737,X ; And store it back
CFF999    STA $00     ; Keep a temporary copy in $00, this is read by the trig function later
CFF99B    CMP #$08    ; If it's not 8 then...
CFF99D    BNE $CFF9A5 ; Skip the next two lines, otherwise
CFF99F    STZ $98A7,X ; Zero out the movement flag
CFF9A2    STZ $9737,X ; Zero out the counter

; Calculate Y movement
CFF9A5    LDA $9786,X ; Load the enemies movement angle
CFF9A8    JSR $F9FB   ; This uses a trig lookup table to calculate movement
CFF9AB    STA $C8     ; The output, which is Y movement, is stored in $C8

;Calculate X Movement
CFF9AD    LDX $C6     ; Reload current enemy
CFF9AF    CLC
CFF9B0    LDA $9786,X ; Reload movement angle
CFF9B3    ADC #$40    ; Add 0x40 (64, or 90 degrees) 
CFF9B5    JSR $F9FB   ; Calculate movement
CFF9B8    STA $C9     ; Store the output, X movement, into $C9

; Apply the calcuated movement
CFF9BA    LDX $C6     ; Load the enemy one more time
CFF9BC    CLC
CFF9BD    LDA $98B7,X ; Load current Y Coord
CFF9C0    ADC $C8     ; Add calculated value
CFF9C2    STA $1D26,X ; Store updated Y Coord
CFF9C5    CLC
CFF9C6    LDA $98AF,X ; Load current X Coord
CFF9C9    ADC $C9     ; Add calculated value
CFF9CB    STA $1D0F,X ; Store updated X Coord

; Decrement the loop counter, if it's still positive continue looping, otherwise we're done
CFF9CE    DEC $C6
CFF9D0    BPL $CFF97D
CFF9D2    RTS

The details of partial movement aren’t completely understood by me, but the gist is that the counter ($9737) is used to keep track of partial pixel movement (this is probably what the copy stored in $00 and referenced by $F9FB)

The code for $F9FB can be seen here
CFF9FB    REP #$20
CFF9FD    ASL
CFF9FE    ASL
CFF9FF    AND #$03FF
CFFA02    TAX
CFFA03    LDA $C0F900,X
CFFA07    AND #$00FF
CFFA0A    CPX #$0200
CFFA0D    BCC $CFFA13
CFFA0F    EOR #$FFFF
CFFA12    INC
CFFA13    STA $02
CFFA15    SEP #$20
CFFA17    PHB
CFFA18    TDC
CFFA19    PHA
CFFA1A    PLB
CFFA1B    LDA $00
CFFA1D    STA $4202
CFFA20    LDA $02
CFFA22    STA $4203
CFFA25    PHP
CFFA26    LDA $03
CFFA28    STZ $06
CFFA2A    LDX $4216
CFFA2D    STA $4203
CFFA30    STX $04
CFFA32    REP #$21
CFFA34    LDA $05
CFFA36    ADC $4216
CFFA39    STA $05
CFFA3B    TDC
CFFA3C    PLP
CFFA3D    SEP #$20
CFFA3F    PLB
CFFA40    LDA $05
CFFA42    RTS

The effect of this is that enemies move in 8 step increments, if an enemy is moving from 0->8 then each increment they will move 1 pixel. If an enemy is moving from 0->4 then every other increment they will move 1 pixel. This has been observed and is accurate, the details of where the enemy’s X/Y positions will be on each individual frame are not completely understood, but it seems that once we’ve reached this point in movement the movement is set and no other actions will occur until movement is finished.1

The maximum value of the movement timer is loaded from the enemies sprite data. For Blue Imps it is loaded from E4F681 and the value is 0x21. That value is shifted right 4 times then stored to 7E9742 + Y (Y being the enemy number being processed).

0x21 >> 4 = 1, so every other frame the Blue Imp moves.

In observation the sprite of the Blue Imp seems to move every frame despite the actual X/Y coordinate only being updated every other frame, the sprite value is likely related to the number of frames is takes for the sprite’s movement animation to play although further observations are needed.


  1. At this point this is what I’m hoping, but it’s possible that I’m completely wrong and will eventually have to flesh this out some more. ↩︎