Created
August 16, 2023 18:50
-
-
Save yuriks/896dc682211c8cf12a9ba466c6472d93 to your computer and use it in GitHub Desktop.
Bank $A8 - Morph Ball eye disassembly
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
asar 1.90 | |
lorom | |
dpbase 0 | |
optimize dp always | |
optimize address ram | |
;;; RAM Variables | |
samus_collected_items = $7E09A4 | |
samus_pos_x = $7E0AF6 | |
samus_pos_x_sub = $7E0AF8 | |
samus_pos_y = $7E0AFA | |
samus_pos_y_sub = $7E0AFC | |
enemy_index = $7E0E54 | |
enemies_pos_x = $7E0F7A | |
enemies_pos_x_sub = $7E0F7C | |
enemies_pos_y = $7E0F7E | |
enemies_pos_y_sub = $7E0F80 | |
enemies_flags1 = $7E0F86 | |
enemies_flags2 = $7E0F88 | |
enemies_spritemap = $7E0F8E | |
enemies_timer = $7E0F90 | |
enemies_instruction_list = $7E0F92 | |
enemies_instruction_timer = $7E0F94 | |
enemies_ai_var0 = $7E0FA8 | |
enemies_ai_var1 = $7E0FAA | |
enemies_ai_var2 = $7E0FAC | |
enemies_ai_var3 = $7E0FAE | |
enemies_ai_var4 = $7E0FB0 | |
enemies_ai_var5 = $7E0FB2 | |
enemies_parameter1 = $7E0FB4 | |
enemies_parameter2 = $7E0FB6 | |
ram_hdma_buffer = $7E9100 | |
;;; Common enemy bank routines/constants | |
!enemy_empty_spritemap = $804D | |
;;; External routines | |
snd_playLib2Sfx = $8090CB ; $90CB: Queue sound, sound library 2, max queued sounds allowed = 6 | |
hdma_spawnEyeHdma = $88E8D9 ; $E8D9: Spawn morph ball eye beam HDMA object | |
ai_util_isSamusWithinYRadius = $A0AEED ; $AEED: Is Samus within [A] pixel rows of enemy | |
ai_util_isSamusWithinXRadius = $A0AF0B ; $AF0B: Is Samus within [A] pixel columns of enemy | |
ai_util_atan2 = $A0C0AE ; $C0AE: Calculate angle of ([$12], [$14]) offset | |
;;; Variable aliases | |
; Read by beam HDMA object, TODO purpose unknown | |
eye_vars_hdma_flag = enemies_ai_var2 | |
; Read by beam HDMA object | |
eye_vars_angle = enemies_ai_var3 | |
; Used to add a delay during the opening/closing AI states | |
eye_vars_anim_delay = enemies_ai_var4 | |
; Pointer to current AI state handler, called from pre-instruction | |
eye_vars_state = enemies_ai_var5 | |
org $A88F8C | |
;;; $8F8C: Palette - enemy $E6BF (morph ball eye) ;;; | |
skip $20 ; Let SMART manage this | |
;dw 3800, 72B2, 71C7, 2461, 1840, 7A8E, 660B, 4D03, 4900, 7FE0, 7E80, 44E0, 2C20, 0000, 0000, 0000 | |
;;; $8FAC: Instruction list - ;;; | |
{ | |
instrs_eye_directions: | |
dw $000A,$9210 | |
dw $000A,$9210 | |
dw $000A,$9217 | |
dw $000A,$921E | |
dw $000A,$9225 | |
dw $000A,$922C | |
dw $000A,$9233 | |
dw $000A,$923A | |
dw $000A,$9209 | |
dw $000A,$9202 | |
dw $000A,$91FB | |
dw $000A,$91F4 | |
dw $000A,$91ED | |
dw $000A,$91E6 | |
dw $000A,$91DF | |
dw $000A,$91DF | |
dw $80ED,instrs_eye_directions ; Go to | |
} | |
;;; $8FF0: Instruction list - ;;; | |
{ | |
instrs_eye_left_closing: | |
dw $0008,$9257 | |
dw $0030,$91F4 | |
dw $0005,$9257 | |
.closed: | |
dw $0030,$9241 | |
dw $812F ; Sleep | |
} | |
;;; $9002: Instruction list - ;;; | |
{ | |
instrs_eye_right_closing: | |
dw $0008,$9283 | |
dw $0030,$9225 | |
dw $0005,$9283 | |
.closed: | |
dw $0030,$926D | |
dw $812F ; Sleep | |
} | |
;;; $9014: Instruction list - ;;; | |
{ | |
instrs_eye_left_opening: | |
dw $0020,$9241 | |
dw $0005,$9257 | |
dw $0030,$91F4 | |
dw $0008,$9257 | |
dw $812F ; Sleep | |
} | |
;;; $9026: Instruction list - ;;; | |
{ | |
instrs_eye_right_opening: | |
dw $0020,$926D | |
dw $0005,$9283 | |
dw $0030,$9225 | |
dw $0008,$9283 | |
dw $812F ; Sleep | |
} | |
;;; $9038: Instruction list - ;;; | |
{ | |
instrs_mount_right: | |
dw $0001,$9299 | |
dw $812F ; Sleep | |
} | |
;;; $903E: Instruction list - ;;; | |
{ | |
instrs_mount_down: | |
dw $0001,$92A5 | |
dw $812F ; Sleep | |
} | |
;;; $9044: Instruction list - ;;; | |
{ | |
instrs_mount_left: | |
dw $0001,$92B1 | |
dw $812F ; Sleep | |
} | |
;;; $904A: Instruction list - ;;; | |
{ | |
instrs_mount_up: | |
dw $0001,$92BD | |
dw $812F ; Sleep | |
} | |
;;; $9050: Morph ball eye constants ;;; | |
{ | |
; How close Samus needs to be for the eye to wake up and start tracking her | |
eye_acquire_xradius: | |
dw $80 | |
; How close Samus needs to be for the eye to continue tracking once it's already awake | |
eye_track_xradius: | |
dw $B0 | |
eye_acquire_yradius: | |
dw $80 | |
eye_track_yradius: | |
dw $80 | |
} | |
;;; $9058: Initialisation AI - enemy $E6BF (morph ball eye) ;;; | |
{ | |
eye_ai_init: | |
LDX enemy_index | |
; Set "Process Instructions" flag | |
LDA enemies_flags1,x | |
ORA #$2000 | |
STA enemies_flags1,x | |
LDA #!enemy_empty_spritemap | |
STA enemies_spritemap,x | |
LDA.w #1 | |
STA enemies_instruction_timer,x | |
STZ enemies_timer,x | |
; If (param2 & 8000h != 0) this is the mount part, initialize mount graphics | |
LDA enemies_parameter2,x | |
BMI .init_mount | |
; Otherwise, this is the eye part, continue with eye initialization | |
LDA #eye_state_waiting | |
STA eye_vars_state,x | |
; param1 & 0x0001 determines eye facing: 0 = Left, 1 = Right | |
LDA enemies_parameter1,x | |
BIT #$0001 | |
BEQ .facing_left | |
; Right-facing | |
LDA #instrs_eye_right_closing_closed | |
STA enemies_instruction_list,x | |
BRA .return | |
.facing_left: | |
; Left-facing | |
LDA #instrs_eye_left_closing_closed | |
STA enemies_instruction_list,x | |
BRA .return | |
.init_mount: | |
; Depending on the mount orientation, offset origin and set sprite accordingly | |
; Y = table offset for configured orientation ((param2 & Fh) * 2) | |
AND #$000F | |
ASL | |
TAY | |
; Offset X position | |
LDA enemies_pos_x,x | |
CLC | |
ADC .x_offsets_tbl,y | |
STA enemies_pos_x,x | |
; Offset Y position | |
LDA enemies_pos_y,x | |
CLC | |
ADC .y_offsets_tbl,y | |
STA enemies_pos_y,x | |
; Mount uses a null AI state | |
LDA #rtl_91DC | |
STA eye_vars_state,x | |
; Set instruction list with correct sprites for this orientation | |
LDA .instruction_lists_tbl,y | |
STA enemies_instruction_list,x | |
; Fill $7E:9100..92FF ("some HDMA RAM"?) with 00FFh | |
LDX #$01FE | |
- LDA #$00FF | |
STA ram_hdma_buffer,x | |
DEX #2 | |
BPL - | |
.return: | |
RTL | |
; Left, Right, Up, Down | |
.x_offsets_tbl: | |
dw -8, 8, 0, 0 | |
.y_offsets_tbl: | |
dw 0, 0, -8, 8 | |
.instruction_lists_tbl: | |
dw instrs_mount_left, instrs_mount_right, instrs_mount_up, instrs_mount_down | |
} | |
;;; $90E2: Main AI - enemy $E6BF (morph ball eye) ;;; | |
{ | |
eye_ai_main: | |
LDX enemy_index | |
LDA samus_collected_items | |
BIT #$0004 ; Morph ball | |
BEQ .return | |
; If morph ball has been collected, run current AI state handler | |
JMP (eye_vars_state,x) | |
.return: | |
RTL | |
} | |
;;; $90F1: AI State: Eye closed, waiting for Samus to come nearby | |
{ | |
eye_state_waiting: | |
; Check if Samus is near the vicinity of the enemy and return otherwise | |
LDA eye_acquire_yradius | |
JSL ai_util_isSamusWithinYRadius | |
TAY | |
BEQ .return | |
LDA eye_acquire_xradius | |
JSL ai_util_isSamusWithinXRadius | |
TAY | |
BEQ .return | |
LDA.w #32 | |
STA eye_vars_anim_delay,x | |
LDA.w #1 | |
STA enemies_instruction_timer,x | |
; Check facing direction (param1 & 0x0001), 0 = Left, 1 = Right | |
LDA enemies_parameter1,x | |
BIT #$0001 | |
BEQ .facing_left | |
; Otherwise facing right | |
LDA #instrs_eye_right_opening | |
STA enemies_instruction_list,x | |
BRA .next_state | |
.facing_left: | |
LDA #instrs_eye_left_opening | |
STA enemies_instruction_list,x | |
.next_state: | |
LDA #eye_state_opening | |
STA eye_vars_state,x | |
.return: | |
RTL | |
} | |
;;; $912E: AI State: Eye spotted Samus and is playing opening animation | |
{ | |
eye_state_opening: | |
DEC eye_vars_anim_delay,x | |
BEQ .eye_opened | |
BPL .return | |
.eye_opened: | |
; Queue sound 17h, sound library 2, max queued sounds allowed = 6 (morph ball eye's ray) | |
LDA.w #$17 | |
JSL snd_playLib2Sfx | |
JSL hdma_spawnEyeHdma | |
LDA #eye_state_tracking | |
STA eye_vars_state,x | |
; Calculate angle from enemy to Samus | |
LDA samus_pos_x | |
SEC : SBC enemies_pos_x,x | |
STA $12 | |
LDA samus_pos_y | |
SEC : SBC enemies_pos_y,x | |
STA $14 | |
JSL ai_util_atan2 ; Takes $12,$14 as params | |
STA eye_vars_angle,x | |
.return: | |
RTL | |
} | |
;;; $9160: AI State: Eye is open and tracking Samus' direction | |
{ | |
eye_state_tracking: | |
; Check if Samus is still near the eye object | |
LDA eye_track_yradius | |
JSL ai_util_isSamusWithinYRadius | |
TAY | |
BEQ .samus_out_of_range | |
LDA eye_track_xradius | |
JSL ai_util_isSamusWithinXRadius | |
TAY | |
BNE .track_samus | |
.samus_out_of_range: | |
; Stop x-ray sound | |
; Queue sound 71h, sound library 2, max queued sounds allowed = 6 (silence) | |
LDA.w #$71 | |
JSL snd_playLib2Sfx | |
STZ eye_vars_hdma_flag,x | |
LDA.w #32 | |
STA eye_vars_anim_delay,x | |
; Play eye closing animation. The correct instruction list is set | |
; according to the eye direction. ((param1 & 1): 0=Left, 1=Right) | |
LDA enemies_parameter1,x | |
BIT #$0001 | |
BEQ .facing_left | |
LDA #instrs_eye_right_closing | |
STA enemies_instruction_list,x | |
BRA .next_state | |
.facing_left: | |
LDA #instrs_eye_left_closing | |
STA enemies_instruction_list,x | |
.next_state: | |
LDA #eye_state_closing | |
STA eye_vars_state,x | |
BRA .return | |
.track_samus: | |
; Update beam angle by calculating new angle towards samus | |
LDA samus_pos_x | |
SEC : SBC enemies_pos_x,x | |
STA $12 | |
LDA samus_pos_y | |
SEC : SBC enemies_pos_y,x | |
STA $14 | |
JSL ai_util_atan2 ; Takes $12,$14 as params | |
STA eye_vars_angle,x | |
; Update eye sprite to look in the correct direction. Uses the angle to | |
; index into the instruction list containing each of the poses. | |
; angle / 16 (num. of poses) * 4 (bytes per instruction in the list) | |
AND #$00F0 : LSR #2 | |
CLC : ADC #instrs_eye_directions | |
STA enemies_instruction_list,x | |
.return: | |
LDA #$0001 | |
STA enemies_instruction_timer,x | |
RTL | |
} | |
;;; $91CE: AI State: Eye lost track of Samus and is playing its closing animation before going back to waiting state | |
{ | |
eye_state_closing: | |
DEC eye_vars_anim_delay,x | |
BEQ .counter_expired | |
BPL .return | |
.counter_expired: | |
LDA #eye_state_waiting | |
STA eye_vars_state,x | |
.return: | |
RTL | |
} | |
;;; $91DC: RTL | |
{ | |
rtl_91DC: | |
RTL | |
} | |
;;; $91DD: RTL (unused?) | |
{ | |
rtl_91DD: | |
RTL | |
} | |
;;; $91DE: RTL (unused?) | |
{ | |
rtl_91DE: | |
RTL | |
} | |
warnpc $A891DF | |
;;; $91DF: Morph ball eye spritemaps ;;; | |
{ | |
macro sprmap_entry(xoff, yoff, tile) | |
dw <xoff> : db <yoff> : dw <tile> | |
endmacro | |
; Open eye looking towards various angles | |
eye_spritemaps: | |
.f0: dw 1 : %sprmap_entry($81F8, $F8, $2100) | |
.f1: dw 1 : %sprmap_entry($81F8, $F8, $2102) | |
.f2: dw 1 : %sprmap_entry($81F8, $F8, $2104) | |
.f3: dw 1 : %sprmap_entry($81F8, $F8, $2106) | |
.f4: dw 1 : %sprmap_entry($81F8, $F8, $2108) | |
.f5: dw 1 : %sprmap_entry($81F8, $F8, $210A) | |
.f6: dw 1 : %sprmap_entry($81F8, $F8, $210C) | |
.f7: dw 1 : %sprmap_entry($81F8, $F8, $6100) | |
.f8: dw 1 : %sprmap_entry($81F8, $F8, $6102) | |
.f9: dw 1 : %sprmap_entry($81F8, $F8, $6104) | |
.f10: dw 1 : %sprmap_entry($81F8, $F8, $6106) | |
.f11: dw 1 : %sprmap_entry($81F8, $F8, $6108) | |
.f12: dw 1 : %sprmap_entry($81F8, $F8, $610A) | |
.f13: dw 1 : %sprmap_entry($81F8, $F8, $610C) | |
; Eye opening left | |
.f14: dw 4 | |
%sprmap_entry($01F8, $00, $A11F) | |
%sprmap_entry($01F8, $F8, $211F) | |
%sprmap_entry($0000, $00, $2117) | |
%sprmap_entry($0000, $F8, $2107) | |
.f15: dw 4 | |
%sprmap_entry($01F8, $00, $A11E) | |
%sprmap_entry($01F8, $F8, $211E) | |
%sprmap_entry($0000, $00, $2117) | |
%sprmap_entry($0000, $F8, $2107) | |
; Eye opening right | |
.f16: dw 4 | |
%sprmap_entry($0000, $00, $E11F) | |
%sprmap_entry($0000, $F8, $611F) | |
%sprmap_entry($01F8, $00, $6117) | |
%sprmap_entry($01F8, $F8, $6107) | |
.f17: dw 4 | |
%sprmap_entry($0000, $00, $E11E) | |
%sprmap_entry($0000, $F8, $611E) | |
%sprmap_entry($01F8, $00, $6117) | |
%sprmap_entry($01F8, $F8, $6107) | |
; Mount in 4 directions | |
.f18: dw 2 | |
%sprmap_entry($01FC, $00, $A10E) | |
%sprmap_entry($01FC, $F8, $210E) | |
.f19: dw 2 | |
%sprmap_entry($0000, $FC, $610F) | |
%sprmap_entry($01F8, $FC, $210F) | |
.f20: dw 2 | |
%sprmap_entry($01FC, $00, $E10E) | |
%sprmap_entry($01FC, $F8, $610E) | |
.f21: dw 2 | |
%sprmap_entry($0000, $FC, $E10F) | |
%sprmap_entry($01F8, $FC, $A10F) | |
; Unused. Seem to be duplicates of 9241-9298 | |
.f22: dw 4 | |
%sprmap_entry($01F8, $00, $A11F) | |
%sprmap_entry($01F8, $F8, $211F) | |
%sprmap_entry($0000, $00, $2117) | |
%sprmap_entry($0000, $F8, $2107) | |
.f23: dw 4 | |
%sprmap_entry($01F8, $00, $A11E) | |
%sprmap_entry($01F8, $F8, $211E) | |
%sprmap_entry($0000, $00, $2117) | |
%sprmap_entry($0000, $F8, $2107) | |
.f24: dw 4 | |
%sprmap_entry($0000, $00, $E11F) | |
%sprmap_entry($0000, $F8, $611F) | |
%sprmap_entry($01F8, $00, $6117) | |
%sprmap_entry($01F8, $F8, $6107) | |
.f25: dw 4 | |
%sprmap_entry($0000, $00, $E11E) | |
%sprmap_entry($0000, $F8, $611E) | |
%sprmap_entry($01F8, $00, $6117) | |
%sprmap_entry($01F8, $F8, $6107) | |
.f26: dw 4 | |
%sprmap_entry($0000, $00, $E11F) | |
%sprmap_entry($0000, $F8, $611F) | |
%sprmap_entry($01F8, $00, $6117) | |
%sprmap_entry($01F8, $F8, $6107) | |
.f27: dw 4 | |
%sprmap_entry($0000, $00, $E11E) | |
%sprmap_entry($0000, $F8, $611E) | |
%sprmap_entry($01F8, $00, $6117) | |
%sprmap_entry($01F8, $F8, $6107) | |
.f28: dw 4 | |
%sprmap_entry($01F8, $00, $A11F) | |
%sprmap_entry($01F8, $F8, $211F) | |
%sprmap_entry($0000, $00, $2117) | |
%sprmap_entry($0000, $F8, $2107) | |
.f29: dw 4 | |
%sprmap_entry($01F8, $00, $A11E) | |
%sprmap_entry($01F8, $F8, $211E) | |
%sprmap_entry($0000, $00, $2117) | |
%sprmap_entry($0000, $F8, $2107) | |
} | |
} | |
; End of eye code | |
warnpc $A89379 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment