Code;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; SMW Pokey (sprite 70), credit by imamelia
;;
;; This is a disassembly of sprite 70 in SMW, the Pokey.
;;
;; Uses first extra bit: YES
;;
;; If the extra bit is clear, the sprite will be 5 or 3 segments tall depending on
;; whether or not the player has Yoshi. If the extra bit is set, the sprite will be 4
;; segments tall regardless. (This is the default, anyway. The actual values are
;; set by the !SegmentsX defines.)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; defines and tables
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
!Segments1 = $07 ; number of segments the Pokey will have if the extra bit is clear and the player is not on Yoshi
!Segments2 = $1F ; number of segments the Pokey will have if the extra bit is clear and the player is on Yoshi
!Segments3 = $0F ; number of segments the Pokey will have if the extra bit is set and the player is not on Yoshi
!Segments4 = $0F ; number of segments the Pokey will have if the extra bit is set and the player is on Yoshi
!HeadTile = $8A ;
!BodyTile = $E8 ;
Clipping: ; the sprite clipping value indexed by the number of sections Pokey has
db $1B,$1B,$1A,$19,$18,$17 ; 0, 1, 2, 3, 4, 5
BitTable1:
db $01,$02,$04,$08
BitTable2:
db $00,$01,$03,$07
BitTable3:
db $FF,$FE,$FC,$F8
BitTable4:
db $EF,$F7,$FB,$FD,$FE
BitTable5:
db $E0,$F0,$F8,$FC,$FE
XDisp:
db $00,$01,$00,$FF
XSpeed: ; X speeds for each direction (right, left)
db $02,$FE ; holy fecal matter, this sprite is slow
Data1:
db $00,$05,$09,$0C,$0E,$0F,$10,$10,$10,$10,$10,$10,$10
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; init routine
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
print "INIT ",pc
LDA $7FAB10,x ;
AND #$04 ; if the extra bit is set...
BNE Init2 ; use the other two values for the segment count
LDA #!Segments2 ; if the player is on Yoshi, then the Pokey has 5 segments ($C2,x = #$1F = #%00011111)
LDY $187A ;
BNE StoreSegments ;
LDA #!Segments1 ; if the player is not on Yoshi, then the Pokey has 3 segments ($C2,x = #$07 = #%00000111)
BRA StoreSegments ;
Init2: ;
LDA #!Segments4 ; if the player is on Yoshi, then the Pokey has 4 segments ($C2,x = #$0F = #%00001111)
LDY $187A ;
BNE StoreSegments ;
LDA #!Segments3 ; if the player is not on Yoshi, then the Pokey...still has 4 segments ($C2,x = #$0F = #%00001111)
StoreSegments: ;
STA $C2,x ;
%SubHorzPos()
TYA ; face the player initially
STA $157C,x ;
RTL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; main routine wrapper
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
print "MAIN ",pc
PHB
PHK
PLB
JSR PokeyMain
LDA $C2,x ; $C2,x has a bit for each segment
PHX ;
LDX #$04 ; start X at 04 because 5 segments is the max
LDY #$00 ;
PokeyLoopStart: ;
LSR ; shift the sprite state to the right
BCC PokeyBitClear ; if the shifted bit was clear, the carry flag will be clear
INY ; if the shifted bit was set, increment the number of segments (contained in Y)
PokeyBitClear: ;
DEX ;
BPL PokeyLoopStart ;
PLX ;
LDA Clipping,y ; set the sprite clipping value depending on how many segments it has
STA $1662,x ;
PLB
RTL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; main routine
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PokeyMain:
LDA $1534,x ; if the sprite is dead...
BNE DeadSegment ; run the routine for that
LDA $14C8,x ;
CMP #$08 ; if the sprite is in normal status...
BEQ NormalRt ; run that code
JMP PokeyGFX ; else, just run the GFX routine
DeadSegment:
JSL $8190B2 ; generic single 16x16 sprite GFX routine
LDY $15EA,x ; load the sprite OAM index back into Y
LDA $C2,x ;
CMP #$01 ; if the sprite state is not 00...
LDA #!HeadTile ; use the head tile
BCC StoreDeadTile ;
LDA #!BodyTile ; if the sprite state is 00, use the body tile
StoreDeadTile: ;
STA $0302,y ;
LDA #$80 ;Y flip if dead.
LDA $14C8,x ;
CMP #$08 ; if the sprite is not in normal status...
BNE Return00 ; return
JSL $81801A ; update sprite Y position without gravity
INC $AA,x ;
INC $AA,x ; make the sprite accelerate
LDA #$00
%SubOffScreen()
Return00: ;
RTS ;
NormalRt:
LDA $C2,x ; if there are still sections left...
BNE PokeyAlive ; then the Pokey is still alive
EraseSprite2: ;
STZ $14C8,x ; if not, erase the sprite
RTS ;
PokeyAlive: ;
CMP #$20 ; if the sprite state bits are greater than 20...
BCS EraseSprite2 ; erase the sprite immediately
LDA $9D ; if sprites are locked...
BNE SkipToGFX ; skip ahead to the GFX routine
%SubOffScreen()
JSL $81A7DC ; interact with the player
INC $1570,x ; increment the sprite frame counter
LDA $1570,x ;
AND #$7F ; every 80 frames...
BNE NoFace ;
%SubHorzPos()
TYA ;
STA $157C,x ;
NoFace: ;
LDY $157C,x ;
LDA XSpeed,y ; set the sprite's X speed depending on direction
STA $B6,x ;
JSL $818022 ; update sprite X position without gravity
JSL $81801A ; update sprite Y position without gravity
LDA $AA,x ;
CMP #$40 ; if the sprite Y speed is less than 40...
BPL NoAccelerate ;
CLC ;
ADC #$02 ; make the sprite accelerate
STA $AA,x ;
NoAccelerate: ;
JSL $819138 ; interact with objects
LDA $1588,x ;
AND #$04 ; if the sprite is touching the ground...
BEQ NoZeroXSpeed ;
STZ $AA,x ; set its X speed to zero
NoZeroXSpeed: ;
LDA $1588,x ;
AND #$03 ; if the sprite is touching a wall...
BEQ NoFlipDir ;
LDA $157C,x ;
EOR #$01 ; flip its direction
STA $157C,x ;
NoFlipDir: ;
JSR SpriteInteract ; interact with sprites; check if sections need to be removed
LDY #$00 ; start Y at 00
CheckLoop: ;
LDA $C2,x ;
AND BitTable1,y ; if a particular bit is set...
BNE EndOfLoop ; then the sprite has that section
LDA $C2,x ;
PHA ;
AND BitTable2,y ; clear the top bit
STA $00 ; and save the result
PLA ;
LSR ;
AND BitTable3,y ; I'm honestly not sure what the heck the purpose of this subroutine is.
ORA $00 ; It's, like, clearing bits and then setting them again or something...
STA $C2,x ;
EndOfLoop: ;
INY ; increment the bit index
CPY #$04 ; if we've reached the 5th index...
BNE CheckLoop ; break the loop
SkipToGFX:
JMP PokeyGFX ; I separated this from the main routine to make things simpler.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; sprite interaction routine
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SpriteInteract:
LDY #$09 ; 0A sprites to loop through (why not 0C, SMW?)
SprCheckLoop:
TYA ;
EOR $13 ; every other frame depending on whether the
LSR ; contacting sprite has an even or odd index...
BCS EndOfLoop2 ; skip interaction
LDA $14C8,y ; check the sprite status of the second sprite
CMP #$0A ; if the second sprite isn't kicked...
BNE EndOfLoop2 ; skip interaction
;PHB ; preserve the current data bank
;LDA #$03 ; set the data bank to 03
;PHA ; which, of course, is completely pointless,
;PLB ; since these subroutines load all ROM tables in 24-bit mode anyway
PHX ; preserve the Pokey sprite index
TYX ; get the second sprite index into X
JSL $83B6E5 ; get clipping values for the second sprite
PLX ; get the first sprite index back into X
JSL $83B69F ; get clipping values for the first sprite
JSL $83B72B ; check for contact between the two
;PLB ;
BCS IsContact ; if the carry flag is set, then the sprites made contact
EndOfLoop2:
DEY ; decrement the sprite index
BPL SprCheckLoop ; if still positive, there are more sprites to check
RTS
IsContact:
LDA $1558,x ; if the sprite is sinking in lava (see, this is why this shouldn't be used as a misc. table)...
BNE Return01 ; return
LDA $00D8,y ; Y position of the contacting sprite
SEC ;
SBC $D8,x ; minus Y position of the Pokey
PHY ;
STY $1695 ; preserve the second sprite index
JSR RemoveSegment ; remove one or more of Pokey's segments
;JSL $82B81C ; odd, this routine could actually have been JSL'd to...I wonder why they didn't use that?
PLY ; pull back the contacting sprite index
JSR SpawnSegment ; the segments flying off Pokey when it gets hit are actually new Pokeys that are already dead
Return01: ;
RTS ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; segment-removing routine
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RemoveSegment: ; This subroutine does exactly what you'd expect it to do, in case the header wasn't clear enough.
LDY #$00 ; start Y off at 00
CMP #$09 ; if the result of the position subtraction operation was less than 09...
BMI ClearBit ; keep the current Y-index
INY ; if the result was 09 or greater, increment the index
CMP #$19 ; if the result of the position subtraction operation was between 09 and 18...
BMI ClearBit ; keep the current Y-index
INY ; if the result was 19 or greater, increment the index
CMP #$29 ; if the result of the position subtraction operation was between 19 and 28...
BMI ClearBit ; keep the current Y-index
INY ; if the result was 29 or greater, increment the index
CMP #$39 ; if the result of the position subtraction operation was between 29 and 38...
BMI ClearBit ; keep the current Y-index
INY ; if the result was 39 or greater, increment the index
ClearBit: ;
LDA $C2,x ; sprite state (section counter)
AND BitTable4,y ; clear a specific bit, which effectively removes the segment it represents
STA $C2,x ;
LDA BitTable5,y ; this table is the inverse of the section counter
STA $0D ;
LDA #$0C ;
STA $1540,x ; set...a timer
ASL ;
STA $1558,x ;
Return02: ;
RTS ;
SpawnSegment:
JSL $82A9E4 ; find a free sprite slot for the dead Pokey head
BMI Return02 ; return if none are free
LDA #$02 ; set the sprite status as dead
STA $14C8,y ; (seriously, how many other spawning routines have you seen that do this?)
PHX ;
LDA $7FAB9E,x ; same sprite number
TYX ;
STA $7FAB9E,x ;
PLX ;
LDA $E4,x ;
STA $00E4,y ; new sprite X position = the same as the old one
LDA $14E0,x ;
STA $14E0,y ;
PHX ;
TYX ;
JSL $87F7D2 ; initialize sprite tables
JSL $8187A7 ;
LDX $1695 ; load the index of the kicked sprite into X
LDA $D8,x ;
STA $00D8,y ; set the sprite Y position
LDA $14D4,x ; relative to the kicked sprite rather than the Pokey
STA $14D4,y ;
LDA $B6,x ;
STA $00 ;
ASL ;
ROR $00 ;
LDA $00 ;
STA $00B6,y ; set the dead sprite X speed
LDA #$E0 ;
STA $00AA,y ; set its Y speed
PLX ;
LDA $C2,x ; sprite state of the old sprite
AND $0D ; the bits that we set earlier
STA $00C2,y ;
LDA #$01 ; set the "dead" flag
STA $1534,y ;
LDA #$01 ; give 200 points
JSL $82ACE1 ;
RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; graphics routine
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PokeyGFX:
%GetDrawInfo()
LDA $01 ;
CLC ;
ADC #$40 ; offset the sprite Y position by 40 pixels
STA $01 ;
LDA $C2,x ; sprite state
STA $02 ; into *two* bytes of scratch RAM?
STA $07 ;
LDA $151C,x ; ...huh? $151C wasn't even referenced or modified anywhere else!
STA $04 ;
LDY $1540,x ;
LDA Data1,y ;
STA $03 ;
STZ $05 ;
LDY $15EA,x ;
PHX ;
LDX #$04 ; 5 tiles to draw
GFXLoop:
STX $06 ;
LDA $14 ;
LSR #3 ;
CLC ;
ADC $06 ;
AND #$03 ;
TAX ;
LDA $07 ;
CMP #$01 ; if the sprite has only 1 segment left, or we're drawing the bottom segment
BNE Not1Segment ;
LDX #$00 ; the X displacement index is 00
Not1Segment: ;
LDA $00 ;
CLC ;
ADC XDisp,x ; set the tile X displacement
STA $0300,y ;
LDX $06 ;
LDA $01 ;
LSR $02 ;
BCC Label00 ;
LSR $04 ; ...what?
BCS Label01 ;
PHA ;
LDA $03 ;
STA $05 ;
PLA ;
Label01: ;
SEC ;
SBC $05 ;
STA $0301,y ;
Label00: ;
LDA $01 ;
SEC ;
SBC #$10 ;
STA $01 ;
LDA $02 ;
LSR ;
LDA #!BodyTile ;
BCS StoreTile ;
LDA #!HeadTile ;
StoreTile: ;
STA $0302,y ;
LDA #$05 ;
ORA $64 ;
STA $0303,y ;
INY #4 ;
DEX ;
BPL GFXLoop ;
PLX ;
LDA #$04 ;
LDY #$02 ;
JSL $81B7B3 ;
RTS;