;SMBDIS.ASM - A COMPREHENSIVE SUPER MARIO BROS. DISASSEMBLY (Super Mario Bros (JU))
;by doppelganger (doppelheathen@gmail.com)
;This file is provided for your own use as-is. It will require the character rom data
;and an iNES file header to get it to work.
;There are so many people I have to thank for this, that taking all the credit for
;myself would be an unforgivable act of arrogance. Without their help this would
;probably not be possible. So I thank all the peeps in the nesdev scene whose insight into
;the 6502 and the NES helped me learn how it works (you guys know who you are, there's no
;way I could have done this without your help), as well as the authors of x816 and SMB
;Utility, and the reverse-engineers who did the original Super Mario Bros. Hacking Project,
;which I compared notes with but did not copy from. Last but certainly not least, I thank
;Nintendo for creating this game and the NES, without which this disassembly would
;only be theory.
;Assembles with x816.
;< LOW low byte
;> HIGH high byte
;-------------------------------------------------------------------------------------
;DIRECTIVES
if Z80==0
.index 8
.mem 8
.org $8000
endif
;-------------------------------------------------------------------------------------
Start:
if Z80==0
sei ;pretty standard 6502 type init here
cld
ldan ++%00010000 ;init PPU control register 1
sta PPU_CTRL_REG1
ldxn ++$ff ;reset stack pointer
txs
VBlank1: lda PPU_STATUS ;wait two frames
checka
bpl VBlank1
VBlank2: lda PPU_STATUS
checka
bpl VBlank2
endif
ldyn16 ++MEMDATA_end-AREADATA;ColdBootOffset ;load default cold boot pointer
ldxn ++$05 ;this is where we check for a warm boot
WBootCheck:
if Z80==0
ldax TopScoreDisplay,x ;check each score digit in the top score
cmpn ++10 ;to see if we have a valid digit
cmpcy
bcs ColdBoot ;if not, give up and proceed with cold boot
dex
bpl WBootCheck
lda WarmBootValidation ;second checkpoint, check to see if
cmpn ++$a5 ;another location has a specific value
bne ColdBoot
ldyn16 ++WARMMEMDATA_end-AREADATA;WarmBootOffset ;if passed both, load warm boot pointer
endif
ColdBoot: jsr InitializeMemory ;clear memory using pointer in Y
sta SND_DELTA_REG+1 ;reset delta counter load register
sta OperMode ;reset primary mode of operation
ldan ++$a5 ;set warm boot flag
sta WarmBootValidation
sta PseudoRandomBitReg ;set seed for pseudorandom register
ldan ++%00001111
sta SND_MASTERCTRL_REG ;enable all sound channels except dmc
if Z80==0
ldan ++%00000110
sta PPU_CTRL_REG2 ;turn off clipping for OAM and background
endif
jsr MoveAllSpritesOffscreen
jsr InitializeNameTables ;initialize both name tables
inci DisableScreenFlag ;set flag to disable screen output
if Z80==0
lda Mirror_PPU_CTRL_REG1
oran ++%10000000 ;enable NMIs
jsr WritePPUReg1
endif
call NonMaskableInterrupt
call gettimer ;hl=timer
ld (oldupdtimer),hl
EndlessLoop:
if Z80
call EmulatePPU
call gettimer ;hl=timer
oldupdtimer=$+1
ld de,oldupdtimer
ld (oldupdtimer),hl
or a
sbc hl,de
if FASTDEMOBEFOREBREAKPOINT
ld c,1
jr z,c_logic0
else
jr z,nologic
endif
ld c,8
ld a,l
cp c
jr nc,$+3 ;сюы№°х с√трхЄ Єюы№ъю т сЁ ъяюшэЄрї
ld c,l
c_logic0
ld hl,logicframe
ld (hl),c
ld b,0
ld d,b
logic0
call NonMaskableInterrupt
ld hl,logicframe
dec (hl)
jr nz,logic0
jr logicq
nologic
;jr $ ;Ёхры№эю эх яюярфрхь ё■фр яЁш эюЁьры№эющ шуЁх
YIELD;halt
ld b,0
ld d,b
logicq
endif
jmp EndlessLoop ;endless loop, need I say more?
if MUSICONINT
include "smbsound.asm"
include "smbmusic.asm"
;display $,"<=0x8000"
endif
;-------------------------------------------------------------------------------------
;$00 - vram buffer address table low, also used for pseudorandom bit
;$01 - vram buffer address table high
VRAM_AddrTable_Low:
.db LOW VRAM_Buffer1, LOW WaterPaletteData, LOW GroundPaletteData
.db LOW UndergroundPaletteData, LOW CastlePaletteData, LOW VRAM_Buffer1_Offset
.db LOW VRAM_Buffer2, LOW VRAM_Buffer2, LOW BowserPaletteData
.db LOW DaySnowPaletteData, LOW NightSnowPaletteData, LOW MushroomPaletteData
.db LOW MarioThanksMessage, LOW LuigiThanksMessage, LOW MushroomRetainerSaved
.db LOW PrincessSaved1, LOW PrincessSaved2, LOW WorldSelectMessage1
.db LOW WorldSelectMessage2
VRAM_AddrTable_High:
.db HIGH VRAM_Buffer1, HIGH WaterPaletteData, HIGH GroundPaletteData
.db HIGH UndergroundPaletteData, HIGH CastlePaletteData, HIGH VRAM_Buffer1_Offset
.db HIGH VRAM_Buffer2, HIGH VRAM_Buffer2, HIGH BowserPaletteData
.db HIGH DaySnowPaletteData, HIGH NightSnowPaletteData, HIGH MushroomPaletteData
.db HIGH MarioThanksMessage, HIGH LuigiThanksMessage, HIGH MushroomRetainerSaved
.db HIGH PrincessSaved1, HIGH PrincessSaved2, HIGH WorldSelectMessage1
.db HIGH WorldSelectMessage2
if Z80
tpalettes
dw waterpalette
dw groundpalette
dw undergroundpalette
dw castlepalette
endif
VRAM_Buffer_Offset:
.db LOW VRAM_Buffer1_Offset, LOW VRAM_Buffer2_Offset
NonMaskableInterrupt:
if Z80
;ldx VRAM_Buffer_AddrCtrl ;load control for pointer to buffer contents
;ldax VRAM_AddrTable_High,x
;ld d,a
;ldax VRAM_AddrTable_Low,x
;ld e,a
;ex de,hl
;ld de,PPU_SPRLIST
;ld bc,0x100
;ldir
;ex de,hl
;ld d,b;0
lda Mirror_PPU_CTRL_REG1 ;d0 = ёЄЁрэшЎр (фы ёъЁюыыр)
sta PPU_CTRL_REG1
else
lda Mirror_PPU_CTRL_REG1 ;disable NMIs in mirror reg
andn ++%01111111 ;save all other bits
sta Mirror_PPU_CTRL_REG1
andn ++%01111110 ;alter name table address to be $2800 ;ўЄюс√ т√тюфшЄ№ тёхуфр ёЄЁрэшЎє ёю ёў╕Єюь
sta PPU_CTRL_REG1 ;(essentially $2000) but save other bits
lda Mirror_PPU_CTRL_REG2 ;disable OAM and background display by default
andn ++%11100110
ldy DisableScreenFlag ;get screen disable flag
checky
bne ScreenOff ;if set, used bits as-is
lda Mirror_PPU_CTRL_REG2 ;otherwise reenable bits and save them
oran ++%00011110
ScreenOff: sta Mirror_PPU_CTRL_REG2 ;save bits for later but not in register at the moment
andn ++%11100111 ;disable screen for now
sta PPU_CTRL_REG2
ldx PPU_STATUS ;reset flip-flop and reset scroll registers to zero
ldan ++$00
jsr InitScroll ;яш°хЄ эєыш т ёъЁюыы√ (ўЄюс√ эх ёъЁюыышЄ№ ёў╕Є)
sta PPU_SPR_ADDR ;reset spr-ram address register
ldan ++$02 ;perform spr-ram DMA access on $0200-$02ff
sta SPR_DMA
endif
ldx VRAM_Buffer_AddrCtrl ;load control for pointer to buffer contents
ldax VRAM_AddrTable_Low,x ;set indirect at $00 to pointer
sta SCRATCHPAD+$00
ldax VRAM_AddrTable_High,x
sta SCRATCHPAD+$01
;cp 0x03
;jr nz,$
jsr UpdateScreen ;update screen with buffer contents
ldyn ++$00
ldx VRAM_Buffer_AddrCtrl ;check for usage of $0341 ;VRAM_Buffer2???
cpxn ++$06
bne InitBuffer
iny ;get offset based on usage
InitBuffer: ldxy VRAM_Buffer_Offset,y
ldan ++$00 ;clear buffer header at last location
stax VRAM_Buffer1_Offset,x
stax VRAM_Buffer1,x
sta VRAM_Buffer_AddrCtrl ;reinit address control to $0301 ;VRAM_Buffer1???
if Z80==0
lda Mirror_PPU_CTRL_REG2 ;copy mirror of $2001 to register
sta PPU_CTRL_REG2
endif
if MUSICONINT==0
jsr SoundEngine ;play sound
else
soundenginepatch=$+1
call SoundEngine_noint ;play sound logically
endif
jsr ReadJoypads ;read joypads
if DEMO
ld a,(readdemo_stopflag)
or a
jr nz,randomskip;PauseSkip
endif
jsr PauseRoutine ;handle pause ;яюўхьє-Єю хёыш т√°х, Єю яюёых т√їюфр шч фхь√ тъы■ўрхЄ ярєчє
jsr UpdateTopScore
lda GamePauseStatus ;check for pause status
lsr
bcs PauseSkip
if Z80;OPT
ld a,(TimerControl) ;if master timer control not set, decrement
or a
jr z,DecTimers ;all frame and interval timers
dec a
ld (TimerControl),a
jr nz,NoDecTimers
DecTimers: ;xor a
ld b,21;ldxn ++$14 ;load end offset for end of frame timers
ld hl,IntervalTimerControl
dec (hl) ;decrement interval timer control,
ld hl,Timers+20
jp p,DecTimersLoop ;if not expired, only frame timers will decrement
ld a,20
ld (IntervalTimerControl),a ;if control for interval timers expired, (21 frame rule???)
ld b,36;ldxn ++$23 ;interval timers will decrement along with frame timers
ld hl,Timers+35
xor a
DecTimersLoop: cp (hl);ldax Timers,x ;check current timer
jr z,SkipExpTimer ;if current timer expired, branch to skip,
dec (hl);decx Timers,x ;otherwise decrement the current timer
SkipExpTimer: dec hl ;dex ;move onto next timer
djnz DecTimersLoop ;do this until all timers are dealt with
NoDecTimers: ld hl,FrameCounter
inc (hl) ;increment frame counter
else ;~Z80
lda TimerControl ;if master timer control not set, decrement
checka
beq DecTimers ;all frame and interval timers
deci TimerControl
bne NoDecTimers
DecTimers: ldxn ++$14 ;load end offset for end of frame timers
deci IntervalTimerControl ;decrement interval timer control,
bpl DecTimersLoop ;if not expired, only frame timers will decrement
ldan ++$14
sta IntervalTimerControl ;if control for interval timers expired, (21 frame rule???)
ldxn ++$23 ;interval timers will decrement along with frame timers
DecTimersLoop: ldax Timers,x ;check current timer
checka
beq SkipExpTimer ;if current timer expired, branch to skip,
decx Timers,x ;otherwise decrement the current timer
SkipExpTimer: dex ;move onto next timer
bpl DecTimersLoop ;do this until all timers are dealt with
NoDecTimers: inci FrameCounter ;increment frame counter
endif
PauseSkip: ldxn ++$00
ldyn ++$07
lda PseudoRandomBitReg ;get first memory location of LSFR bytes
andn ++%00000010 ;mask out all but d1
sta SCRATCHPAD+$00 ;save here
lda PseudoRandomBitReg+1 ;get second memory location
andn ++%00000010 ;mask out all but d1
eori SCRATCHPAD+$00 ;perform exclusive-OR on d1 from first and second bytes
clc ;if neither or both are set, carry will be clear
beq RotPRandomBit
sec ;if one or the other is set, carry will be set
RotPRandomBit: rorx PseudoRandomBitReg,x ;rotate carry into d7, and rotate last bit into carry
inx ;increment to next byte
dey ;decrement for loop
bne RotPRandomBit
randomskip
lda Sprite0HitDetectFlag ;check for flag here
checka
beq SkipSprite0
if Z80==0
Sprite0Clr: lda PPU_STATUS ;wait for sprite 0 flag to clear, which will
andn ++%01000000 ;not happen until vblank has ended
bne Sprite0Clr
endif
if DEMO
ld a,(readdemo_stopflag)
or a
jr nz,Sprite0Hit
endif
lda GamePauseStatus ;if in pause mode, do not bother with sprites at all
lsr
bcs Sprite0Hit
jsr MoveSpritesOffscreen ;3443t (3 Goomba + 2 юўъш + ╠рЁшю)
jsr SpriteShuffler ;545t
Sprite0Hit:
if Z80==0
lda PPU_STATUS ;do sprite ++0 hit detection
andn ++%01000000
beq Sprite0Hit ;цф╕ь, ъюуфр сєфхЄ ъЁрщ ьюэхЄъш эртхЁїє ¤ъЁрэр - ¤Єю ъюэхЎ эхёъЁюыышЁєхьющ чюэ√
ldyn ++$14 ;small delay, to wait until we hit horizontal blank time
HBlankDelay: dey
bne HBlankDelay
endif
SkipSprite0: lda HorizontalScroll ;set scroll registers from variables
sta PPU_SCROLL_REG_H
lda VerticalScroll
sta PPU_SCROLL_REG_V
if Z80==0
lda Mirror_PPU_CTRL_REG1 ;load saved mirror of $2000
pha
sta PPU_CTRL_REG1
endif
if DEMO
ld a,(readdemo_stopflag)
or a
jr nz,SkipMainOper
endif
lda GamePauseStatus ;if in pause mode, do not perform operation mode stuff
lsr ;яюўхьє ь√ т Ёхцшьх ярєч√??? 1
bcs SkipMainOper
jsr OperModeExecutionTree ;otherwise do one of many, many possible subroutines ;49489t (3 Goomba + 2 юўъш + ╠рЁшю)
SkipMainOper:
if Z80==0
lda PPU_STATUS ;reset flip-flop
pla
oran ++%10000000 ;reactivate NMIs
sta PPU_CTRL_REG1
endif
rti ;we are done until the next frame!
;-------------------------------------------------------------------------------------
PauseRoutine:
lda OperMode ;are we in victory mode?
cmpn ++VictoryModeValue ;if so, go ahead
beq ChkPauseTimer
cmpn ++GameModeValue ;are we in game mode?
bne ExitPause ;if not, leave
lda OperMode_Task ;if we are in game mode, are we running game engine?
cmpn ++$03
bne ExitPause ;if not, leave
ChkPauseTimer: lda GamePauseTimer ;check if pause timer is still counting down
checka
beq ChkStart
deci GamePauseTimer ;if so, decrement and leave
rts
ChkStart: lda SavedJoypad1Bits ;check to see if start is pressed
andn ++Start_Button ;on controller 1
beq ClrPauseTimer
lda GamePauseStatus ;check to see if timer flag is set
andn ++%10000000 ;and if so, do not reset timer (residual,
bne ExitPause ;joypad reading routine makes this unnecessary)
;jr $
ldan ++$2b ;set pause timer
sta GamePauseTimer
lda GamePauseStatus
tay
iny ;set pause sfx queue for next pause mode
sty PauseSoundQueue
eorn ++%00000001 ;invert d0 and set d7
oran ++%10000000
bne SetPause ;unconditional branch
ClrPauseTimer: lda GamePauseStatus ;clear timer flag if timer is at zero and start button
andn ++%01111111 ;is not pressed
SetPause: sta GamePauseStatus
ExitPause: rts
;-------------------------------------------------------------------------------------
;$00 - used for preset value
SpriteShuffler:
if Z80
else
ldy AreaType ;load level type, likely residual code
ldan ++$28 ;load preset value which will put it at
sta SCRATCHPAD+$00 ;sprite #10
ldxn ++$0e ;start at the end of OAM data offsets
ShuffleLoop: ldax SprDataOffset,x ;check for offset value against
cmpi SCRATCHPAD+$00 ;the preset value
cmpcy
bcc NextSprOffset ;if less, skip this part
ldy SprShuffleAmtOffset ;get current offset to preset value we want to add
clc
adcy SprShuffleAmt,y ;get shuffle amount, add to current sprite offset
bcc StrSprOffset ;if not exceeded $ff, skip second add
clc
adci SCRATCHPAD+$00 ;otherwise add preset value $28 to offset
StrSprOffset: stax SprDataOffset,x ;store new offset here or old one if branched to here
NextSprOffset: dex ;move backwards to next one
bpl ShuffleLoop
ldx SprShuffleAmtOffset ;load offset
inx
cpxn ++$03 ;check if offset + 1 goes to 3
bne SetAmtOffset ;if offset + 1 not 3, store
ldxn ++$00 ;otherwise, init to 0
SetAmtOffset: stx SprShuffleAmtOffset
endif
ldxn ++$08 ;load offsets for values and storage
ldyn ++$02
SetMiscOffset: lday SprDataOffset+5,y ;load one of three OAM data offsets
stax Misc_SprDataOffset-2,x ;store first one unmodified, but
clc ;add eight to the second and eight
adcn ++$08 ;more to the third one
stax Misc_SprDataOffset-1,x ;note that due to the way X is set up,
clc ;this code loads into the misc sprite offsets
adcn ++$08
stax Misc_SprDataOffset,x
dex
dex
dex
dey
bpl SetMiscOffset ;do this until all misc spr offsets are loaded (3 °Єєъш)
rts
;-------------------------------------------------------------------------------------
OperModeExecutionTree:
lda OperMode ;this is the heart of the entire program,
jsr JumpEngine ;most of what goes on starts here
.dw TitleScreenMode
.dw GameMode
.dw VictoryMode
.dw GameOverMode
;-------------------------------------------------------------------------------------
MoveAllSpritesOffscreen:
ldyn ++$00 ;this routine moves all sprites off the screen
jr MoveSpritesOffscreen_go;.db $2c ;BIT instruction opcode
MoveSpritesOffscreen:
ldyn ++$04 ;this routine moves all but sprite 0
MoveSpritesOffscreen_go
ldan ++$f8 ;off the screen
SprInitLoop: stay Sprite_Y_Position,y ;write 248 into OAM data's Y coordinate
iny ;which will move it off the screen
iny
iny
iny
bne SprInitLoop
rts
;-------------------------------------------------------------------------------------
TitleScreenMode:
lda OperMode_Task
jsr JumpEngine
.dw InitializeGame
.dw ScreenRoutines
.dw PrimaryGameSetup
.dw GameMenuRoutine
;-------------------------------------------------------------------------------------
WSelectBufferTemplate:
.db $04, $20, $73, $01, $00, $00
GameMenuRoutine:
;jr $
ldyn ++$00
lda SavedJoypad1Bits ;check to see if either player pressed
orai SavedJoypad2Bits ;only the start button (either joypad)
cmpn ++Start_Button
beq StartGame
cmpn ++A_Button+Start_Button ;check to see if A + start was pressed
bne ChkSelect ;if not, branch to check select button
StartGame:
;jr $
jmp ChkContinue ;if either start or A + start, execute here
ChkSelect: cmpn ++Select_Button ;check to see if the select button was pressed
beq SelectBLogic ;if so, branch reset demo timer
ldx DemoTimer ;otherwise check demo timer
checkx
bne ChkWorldSel ;if demo timer not expired, branch to check world selection
sta SelectTimer ;set controller bits here if running demo
jsr DemoEngine ;run through the demo actions
bcs ResetTitle ;if carry flag set, demo over, thus branch
;jr $ ;ё■фр яюярфрхь
jmp RunDemo ;otherwise, run game engine for demo
ChkWorldSel: ldx WorldSelectEnableFlag ;check to see if world selection has been enabled
checkx
beq NullJoypad
cmpn ++B_Button ;if so, check to see if the B button was pressed
bne NullJoypad
iny ;if so, increment Y and execute same code as select
SelectBLogic: lda DemoTimer ;if select or B pressed, check demo timer one last time
;jr $ ;ё■фр яюярфрхь яю select
checka
beq ResetTitle ;if demo timer expired, branch to reset title screen mode
;jr $ ;ё■фр яюярфрхь яю select Єюы№ъю ё яЁртшы№э√ьш ЄрщьхЁрьш
ldan ++$18 ;otherwise reset demo timer
sta DemoTimer
lda SelectTimer ;check select/B button timer
checka
bne NullJoypad ;if not expired, branch
ldan ++$10 ;otherwise reset select button timer
sta SelectTimer
cpyn ++$01 ;was the B button pressed earlier? if so, branch
beq IncWorldSel ;note this will not be run if world selection is disabled
;jr $ ;ё■фр яюярфрхь яю select Єюы№ъю ё яЁртшы№э√ьш ЄрщьхЁрьш
lda NumberOfPlayers ;if no, must have been the select button, therefore
eorn ++%00000001 ;change number of players and draw icon accordingly
sta NumberOfPlayers
jsr DrawMushroomIcon
jmp NullJoypad
IncWorldSel: ldx WorldSelectNumber ;increment world select number
inx
txa
andn ++%00000111 ;mask out higher bits
sta WorldSelectNumber ;store as current world select number
jsr GoContinue
UpdateShroom: ldax WSelectBufferTemplate,x ;write template for world select in vram buffer
stax VRAM_Buffer1-1,x ;do this until all bytes are written
inx
cpxn ++$06
bmi UpdateShroom
ldy WorldNumber ;get world number from variable and increment for
iny ;proper display, and put in blank byte before
sty VRAM_Buffer1+3 ;null terminator
NullJoypad: ldan ++$00 ;clear joypad bits for player 1
sta SavedJoypad1Bits
RunDemo: jsr GameCoreRoutine ;run game engine
lda GameEngineSubroutine ;check to see if we're running lose life routine
cmpn ++$06
bne ExitMenu ;if not, do not do all the resetting below
ResetTitle: ldan ++$00 ;reset game modes, disable
sta OperMode ;sprite 0 check and disable
sta OperMode_Task ;screen output
sta Sprite0HitDetectFlag
inci DisableScreenFlag
rts
ChkContinue: ldy DemoTimer ;if timer for demo has expired, reset modes
checky
;jr $
beq ResetTitle ;яюўхьє ьх°рхЄ фхьрь??? хёыш чръюььхэЄшЁютрЄ№, Єю шч фхь√ яю Start яюярфрхь эх т ьхэ■, р т шуЁє, ё сюы№°шь ╠рЁшю, хёыш юэ єцх сюы№°ющ
asl ;check to see if A button was also pushed
bcc StartWorld1 ;if not, don't load continue function's world number
lda ContinueWorld ;load previously saved world number for secret
jsr GoContinue ;continue function when pressing A + start
StartWorld1: jsr LoadAreaPointer
;jr $ ;ё■фр яюярфрхь яю start
inci Hidden1UpFlag ;set 1-up box flag for both players
inci OffScr_Hidden1UpFlag
inci FetchNewGameTimerFlag ;set fetch new game timer flag
inci OperMode ;set next game mode
lda WorldSelectEnableFlag ;if world select flag is on, then primary
sta PrimaryHardMode ;hard mode must be on as well
ldan ++$00
sta OperMode_Task ;set game mode here, and clear demo timer
sta DemoTimer
ldxn ++$17
ldan ++$00
InitScores: stax ScoreAndCoinDisplay,x ;clear player scores and coin displays
dex
bpl InitScores
ExitMenu: rts
GoContinue: sta WorldNumber ;start both players at the first area
sta OffScr_WorldNumber ;of the previously saved world number
ldxn ++$00 ;note that on power-up using this function
stx AreaNumber ;will make no difference
stx OffScr_AreaNumber
rts
;-------------------------------------------------------------------------------------
MushroomIconData:
.db $07, $22, $49, $83, $ce, $24, $24, $00
DrawMushroomIcon:
ldyn ++$07 ;read eight bytes to be read by transfer routine
IconDataRead: lday MushroomIconData,y ;note that the default position is set for a
stay VRAM_Buffer1-1,y ;1-player game
dey
bpl IconDataRead
lda NumberOfPlayers ;check number of players
checka
beq ExitIcon ;if set to 1-player game, we're done
ldan ++$24 ;otherwise, load blank tile in 1-player position
sta VRAM_Buffer1+3
ldan ++$ce ;then load shroom icon tile in 2-player position
sta VRAM_Buffer1+5
ExitIcon: rts
;-------------------------------------------------------------------------------------
DemoActionData:
.db $01, $80, $02, $81, $41, $80, $01
.db $42, $c2, $02, $80, $41, $c1, $41, $c1
.db $01, $c1, $01, $02, $80, $00
DemoTimingData:
.db $9b, $10, $18, $05, $2c, $20, $24
.db $15, $5a, $10, $20, $28, $30, $20, $10
.db $80, $20, $30, $30, $01, $ff, $00
DemoEngine:
ldx DemoAction ;load current demo action
lda DemoActionTimer ;load current action timer
checka
bne DoAction ;if timer still counting down, skip
inx
inci DemoAction ;if expired, increment action, X, and
sec ;set carry by default for demo over
ldax DemoTimingData-1,x ;get next timer
if Z80
sec
endif
sta DemoActionTimer ;store as current timer
checka
beq DemoOver ;if timer already at zero, skip
DoAction: ldax DemoActionData-1,x ;get and perform action (current or next)
sta SavedJoypad1Bits
deci DemoActionTimer ;decrement action timer
clc ;clear carry if demo still going
DemoOver: rts
;-------------------------------------------------------------------------------------
VictoryMode:
jsr VictoryModeSubroutines ;run victory mode subroutines
lda OperMode_Task ;get current task of victory mode
checka
beq AutoPlayer ;if on bridge collapse, skip enemy processing
ldxn ++$00
stx ObjectOffset ;otherwise reset enemy object offset
jsr EnemiesAndLoopsCore ;and run enemy code
AutoPlayer: jsr RelativePlayerPosition ;get player's relative coordinates
jmp PlayerGfxHandler ;draw the player, then leave
VictoryModeSubroutines:
lda OperMode_Task
jsr JumpEngine
.dw BridgeCollapse
.dw SetupVictoryMode
.dw PlayerVictoryWalk
.dw PrintVictoryMessages
.dw PlayerEndWorld
;-------------------------------------------------------------------------------------
SetupVictoryMode:
ldx ScreenRight_PageLoc ;get page location of right side of screen
inx ;increment to next page
stx DestinationPageLoc ;store here
ldan ++EndOfCastleMusic
sta EventMusicQueue ;play win castle music
jmp IncModeTask_B ;jump to set next major task in victory mode
;-------------------------------------------------------------------------------------
PlayerVictoryWalk:
ldyn ++$00 ;set value here to not walk player by default
sty VictoryWalkControl
lda Player_PageLoc ;get player's page location
cmpi DestinationPageLoc ;compare with destination page location
bne PerformWalk ;if page locations don't match, branch
lda Player_X_Position ;otherwise get player's horizontal position
cmpn ++$60 ;compare with preset horizontal position
cmpcy
bcs DontWalk ;if still on other page, branch ahead
PerformWalk: inci VictoryWalkControl ;otherwise increment value and Y
iny ;note Y will be used to walk the player
DontWalk: tya ;put contents of Y in A and
jsr AutoControlPlayer ;use A to move player to the right or not
lda ScreenLeft_PageLoc ;check page location of left side of screen
cmpi DestinationPageLoc ;against set value here
beq ExitVWalk ;branch if equal to change modes if necessary
lda ScrollFractional
clc ;do fixed point math on fractional part of scroll
adcn ++$80
sta ScrollFractional ;save fractional movement amount
ldan ++$01 ;set 1 pixel per frame
adcn ++$00 ;add carry from previous addition
tay ;use as scroll amount
jsr ScrollScreen ;do sub to scroll the screen
jsr UpdScrollVar ;do another sub to update screen and scroll variables
inci VictoryWalkControl ;increment value to stay in this routine
ExitVWalk: lda VictoryWalkControl ;load value set here
checka
beq IncModeTask_A ;if zero, branch to change modes
rts ;otherwise leave
;-------------------------------------------------------------------------------------
PrintVictoryMessages:
lda SecondaryMsgCounter ;load secondary message counter
checka
bne IncMsgCounter ;if set, branch to increment message counters
lda PrimaryMsgCounter ;otherwise load primary message counter
checka
beq ThankPlayer ;if set to zero, branch to print first message
cmpn ++$09 ;if at 9 or above, branch elsewhere (this comparison
cmpcy
bcs IncMsgCounter ;is residual code, counter never reaches 9)
ldy WorldNumber ;check world number
cpyn ++World8
bne MRetainerMsg ;if not at world 8, skip to next part
cmpn ++$03 ;check primary message counter again
cmpcy
bcc IncMsgCounter ;if not at 3 yet (world 8 only), branch to increment
cmpcy
sbcn ++$01 ;otherwise subtract one
jmp ThankPlayer ;and skip to next part
MRetainerMsg: cmpn ++$02 ;check primary message counter
cmpcy
bcc IncMsgCounter ;if not at 2 yet (world 1-7 only), branch
ThankPlayer: tay ;put primary message counter into Y
checka
bne SecondPartMsg ;if counter nonzero, skip this part, do not print first message
lda CurrentPlayer ;otherwise get player currently on the screen
checka
beq EvalForMusic ;if mario, branch
iny ;otherwise increment Y once for luigi and
bne EvalForMusic ;do an unconditional branch to the same place
SecondPartMsg: iny ;increment Y to do world 8's message
lda WorldNumber
cmpn ++World8 ;check world number
beq EvalForMusic ;if at world 8, branch to next part
dey ;otherwise decrement Y for world 1-7's message
cpyn ++$04 ;if counter at 4 (world 1-7 only)
cmpcy
bcs SetEndTimer ;branch to set victory end timer
cpyn ++$03 ;if counter at 3 (world 1-7 only)
cmpcy
bcs IncMsgCounter ;branch to keep counting
EvalForMusic: cpyn ++$03 ;if counter not yet at 3 (world 8 only), branch
bne PrintMsg ;to print message only (note world 1-7 will only
ldan ++VictoryMusic ;reach this code if counter = 0, and will always branch)
sta EventMusicQueue ;otherwise load victory music first (world 8 only)
PrintMsg: tya ;put primary message counter in A
clc ;add $0c or 12 to counter thus giving an appropriate value,
adcn ++$0c ;($0c-$0d = first), ($0e = world 1-7's), ($0f-$12 = world 8's)
sta VRAM_Buffer_AddrCtrl ;write message counter to vram address controller
IncMsgCounter: lda SecondaryMsgCounter
clc
adcn ++$04 ;add four to secondary message counter
sta SecondaryMsgCounter
lda PrimaryMsgCounter
adcn ++$00 ;add carry to primary message counter
sta PrimaryMsgCounter
cmpn ++$07 ;check primary counter one more time
cmpcy
SetEndTimer:
bcc ExitMsgs ;if not reached value yet, branch to leave
ldan ++$06
sta WorldEndTimer ;otherwise set world end timer
IncModeTask_A: inci OperMode_Task ;move onto next task in mode
ExitMsgs: rts ;leave
;-------------------------------------------------------------------------------------
PlayerEndWorld:
lda WorldEndTimer ;check to see if world end timer expired
checka
bne EndExitOne ;branch to leave if not
ldy WorldNumber ;check world number
cpyn ++World8 ;if on world 8, player is done with game,
cmpcy
bcs EndChkBButton ;thus branch to read controller
ldan ++$00
sta AreaNumber ;otherwise initialize area number used as offset
sta LevelNumber ;and level number control to start at area 1
sta OperMode_Task ;initialize secondary mode of operation
inci WorldNumber ;increment world number to move onto the next world
jsr LoadAreaPointer ;get area address offset for the next area
inci FetchNewGameTimerFlag ;set flag to load game timer from header
ldan ++GameModeValue
sta OperMode ;set mode of operation to game mode
EndExitOne: rts ;and leave
EndChkBButton: lda SavedJoypad1Bits
orai SavedJoypad2Bits ;check to see if B button was pressed on
andn ++B_Button ;either controller
beq EndExitTwo ;branch to leave if not
ldan ++$01 ;otherwise set world selection flag
sta WorldSelectEnableFlag
ldan ++$ff ;remove onscreen player's lives
sta NumberofLives
jsr TerminateGame ;do sub to continue other player or end game
EndExitTwo: rts ;leave
;-------------------------------------------------------------------------------------
;data is used as tiles for numbers
;that appear when you defeat enemies
FloateyNumTileData:
.db $ff, $ff ;dummy
.db $f6, $fb ; "100"
.db $f7, $fb ; "200"
.db $f8, $fb ; "400"
.db $f9, $fb ; "500"
.db $fa, $fb ; "800"
.db $f6, $50 ; "1000"
.db $f7, $50 ; "2000"
.db $f8, $50 ; "4000"
.db $f9, $50 ; "5000"
.db $fa, $50 ; "8000"
.db $fd, $fe ; "1-UP"
;high nybble is digit number, low nybble is number to
;add to the digit of the player's score
ScoreUpdateData:
.db $ff ;dummy
.db $41, $42, $44, $45, $48
.db $31, $32, $34, $35, $38, $00
FloateyNumbersRoutine:
ldax FloateyNum_Control,x ;load control for floatey number
checka
beq EndExitOne ;if zero, branch to leave
cmpn ++$0b ;if less than $0b, branch
cmpcy
bcc ChkNumTimer
ldan ++$0b ;otherwise set to $0b, thus keeping
stax FloateyNum_Control,x ;it in range
ChkNumTimer: tay ;use as Y
ldax FloateyNum_Timer,x ;check value here
checka
bne DecNumTimer ;if nonzero, branch ahead
stax FloateyNum_Control,x ;initialize floatey number control and leave
rts
DecNumTimer: decx FloateyNum_Timer,x ;decrement value here
cmpn ++$2b ;if not reached a certain point, branch
bne ChkTallEnemy
cpyn ++$0b ;check offset for $0b
bne LoadNumTiles ;branch ahead if not found
inci NumberofLives ;give player one extra life (1-up)
ldan ++Sfx_ExtraLife
sta Square2SoundQueue ;and play the 1-up sound
LoadNumTiles: lday ScoreUpdateData,y ;load point value here
lsr ;move high nybble to low
lsr
lsr
lsr
tax ;use as X offset, essentially the digit
lday ScoreUpdateData,y ;load again and this time
andn ++%00001111 ;mask out the high nybble
stax DigitModifier,x ;store as amount to add to the digit
jsr AddToScore ;update the score accordingly
ChkTallEnemy: ldyx Enemy_SprDataOffset,x ;get OAM data offset for enemy object
ldax Enemy_ID,x ;get enemy object identifier
cmpn ++Spiny
beq FloateyPart ;branch if spiny
cmpn ++PiranhaPlant
beq FloateyPart ;branch if piranha plant
cmpn ++HammerBro
beq GetAltOffset ;branch elsewhere if hammer bro
cmpn ++GreyCheepCheep
beq FloateyPart ;branch if cheep-cheep of either color
cmpn ++RedCheepCheep
beq FloateyPart
cmpn ++TallEnemy
cmpcy
bcs GetAltOffset ;branch elsewhere if enemy object =HIGH $09
ldax Enemy_State,x
cmpn ++$02 ;if enemy state defeated or otherwise
cmpcy
bcs FloateyPart ;$02 or greater, branch beyond this part
GetAltOffset: ldx SprDataOffset_Ctrl ;load some kind of control bit
ldyx Alt_SprDataOffset,x ;get alternate OAM data offset
ldx ObjectOffset ;get enemy object offset again
FloateyPart: ldax FloateyNum_Y_Pos,x ;get vertical coordinate for
cmpn ++$18 ;floatey number, if coordinate in the
cmpcy
bcc SetupNumSpr ;status bar, branch
cmpcy
sbcn ++$01
stax FloateyNum_Y_Pos,x ;otherwise subtract one and store as new
SetupNumSpr: ldax FloateyNum_Y_Pos,x ;get vertical coordinate
or a
sbcn ++$08 ;subtract eight and dump into the
jsr DumpTwoSpr ;left and right sprite's Y coordinates
ldax FloateyNum_X_Pos,x ;get horizontal coordinate
stay Sprite_X_Position,y ;store into X coordinate of left sprite
clc
adcn ++$08 ;add eight pixels and store into X
stay Sprite_X_Position+4,y ;coordinate of right sprite
ldan ++$02
stay Sprite_Attributes,y ;set palette control in attribute bytes
stay Sprite_Attributes+4,y ;of left and right sprites
ldax FloateyNum_Control,x
asl ;multiply our floatey number control by 2
tax ;and use as offset for look-up table
ldax FloateyNumTileData,x
stay Sprite_Tilenumber,y ;display first half of number of points
ldax FloateyNumTileData+1,x
stay Sprite_Tilenumber+4,y ;display the second half
ldx ObjectOffset ;get enemy object offset and leave
rts
;-------------------------------------------------------------------------------------
ScreenRoutines:
lda ScreenRoutineTask ;run one of the following subroutines
jsr JumpEngine
.dw InitScreen ;0
.dw SetupIntermediate ;1
.dw WriteTopStatusLine ;2
.dw WriteBottomStatusLine ;3
.dw DisplayTimeUp ;4
.dw ResetSpritesAndScreenTimer ;5
.dw DisplayIntermediate ;6
.dw ResetSpritesAndScreenTimer ;7
.dw AreaParserTaskControl ;8
.dw GetAreaPalette ;9
.dw GetBackgroundColor ;10
.dw GetAlternatePalette1 ;11
.dw DrawTitleScreen ;12
.dw ClearBuffersDrawIcon ;13
.dw WriteTopScore ;14
;-------------------------------------------------------------------------------------
InitScreen:
jsr MoveAllSpritesOffscreen ;initialize all sprites including sprite ++0
jsr InitializeNameTables ;and erase both name and attribute tables
lda OperMode
checka
beq NextSubtask ;if mode still 0, do not load
ldxn ++$03 ;into buffer pointer
jmp SetVRAMAddr_A
;-------------------------------------------------------------------------------------
SetupIntermediate:
lda BackgroundColorCtrl ;save current background color control
pha ;and player status to stack
lda PlayerStatus
pha
ldan ++$00 ;set background color to black
sta PlayerStatus ;and player status to not fiery
ldan ++$02 ;this is the ONLY time background color control
sta BackgroundColorCtrl ;is set to less than 4
jsr GetPlayerColors
pla ;we only execute this routine for
sta PlayerStatus ;the intermediate lives display
pla ;and once we're done, we return bg
sta BackgroundColorCtrl ;color ctrl and player status from stack
jmp IncSubtask ;then move onto the next task
;-------------------------------------------------------------------------------------
if Z80==0
AreaPalette:
.db $01, $02, $03, $04
endif
GetAreaPalette:
if Z80
ld a,(AreaType)
add a,a
ld e,a
ld hl,tpalettes
add hl,de
ld a,(hl)
inc hl
ld h,(hl)
ld l,a
ld (curpalette),hl
jp IncSubtask ;move onto next task
else
ldy AreaType ;select appropriate palette to load
ldxy AreaPalette,y ;based on area type
endif
SetVRAMAddr_A: stx VRAM_Buffer_AddrCtrl ;store offset into buffer control
NextSubtask: jmp IncSubtask ;move onto next task
;-------------------------------------------------------------------------------------
;$00 - used as temp counter in GetPlayerColors
BGColorCtrl_Addr:
.db $00, $09, $0a, $04
BackgroundColors:
if Z80BGCOLOR
db 0xcc,0xcc,0xff,0xff ;used by area type if bg color ctrl not set
db 0xff,0xcc,0xff,0xff ;used by background color control if set
else
.db $22, $22, $0f, $0f ;used by area type if bg color ctrl not set
.db $0f, $22, $0f, $0f ;used by background color control if set
endif
PlayerColors:
if Z80MARIOCOLOR
dw 0x3f3f,0xbdbd ;mario's colors
dw 0x3f3f,0xefef ;luigi's colors
dw 0x3f3f,0x0c0c ;fiery (used by both)
else
.db $22, $16, $27, $18 ;mario's colors
.db $22, $30, $27, $19 ;luigi's colors
.db $22, $37, $27, $16 ;fiery (used by both)
endif
GetBackgroundColor:
ldy BackgroundColorCtrl ;check background color control
checky
beq NoBGColor ;if not set, increment task and fetch palette
if Z80BGCOLOR
else
lday BGColorCtrl_Addr-4,y ;put appropriate palette into vram
sta VRAM_Buffer_AddrCtrl ;note that if set to 5-7, $0301 (VRAM_Buffer1) will not be read
endif
NoBGColor: inci ScreenRoutineTask ;increment to next subtask and plod on through
GetPlayerColors:
if Z80MARIOCOLOR
lda CurrentPlayer ;check which player is on the screen
ld de,PlayerColors
or a
beq ChkFiery
ld de,PlayerColors+4;ldyn ++$04 ;load offset for luigi
ChkFiery: lda PlayerStatus ;check player status
cmpn ++$02
bne StartClrGet ;if fiery, load alternate offset for fiery player
ld de,PlayerColors+8;ldyn ++$08
StartClrGet:
ld hl,(curpalette)
ld c,12*2
add hl,bc
ex de,hl
;c>=4
ldi
ldi
inc de
inc de
ldi
ldi
ld (oldpalette),hl ;!=curpalette, ўЄюс√ ртЄюьрЄшўхёъш юсэютшырё№
ld d,b
if Z80BGCOLOR
ldy BackgroundColorCtrl ;if this value is four or greater, it will be set
checky
bne SetBGColor ;therefore use it as offset to background color
ldy AreaType ;otherwise use area type bits from area offset as offset
SetBGColor: lday BackgroundColors,y ;to background color instead
;jr $
ld hl,(curpalette)
ld (hl),a
inc hl
ld (hl),a
endif
ret
else
ldx VRAM_Buffer1_Offset ;get current buffer offset
ldyn ++$00
lda CurrentPlayer ;check which player is on the screen
checka
beq ChkFiery
ldyn ++$04 ;load offset for luigi
ChkFiery: lda PlayerStatus ;check player status
cmpn ++$02
bne StartClrGet ;if fiery, load alternate offset for fiery player
ldyn ++$08
StartClrGet: ldan ++$03 ;do four colors
sta SCRATCHPAD+$00
ClrGetLoop: lday PlayerColors,y ;fetch player colors and store them
stax VRAM_Buffer1+3,x ;in the buffer
iny
inx
deci SCRATCHPAD+$00
bpl ClrGetLoop
ldx VRAM_Buffer1_Offset ;load original offset from before
ldy BackgroundColorCtrl ;if this value is four or greater, it will be set
checky
bne SetBGColor ;therefore use it as offset to background color
ldy AreaType ;otherwise use area type bits from area offset as offset
SetBGColor: lday BackgroundColors,y ;to background color instead
stax VRAM_Buffer1+3,x
ldan ++HIGH PPU_SPRPAL;++$3f ;set for sprite palette address
stax VRAM_Buffer1,x ;save to buffer
ldan ++LOW PPU_SPRPAL;++$10
stax VRAM_Buffer1+1,x
ldan ++$04 ;write length byte to buffer
stax VRAM_Buffer1+2,x
ldan ++$00 ;now the null terminator
stax VRAM_Buffer1+7,x
txa ;move the buffer pointer ahead 7 bytes
clc ;in case we want to write anything else later
adcn ++$07
endif
SetVRAMOffset: sta VRAM_Buffer1_Offset ;store as new vram buffer offset
rts
;-------------------------------------------------------------------------------------
GetAlternatePalette1:
lda AreaStyle ;check for mushroom level style
cmpn ++$01
bne NoAltPal
ldan ++$0b ;if found, load appropriate palette
SetVRAMAddr_B: sta VRAM_Buffer_AddrCtrl
NoAltPal: jmp IncSubtask ;now onto the next task
;-------------------------------------------------------------------------------------
WriteTopStatusLine:
ldan ++$00 ;select main status bar
jsr WriteGameText ;output it
jmp IncSubtask ;onto the next task
;-------------------------------------------------------------------------------------
WriteBottomStatusLine:
jsr GetSBNybbles ;write player's score and coin tally to screen
ldx VRAM_Buffer1_Offset
ldan ++$20 ;write address for world-area number on screen
stax VRAM_Buffer1,x
ldan ++$73
stax VRAM_Buffer1+1,x
ldan ++$03 ;write length for it
stax VRAM_Buffer1+2,x
ldy WorldNumber ;first the world number
iny
tya
stax VRAM_Buffer1+3,x
ldan ++$28 ;next the dash
stax VRAM_Buffer1+4,x
ldy LevelNumber ;next the level number
iny ;increment for proper number display
tya
stax VRAM_Buffer1+5,x
ldan ++$00 ;put null terminator on
stax VRAM_Buffer1+6,x
txa ;move the buffer offset up by 6 bytes
clc
adcn ++$06
sta VRAM_Buffer1_Offset
jmp IncSubtask
;-------------------------------------------------------------------------------------
DisplayTimeUp:
lda GameTimerExpiredFlag ;if game timer not expired, increment task
checka
beq NoTimeUp ;control 2 tasks forward, otherwise, stay here
ldan ++$00
sta GameTimerExpiredFlag ;reset timer expiration flag
ldan ++$02 ;output time-up screen to buffer
jmp OutputInter
NoTimeUp: inci ScreenRoutineTask ;increment control task 2 tasks forward
jmp IncSubtask
;-------------------------------------------------------------------------------------
DisplayIntermediate:
lda OperMode ;check primary mode of operation
checka
beq NoInter ;if in title screen mode, skip this
cmpn ++GameOverModeValue ;are we in game over mode?
beq GameOverInter ;if so, proceed to display game over screen
lda AltEntranceControl ;otherwise check for mode of alternate entry
checka
bne NoInter ;and branch if found
ldy AreaType ;check if we are on castle level
cpyn ++$03 ;and if so, branch (possibly residual)
beq PlayerInter
lda DisableIntermediate ;if this flag is set, skip intermediate lives display
checka
bne NoInter ;and jump to specific task, otherwise
PlayerInter: jsr DrawPlayer_Intermediate ;put player in appropriate place for
ldan ++$01 ;lives display, then output lives display to buffer
OutputInter: jsr WriteGameText
jsr ResetScreenTimer
ldan ++$00
sta DisableScreenFlag ;reenable screen output
rts
GameOverInter: ldan ++$12 ;set screen timer
sta ScreenTimer
ldan ++$03 ;output game over screen to buffer
jsr WriteGameText
jmp IncModeTask_B
NoInter: ldan ++$08 ;set for specific task (AreaParserTaskControl) and leave
sta ScreenRoutineTask
rts
;-------------------------------------------------------------------------------------
AreaParserTaskControl:
inci DisableScreenFlag ;turn off screen
TaskLoop: jsr AreaParserTaskHandler ;render column set of current area
lda AreaParserTaskNum ;check number of tasks
checka
bne TaskLoop ;if tasks still not all done, do another one
deci ColumnSets ;do we need to render more column sets?
bpl OutputCol
inci ScreenRoutineTask ;if not, move on to the next task
OutputCol: ldan ++$06 ;set vram buffer to output rendered column set
sta VRAM_Buffer_AddrCtrl ;on next NMI
rts
;-------------------------------------------------------------------------------------
;$00 - vram buffer address table low
;$01 - vram buffer address table high
DrawTitleScreen:
;jr $
lda OperMode ;are we in title screen mode?
checka
bne IncModeTask_B ;if not, exit
if Z80
DrawTitleScreen_go
ld hl,TitleScreen
ld de,VRAM_Buffer1_Offset
ld bc,TitleScreenDataSize ;0x13a = 314 (ёЄЁрээюх ўшёыю???)
ldir
ld d,b;0
else
ldan ++HIGH TitleScreenDataOffset ;load address $1ec0 into
sta PPU_ADDRESS ;the vram address register
ldan ++LOW TitleScreenDataOffset
sta PPU_ADDRESS
ldan ++$03 ;put address $0300 into
sta SCRATCHPAD+$01 ;the indirect at $00
ldyn ++$00
sty SCRATCHPAD+$00
lda PPU_DATA ;do one garbage read
OutputTScr: lda PPU_DATA ;get title screen from chr-rom
stayindirect (SCRATCHPAD+$00),y ;store 256 bytes into buffer
iny
bne ChkHiByte ;if not past 256 bytes, do not increment
inci SCRATCHPAD+$01 ;otherwise increment high byte of indirect
ChkHiByte: lda SCRATCHPAD+$01 ;check high byte?
cmpn ++$04 ;at $0400?
bne OutputTScr ;if not, loop back and do another
cpyn ++$3a ;check if offset points past end of data
cmpcy
bcc OutputTScr ;if not, loop back and do another
endif
ldan ++$05 ;set buffer transfer control to $0300,
jmp SetVRAMAddr_B ;increment task and exit
;-------------------------------------------------------------------------------------
ClearBuffersDrawIcon:
lda OperMode ;check game mode
checka
bne IncModeTask_B ;if not title screen mode, leave
ldxn ++$00 ;otherwise, clear buffer space
TScrClear: stax VRAM_Buffer1-1,x
stax VRAM_Buffer1-1+$100,x ;??? яюўхьє Єръ ьэюую?
dex
bne TScrClear
jsr DrawMushroomIcon ;draw player select icon
IncSubtask: inci ScreenRoutineTask ;move onto next task
rts
;-------------------------------------------------------------------------------------
WriteTopScore:
ldan ++$fa ;run display routine to display top score on title
jsr UpdateNumber
IncModeTask_B: inci OperMode_Task ;move onto next mode
rts
;-------------------------------------------------------------------------------------
GameText:
TopStatusBarLine:
.db $20, $43, $05, $16, $0a, $1b, $12, $18 ; "MARIO"
.db $20, $52, $0b, $20, $18, $1b, $15, $0d ; "WORLD TIME"
.db $24, $24, $1d, $12, $16, $0e
.db $20, $68, $05, $00, $24, $24, $2e, $29 ; score trailing digit and coin display
.db $23, $c0, $7f, $aa ; attribute table data, clears name table 0 to palette 2
.db $23, $c2, $01, $ea ; attribute table data, used for coin icon in status bar
.db $ff ; end of data block
WorldLivesDisplay:
.db $21, $cd, $07, $24, $24 ; cross with spaces used on
.db $29, $24, $24, $24, $24 ; lives display
.db $21, $4b, $09, $20, $18 ; "WORLD - " used on lives display
.db $1b, $15, $0d, $24, $24, $28, $24
.db $22, $0c, $47, $24 ; possibly used to clear time up
.db $23, $dc, $01, $ba ; attribute table data for crown if more than 9 lives
.db $ff
TwoPlayerTimeUp:
.db $21, $cd, $05, $16, $0a, $1b, $12, $18 ; "MARIO"
OnePlayerTimeUp:
.db $22, $0c, $07, $1d, $12, $16, $0e, $24, $1e, $19 ; "TIME UP"
.db $ff
TwoPlayerGameOver:
.db $21, $cd, $05, $16, $0a, $1b, $12, $18 ; "MARIO"
OnePlayerGameOver:
.db $22, $0b, $09, $10, $0a, $16, $0e, $24 ; "GAME OVER"
.db $18, $1f, $0e, $1b
.db $ff
WarpZoneWelcome:
.db $25, $84, $15, $20, $0e, $15, $0c, $18, $16 ; "WELCOME TO WARP ZONE!"
.db $0e, $24, $1d, $18, $24, $20, $0a, $1b, $19
.db $24, $23, $18, $17, $0e, $2b
.db $26, $25, $01, $24 ; placeholder for left pipe
.db $26, $2d, $01, $24 ; placeholder for middle pipe
.db $26, $35, $01, $24 ; placeholder for right pipe
.db $27, $d9, $46, $aa ; attribute data
.db $27, $e1, $45, $aa
.db $ff
LuigiName:
.db $15, $1e, $12, $10, $12 ; "LUIGI", no address or length
WarpZoneNumbers:
.db $04, $03, $02, $00 ; warp zone numbers, note spaces on middle
.db $24, $05, $24, $00 ; zone, partly responsible for
.db $08, $07, $06, $00 ; the minus world
GameTextOffsets:
.db TopStatusBarLine-GameText, TopStatusBarLine-GameText
.db WorldLivesDisplay-GameText, WorldLivesDisplay-GameText
.db TwoPlayerTimeUp-GameText, OnePlayerTimeUp-GameText
.db TwoPlayerGameOver-GameText, OnePlayerGameOver-GameText
.db WarpZoneWelcome-GameText, WarpZoneWelcome-GameText
WriteGameText:
pha ;save text number to stack
asl
tay ;multiply by 2 and use as offset
cpyn ++$04 ;if set to do top status bar or world/lives display,
cmpcy
bcc LdGameText ;branch to use current offset as-is
cpyn ++$08 ;if set to do time-up or game over,
cmpcy
bcc Chk2Players ;branch to check players
ldyn ++$08 ;otherwise warp zone, therefore set offset
Chk2Players: lda NumberOfPlayers ;check for number of players
checka
bne LdGameText ;if there are two, use current offset to also print name
iny ;otherwise increment offset by one to not print name
LdGameText: ldxy GameTextOffsets,y ;get offset to message we want to print
ldyn ++$00
GameTextLoop: ldax GameText,x ;load message data
cmpn ++$ff ;check for terminator
beq EndGameText ;branch to end text if found
stay VRAM_Buffer1,y ;otherwise write data to buffer
inx ;and increment increment
iny
bne GameTextLoop ;do this for 256 bytes if no terminator found
EndGameText: ldan ++$00 ;put null terminator at end
stay VRAM_Buffer1,y
pla ;pull original text number from stack
tax
cmpn ++$04 ;are we printing warp zone?
cmpcy
bcs PrintWarpZoneNumbers
dex ;are we printing the world/lives display?
bne CheckPlayerName ;if not, branch to check player's name
lda NumberofLives ;otherwise, check number of lives
clc ;and increment by one for display
adcn ++$01
cmpn ++10 ;more than 9 lives?
cmpcy
bcc PutLives
cmpcy
sbcn ++10 ;if so, subtract 10 and put a crown tile
ldyn ++$9f ;next to the difference...strange things happen if
sty VRAM_Buffer1+7 ;the number of lives exceeds 19
PutLives: sta VRAM_Buffer1+8
ldy WorldNumber ;write world and level numbers (incremented for display)
iny ;to the buffer in the spaces surrounding the dash
sty VRAM_Buffer1+19
ldy LevelNumber
iny
sty VRAM_Buffer1+21 ;we're done here
rts
CheckPlayerName:
lda NumberOfPlayers ;check number of players
checka
beq ExitChkName ;if only 1 player, leave
lda CurrentPlayer ;load current player
dex ;check to see if current message number is for time up
bne ChkLuigi
ldy OperMode ;check for game over mode
cpyn ++GameOverModeValue
beq ChkLuigi
eorn ++%00000001 ;if not, must be time up, invert d0 to do other player
ChkLuigi: lsr
bcc ExitChkName ;if mario is current player, do not change the name
ldyn ++$04
NameLoop: lday LuigiName,y ;otherwise, replace "MARIO" with "LUIGI"
stay VRAM_Buffer1+3,y
dey
bpl NameLoop ;do this until each letter is replaced
ExitChkName: rts
PrintWarpZoneNumbers:
;CY = 0???
or a
sbcn ++$04 ;subtract 4 and then shift to the left
asl ;twice to get proper warp zone number
asl ;offset
tax
ldyn ++$00
WarpNumLoop: ldax WarpZoneNumbers,x ;print warp zone numbers into the
stay VRAM_Buffer1+27,y ;placeholders from earlier
inx
iny ;put a number in every fourth space
iny
iny
iny
cpyn ++$0c
cmpcy
bcc WarpNumLoop
ldan ++$2c ;load new buffer pointer at end of message
jmp SetVRAMOffset
;-------------------------------------------------------------------------------------
ResetSpritesAndScreenTimer:
lda ScreenTimer ;check if screen timer has expired
checka
bne NoReset ;if not, branch to leave
jsr MoveAllSpritesOffscreen ;otherwise reset sprites now
ResetScreenTimer:
ldan ++$07 ;reset timer again
sta ScreenTimer
inci ScreenRoutineTask ;move onto next task
NoReset: rts
;-------------------------------------------------------------------------------------
;$00 - temp vram buffer offset
;$01 - temp metatile buffer offset
;$02 - temp metatile graphics table offset
;$03 - used to store attribute bits
;$04 - used to determine attribute table row
;$05 - used to determine attribute table column
;$06 - metatile graphics table address low
;$07 - metatile graphics table address high
RenderAreaGraphics:
if Z80OPT4
;lda CurrentColumnPos ;store LSB of where we're at
;andn ++$01
;sta SCRATCHPAD+$05
ldy VRAM_Buffer2_Offset ;store vram buffer offset
ld ix,VRAM_Buffer2
add ix,de
ld a,(CurrentNTAddr_Low) ;get current name table address we're supposed to render
ld (ix+1),a;stay VRAM_Buffer2+1,y
ld a,(CurrentNTAddr_High)
ld (ix+0),a;stay VRAM_Buffer2,y
ld a,++$9a ;store length byte of 26 here with d7 set
ld (ix+2),a;stay VRAM_Buffer2+2,y ;to increment by 32 (in columns)
ld bc,MetatileBuffer;ld c,0 ;row
ld hy,13
;row=0..12
DrawMTLoop:
;фы Z80 эх эєцэю ЇюЁьшЁютрЄ№ рЄЁшсєЄ√ - тёх Єрщы√ єцх яхЁхъЁр°хэ√
;ld hl,MetatileBuffer
;add hl,bc
ld a,(bc);(hl);ldax MetatileBuffer,x ;get first metatile number ;%xx000000 - attribute table bits, %00xxxxxx - metatile number
ld e,d;0
add a,a ;*2
rl e
add a,a ;*4
rl e ;e=0..3
ld hl,AreaParserTaskNum ;get current task number for level processing and
bit 0,(hl) ;get current task number for level processing and mask out all but LSB
jr nz,$+4
add a,2 ;multiply by 2, then add to the tile offset so we can draw either side of the metatiles
ld hl,MetatileGraphics_Low
add hl,de ;e=0..3
add a,(hl);l,(iy)
inc hl
inc hl
inc hl
inc hl
;ld e,a
;adc a,(hl)
;sub e
;ld h,a
;ld l,e ;23t
ld h,(hl);(iy+MetatileGraphics_High-MetatileGraphics_Low) ;get address to graphics table from here
jr nc,$+3
inc h
ld l,a ;20.5t
ld a,(hl);ldayindirect (SCRATCHPAD+$06),y ;get first tile number (top left or top right) and store
ld (ix+3),a
inc hl
ld a,(hl) ;now get the second (bottom left or bottom right) and store
ld (ix+4),a;stax VRAM_Buffer2+4,x
inc ix;inci SCRATCHPAD+$00 ;increment vram buffer offset by 2
inc ix;inci SCRATCHPAD+$00
inc bc ;next row
;ld a,c
dec hy ;cp $0d ;check for the bottom of the screen
jp nz,DrawMTLoop ;jp c,DrawMTLoop ;if not there yet, loop back
ld b,d;0
;шЄюую ёфтшэєыш vram buffer offset эр 13*2, ёфтшэхь х∙╕ эр 3 ш ёюїЁрэшь:
ld (ix+3),b;0 ;put null terminator at end of data for name table
ld a,(VRAM_Buffer2_Offset)
add a,13*2+3
ld (VRAM_Buffer2_Offset),a ;store new buffer offset
ld hl,CurrentNTAddr_Low
inc (hl);inci CurrentNTAddr_Low ;increment name table address low
ld a,(hl);lda CurrentNTAddr_Low ;check current low byte
and ++%00011111 ;if no wraparound, just skip this part
jr nz,ExitDrawM
;ld a,++$80 ;if wraparound occurs, make sure low byte stays
ld (hl),$80;sta CurrentNTAddr_Low ;just under the status bar
ld hl,CurrentNTAddr_High
ld a,(hl);lda CurrentNTAddr_High ;and then invert d2 of the name table address high
xor ++%00000100 ;to move onto the next appropriate name table
ld (hl),a;sta CurrentNTAddr_High
ExitDrawM: ;jmp SetVRAMCtrl ;jump to set buffer to $0341 (VRAM_Buffer2) and leave
ldan ++$06
sta VRAM_Buffer_AddrCtrl ;set buffer to $0341 (VRAM_Buffer2) and leave
ret
else ;~Z80
lda CurrentColumnPos ;store LSB of where we're at
andn ++$01
sta SCRATCHPAD+$05
ldy VRAM_Buffer2_Offset ;store vram buffer offset
sty SCRATCHPAD+$00
lda CurrentNTAddr_Low ;get current name table address we're supposed to render
stay VRAM_Buffer2+1,y
lda CurrentNTAddr_High
stay VRAM_Buffer2,y
ldan ++$9a ;store length byte of 26 here with d7 set
stay VRAM_Buffer2+2,y ;to increment by 32 (in columns)
ldan ++$00 ;init attribute row
sta SCRATCHPAD+$04 ;current attribute row
tax ;x=row
;x=row=0..12
DrawMTLoop:
stx SCRATCHPAD+$01 ;store init value of 0 or incremented offset for buffer
ldax MetatileBuffer,x ;get first metatile number, and mask out all but 2 MSB
andn ++%11000000
sta SCRATCHPAD+$03 ;store attribute table bits here
asl ;note that metatile format is:
rol ;%xx000000 - attribute table bits,
rol ;%00xxxxxx - metatile number
tay ;rotate bits to d1-d0 and use as offset here
lday MetatileGraphics_Low,y ;get address to graphics table from here
sta SCRATCHPAD+$06
lday MetatileGraphics_High,y
sta SCRATCHPAD+$07
ldax MetatileBuffer,x ;get metatile number again
asl ;multiply by 4 and use as tile offset
asl
sta SCRATCHPAD+$02
lda AreaParserTaskNum ;get current task number for level processing and
andn ++%00000001 ;mask out all but LSB, then invert LSB, multiply by 2
eorn ++%00000001 ;to get the correct column position in the metatile,
asl ;then add to the tile offset so we can draw either side
adci SCRATCHPAD+$02 ;of the metatiles
tay
ldx SCRATCHPAD+$00 ;use vram buffer offset from before as X
ldayindirect (SCRATCHPAD+$06),y
stax VRAM_Buffer2+3,x ;get first tile number (top left or top right) and store
iny
ldayindirect (SCRATCHPAD+$06),y ;now get the second (bottom left or bottom right) and store
stax VRAM_Buffer2+4,x
ldy SCRATCHPAD+$04 ;get current attribute row
lda SCRATCHPAD+$05 ;get LSB of current column where we're at, and
checka
bne RightCheck ;branch if set (clear = left attrib, set = right)
lda SCRATCHPAD+$01 ;get current row we're rendering
lsr ;branch if LSB set (clear = top left, set = bottom left)
bcs LLeft
roli SCRATCHPAD+$03 ;rotate attribute bits 3 to the left
roli SCRATCHPAD+$03 ;thus in d1-d0, for upper left square
roli SCRATCHPAD+$03
jmp SetAttrib
RightCheck: lda SCRATCHPAD+$01 ;get LSB of current row we're rendering
lsr ;branch if set (clear = top right, set = bottom right)
bcs NextMTRow
lsri SCRATCHPAD+$03 ;shift attribute bits 4 to the right
lsri SCRATCHPAD+$03 ;thus in d3-d2, for upper right square
lsri SCRATCHPAD+$03
lsri SCRATCHPAD+$03
jmp SetAttrib
LLeft: lsri SCRATCHPAD+$03 ;shift attribute bits 2 to the right
lsri SCRATCHPAD+$03 ;thus in d5-d4 for lower left square
NextMTRow: inci SCRATCHPAD+$04 ;move onto next attribute row
SetAttrib: lday AttributeBuffer,y ;get previously saved bits from before
orai SCRATCHPAD+$03 ;if any, and put new bits, if any, onto
stay AttributeBuffer,y ;the old, and store
inci SCRATCHPAD+$00 ;increment vram buffer offset by 2
inci SCRATCHPAD+$00
ldx SCRATCHPAD+$01 ;get current gfx buffer row, and check for
inx ;the bottom of the screen
cpxn ++$0d
cmpcy
bcc DrawMTLoop ;if not there yet, loop back
;шЄюую ёфтшэєыш vram buffer offset эр 13*2, ёфтшэхь х∙╕ эр 3 ш ёюїЁрэшь:
ldy SCRATCHPAD+$00 ;get current vram buffer offset, increment by 3
iny ;(for name table address and length bytes)
iny
iny
ldan ++$00
stay VRAM_Buffer2,y ;put null terminator at end of data for name table
sty VRAM_Buffer2_Offset ;store new buffer offset
inci CurrentNTAddr_Low ;increment name table address low
lda CurrentNTAddr_Low ;check current low byte
andn ++%00011111 ;if no wraparound, just skip this part
bne ExitDrawM
ldan ++$80 ;if wraparound occurs, make sure low byte stays
sta CurrentNTAddr_Low ;just under the status bar
lda CurrentNTAddr_High ;and then invert d2 of the name table address high
eorn ++%00000100 ;to move onto the next appropriate name table
sta CurrentNTAddr_High
ExitDrawM: jmp SetVRAMCtrl ;jump to set buffer to $0341 (VRAM_Buffer2) and leave
endif
;-------------------------------------------------------------------------------------
if Z80OPT4==0 ;фы Z80 эх эєцэю ЇюЁьшЁютрЄ№ рЄЁшсєЄ√ - тёх Єрщы√ єцх яхЁхъЁр°хэ√
;$00 - temp attribute table address high (big endian order this time!)
;$01 - temp attribute table address low
RenderAttributeTables:
lda CurrentNTAddr_Low ;get low byte of next name table address
andn ++%00011111 ;to be written to, mask out all but 5 LSB,
secsub ;subtract four
sbcn ++$04
cmpcy
if Z80
push af
endif
andn ++%00011111 ;mask out bits again and store
sta SCRATCHPAD+$01
if Z80
pop af
endif
lda CurrentNTAddr_High ;get high byte and branch if borrow not set
bcs SetATHigh
eorn ++%00000100 ;otherwise invert d2
SetATHigh: andn ++%00000100 ;mask out all other bits
oran ++$23 ;add $2300 to the high byte and store
sta SCRATCHPAD+$00
lda SCRATCHPAD+$01 ;get low byte - 4, divide by 4, add offset for
lsr ;attribute table and store
lsr
adcn ++$c0 ;we should now have the appropriate block of
sta SCRATCHPAD+$01 ;attribute table in our temp address
ldxn ++$00
ldy VRAM_Buffer2_Offset ;get buffer offset
AttribLoop: lda SCRATCHPAD+$00
stay VRAM_Buffer2,y ;store high byte of attribute table address
lda SCRATCHPAD+$01
clc ;get low byte, add 8 because we want to start
adcn ++$08 ;below the status bar, and store
stay VRAM_Buffer2+1,y
sta SCRATCHPAD+$01 ;also store in temp again
ldax AttributeBuffer,x ;fetch current attribute table byte and store
stay VRAM_Buffer2+3,y ;in the buffer
ldan ++$01
stay VRAM_Buffer2+2,y ;store length of 1 in buffer
lsr
stax AttributeBuffer,x ;clear current byte in attribute buffer
iny ;increment buffer offset by 4 bytes
iny
iny
iny
inx ;increment attribute offset and check to see
cpxn ++$07 ;if we're at the end yet
cmpcy
bcc AttribLoop
stay VRAM_Buffer2,y ;put null terminator at the end
sty VRAM_Buffer2_Offset ;store offset in case we want to do any more
SetVRAMCtrl: ldan ++$06
sta VRAM_Buffer_AddrCtrl ;set buffer to $0341 (VRAM_Buffer2) and leave
rts
endif
;-------------------------------------------------------------------------------------
;$00 - used as temporary counter in ColorRotation
ColorRotatePalette:
if Z80COINCYCLECOLOR
db 0xa1,0xa1,0xa1,0x31,0xf1,0x31
else
.db $27, $27, $27, $17, $07, $17
endif
BlankPalette:
.db $3f, $0c, $04, $ff, $ff, $ff, $ff, $00
;used based on area type
Palette3Data:
.db $0f, $07, $12, $0f
.db $0f, $07, $17, $0f
.db $0f, $07, $17, $1c
.db $0f, $07, $17, $00
ColorRotation:
lda FrameCounter ;get frame counter
andn ++$07 ;mask out all but three LSB
if Z80COINCYCLECOLOR
ret nz
;lda AreaType ;get area type
;asl ;multiply by 4 to get proper offset
;asl
;tay ;save as offset here
;lday Palette3Data,y ;fetch palette to be written based on area type
ldy ColorRotateOffset ;get color cycling offset
lday ColorRotatePalette,y
ld hl,(curpalette)
ld e,11*2
add hl,de
ld (hl),a
inc hl
ld (hl),a
inci ColorRotateOffset ;increment color cycling offset
lda ColorRotateOffset
cmpn ++$06 ;check to see if it's still in range
;cmpcy
ret c;bcc ExitColorRot ;if so, branch to leave
xor a;ldan ++$00
sta ColorRotateOffset ;otherwise, init to keep it in range
ret
else
bne ExitColorRot ;branch if not set to zero to do this every eighth frame
ldx VRAM_Buffer1_Offset ;check vram buffer offset
cpxn ++$31
cmpcy
bcs ExitColorRot ;if offset over 48 bytes, branch to leave
tay ;otherwise use frame counter's 3 LSB as offset here
GetBlankPal: lday BlankPalette,y ;get blank palette for palette 3
stax VRAM_Buffer1,x ;store it in the vram buffer
inx ;increment offsets
iny
cpyn ++$08
cmpcy
bcc GetBlankPal ;do this until all bytes are copied
ldx VRAM_Buffer1_Offset ;get current vram buffer offset
ldan ++$03
sta SCRATCHPAD+$00 ;set counter here
lda AreaType ;get area type
asl ;multiply by 4 to get proper offset
asl
tay ;save as offset here
GetAreaPal: lday Palette3Data,y ;fetch palette to be written based on area type
stax VRAM_Buffer1+3,x ;store it to overwrite blank palette in vram buffer
iny
inx
deci SCRATCHPAD+$00 ;decrement counter
bpl GetAreaPal ;do this until the palette is all copied
ldx VRAM_Buffer1_Offset ;get current vram buffer offset
ldy ColorRotateOffset ;get color cycling offset
lday ColorRotatePalette,y
stax VRAM_Buffer1+4,x ;get and store current color in second slot of palette
lda VRAM_Buffer1_Offset
clc ;add seven bytes to vram buffer offset
adcn ++$07
sta VRAM_Buffer1_Offset
inci ColorRotateOffset ;increment color cycling offset
lda ColorRotateOffset
cmpn ++$06 ;check to see if it's still in range
cmpcy
bcc ExitColorRot ;if so, branch to leave
ldan ++$00
sta ColorRotateOffset ;otherwise, init to keep it in range
ExitColorRot: rts ;leave
endif
;-------------------------------------------------------------------------------------
;$00 - temp store for offset control bit
;$01 - temp vram buffer offset
;$02 - temp store for vertical high nybble in block buffer routine
;$03 - temp adder for high byte of name table address
;$04, $05 - name table address low/high
;$06, $07 - block buffer address low/high
BlockGfxData:
.db $45, $45, $47, $47
.db $47, $47, $47, $47
.db $57, $58, $59, $5a
.db $24, $24, $24, $24 ;blank metatile
.db y26, y26, y26, y26 ;water/lava ;blank metatile for water
RemoveCoin_Axe:
ldyn ++$41 ;set low byte so offset points to $0341
ldan ++$03 ;load offset for default blank metatile
ldx AreaType ;check area type
checkx
bne WriteBlankMT ;if not water type, use offset
ldan ++$04 ;otherwise load offset for blank metatile used in water
WriteBlankMT: jsr PutBlockMetatile ;do a sub to write blank metatile to vram buffer
ldan ++$06
sta VRAM_Buffer_AddrCtrl ;set vram address controller to $0341 (VRAM_Buffer2) and leave
rts
ReplaceBlockMetatile:
jsr WriteBlockMetatile ;write metatile to vram buffer to replace block object
inci Block_ResidualCounter ;increment unused counter (residual code)
decx Block_RepFlag,x ;decrement flag (residual code)
rts ;leave
DestroyBlockMetatile:
ldan ++$00 ;force blank metatile if branched/jumped to this point
WriteBlockMetatile:
ldyn ++$03 ;load offset for blank metatile
cmpn ++$00 ;check contents of A for blank metatile
beq UseBOffset ;branch if found (unconditional if branched from 8a6b)
ldyn ++$00 ;load offset for brick metatile w/ line
cmpn ++$58
beq UseBOffset ;use offset if metatile is brick with coins (w/ line)
cmpn ++$51
beq UseBOffset ;use offset if metatile is breakable brick w/ line
iny ;increment offset for brick metatile w/o line
cmpn ++$5d
beq UseBOffset ;use offset if metatile is brick with coins (w/o line)
cmpn ++$52
beq UseBOffset ;use offset if metatile is breakable brick w/o line
iny ;if any other metatile, increment offset for empty block
UseBOffset: tya ;put Y in A
ldy VRAM_Buffer1_Offset ;get vram buffer offset
iny ;move onto next byte
jsr PutBlockMetatile ;get appropriate block data and write to vram buffer
MoveVOffset: dey ;decrement vram buffer offset
tya ;add 10 bytes to it
clc
adcn ++10
jmp SetVRAMOffset ;branch to store as new vram buffer offset
PutBlockMetatile:
stx SCRATCHPAD+$00 ;store control bit from SprDataOffset_Ctrl
sty SCRATCHPAD+$01 ;store vram buffer offset for next byte
asl
asl ;multiply A by four and use as X
tax
ldyn ++$20 ;load high byte for name table 0
lda SCRATCHPAD+$06 ;get low byte of block buffer pointer
cmpn ++$d0 ;check to see if we're on odd-page block buffer
cmpcy
bcc SaveHAdder ;if not, use current high byte
ldyn ++$24 ;otherwise load high byte for name table 1
SaveHAdder: sty SCRATCHPAD+$03 ;save high byte here
andn ++$0f ;mask out high nybble of block buffer pointer
asl ;multiply by 2 to get appropriate name table low byte
sta SCRATCHPAD+$04 ;and then store it here
ldan ++$00
sta SCRATCHPAD+$05 ;initialize temp high byte
lda SCRATCHPAD+$02 ;get vertical high nybble offset used in block buffer routine
clc
adcn ++$20 ;add 32 pixels for the status bar
asl
roli SCRATCHPAD+$05 ;shift and rotate d7 onto d0 and d6 into carry
asl
roli SCRATCHPAD+$05 ;shift and rotate d6 onto d0 and d5 into carry
adci SCRATCHPAD+$04 ;add low byte of name table and carry to vertical high nybble
sta SCRATCHPAD+$04 ;and store here
lda SCRATCHPAD+$05 ;get whatever was in d7 and d6 of vertical high nybble
adcn ++$00 ;add carry
clc
adci SCRATCHPAD+$03 ;then add high byte of name table
sta SCRATCHPAD+$05 ;store here
ldy SCRATCHPAD+$01 ;get vram buffer offset to be used
RemBridge: ldax BlockGfxData,x ;write top left and top right
stay VRAM_Buffer1+2,y ;tile numbers into first spot
ldax BlockGfxData+1,x
stay VRAM_Buffer1+3,y
ldax BlockGfxData+2,x ;write bottom left and bottom
stay VRAM_Buffer1+7,y ;right tiles numbers into
ldax BlockGfxData+3,x ;second spot
stay VRAM_Buffer1+8,y
lda SCRATCHPAD+$04
stay VRAM_Buffer1,y ;write low byte of name table
clc ;into first slot as read
adcn ++$20 ;add 32 bytes to value
stay VRAM_Buffer1+5,y ;write low byte of name table
lda SCRATCHPAD+$05 ;plus 32 bytes into second slot
stay VRAM_Buffer1-1,y ;write high byte of name
stay VRAM_Buffer1+4,y ;table address to both slots
ldan ++$02
stay VRAM_Buffer1+1,y ;put length of 2 in
stay VRAM_Buffer1+6,y ;both slots
ldan ++$00
stay VRAM_Buffer1+9,y ;put null terminator at end
ldx SCRATCHPAD+$00 ;get offset control bit here
rts ;and leave
;-------------------------------------------------------------------------------------
;METATILE GRAPHICS TABLE
MetatileGraphics_Low:
.db LOW Palette0_MTiles, LOW Palette1_MTiles, LOW Palette2_MTiles, LOW Palette3_MTiles
MetatileGraphics_High:
.db HIGH Palette0_MTiles, HIGH Palette1_MTiles, HIGH Palette2_MTiles, HIGH Palette3_MTiles
if Z80ATTR
xa0=0xec
xa1=0xed
xa2=0xee
xa3=0xef
x27=0xf0
xba=0xf1
xbb=0xf2
x86=0xf3
x87=0xf4
x8a=0xf5
x8b=0xf6
x8e=0xf7
x8f=0xf8
y25=0xf9
y26=0xfa
y35=0xfb
y36=0xfc
y37=0xfd
y38=0xfe
w26=10+('X'-'A')
;(water pipe bottom):
x91=10+('J'-'A');0x91
x92=10+('F'-'A');0x92
;шыш фы уюЁшчюэЄры№э√ї ЄЁєс ёЎхяшЄ№ фтр Єрщыр яю тхЁЄшърыш ш ■чрЄ№ ъръ юфшэ?
else
xa0=0xa0
xa1=0xa1
xa2=0xa2
xa3=0xa3
x27=0x27
xba=0xba
xbb=0xbb
x86=0x86
x87=0x87
x8a=0x8a
x8b=0x8b
x8e=0x8e
x8f=0x8f
y25=0x25
y26=0x26
y35=0x35
y36=0x36
y37=0x37
y38=0x38
w26=0x26
x91=0x91
x92=0x92
endif
Palette0_MTiles:
.db $24, $24, $24, $24 ;blank
.db $27, $27, $27, $27 ;black metatile
.db $24, $24, $24, $35 ;bush left
.db $36, $25, $37, $25 ;bush middle
.db $24, $38, $24, $24 ;bush right
.db $24, $30, $30, w26 ;mountain left
.db w26, w26, $34, w26 ;mountain left bottom/middle center
.db $24, $31, $24, $32 ;mountain middle top
.db $33, w26, $24, $33 ;mountain right
.db $34, w26, w26, w26 ;mountain right bottom
.db w26, w26, w26, w26 ;mountain middle bottom
.db $24, $c0, $24, $c0 ;bridge guardrail
.db $24, $7f, $7f, $24 ;chain
.db $b8, $ba, $b9, $bb ;tall tree top, top half
.db $b8, $bc, $b9, $bd ;short tree top
.db $ba, $bc, $bb, $bd ;tall tree top, bottom half
.db $60, $64, $61, $65 ;warp pipe end left, points up
.db $62, $66, $63, $67 ;warp pipe end right, points up
.db $60, $64, $61, $65 ;decoration pipe end left, points up
.db $62, $66, $63, $67 ;decoration pipe end right, points up
.db $68, $68, $69, $69 ;pipe shaft left
.db w26, w26, $6a, $6a ;pipe shaft right
.db $4b, $4c, $4d, $4e ;tree ledge left edge
.db $4d, $4f, $4d, $4f ;tree ledge middle
.db $4d, $4e, $50, $51 ;tree ledge right edge
.db $6b, $70, $2c, $2d ;mushroom left edge
.db $6c, $71, $6d, $72 ;mushroom middle
.db $6e, $73, $6f, $74 ;mushroom right edge
.db $86, $8a, $87, $8b ;sideways pipe end top ;$1c?
.db $88, $8c, $88, $8c ;sideways pipe shaft top
.db $89, $8d, $69, $69 ;sideways pipe joint top
.db $8e, $91, $8f, $92 ;sideways pipe end bottom
.db w26, $93, w26, $93 ;sideways pipe shaft bottom
.db $90, $94, $69, $69 ;sideways pipe joint bottom
.db $a4, $e9, $ea, $eb ;seaplant ;яюўхьє т 0-щ ярышЄЁх???
.db $24, $24, $24, $24 ;blank, used on bricks or blocks that are hit
.db $24, $2f, $24, $3d ;flagpole ball
.db $a2, $a2, $a3, $a3 ;flagpole shaft
.db $24, $24, $24, $24 ;blank, used in conjunction with vines
Palette1_MTiles:
.db xa2, xa2, xa3, xa3 ;vertical rope
.db $99, $24, $99, $24 ;horizontal rope
.db $24, xa2, $3e, $3f ;left pulley
.db $5b, $5c, $24, xa3 ;right pulley
.db $24, $24, $24, $24 ;blank used for balance rope
.db $9d, $47, $9e, $47 ;castle top
.db $47, $47, x27, x27 ;castle window left
.db $47, $47, $47, $47 ;castle brick wall
.db x27, x27, $47, $47 ;castle window right
.db $a9, $47, $aa, $47 ;castle top w/ brick
.db $9b, x27, $9c, x27 ;entrance top
.db x27, x27, x27, x27 ;entrance bottom
.db $52, $52, $52, $52 ;green ledge stump
.db $80, xa0, $81, xa1 ;fence
.db $be, $be, $bf, $bf ;tree trunk
.db $75, xba, $76, xbb ;mushroom stump top
.db xba, xba, xbb, xbb ;mushroom stump bottom
.db $45, $47, $45, $47 ;breakable brick w/ line
.db $47, $47, $47, $47 ;breakable brick
.db $45, $47, $45, $47 ;breakable brick (not used)
.db $b4, $b6, $b5, $b7 ;cracked rock terrain
.db $45, $47, $45, $47 ;brick with line (power-up)
.db $45, $47, $45, $47 ;brick with line (vine)
.db $45, $47, $45, $47 ;brick with line (star)
.db $45, $47, $45, $47 ;brick with line (coins)
.db $45, $47, $45, $47 ;brick with line (1-up)
.db $47, $47, $47, $47 ;brick (power-up)
.db $47, $47, $47, $47 ;brick (vine)
.db $47, $47, $47, $47 ;brick (star)
.db $47, $47, $47, $47 ;brick (coins)
.db $47, $47, $47, $47 ;brick (1-up)
.db $24, $24, $24, $24 ;hidden block (1 coin)
.db $24, $24, $24, $24 ;hidden block (1-up)
.db $ab, $ac, $ad, $ae ;solid block (3-d block)
.db $5d, $5e, $5d, $5e ;solid block (white wall)
.db $c1, $24, $c1, $24 ;bridge
.db $c6, $c8, $c7, $c9 ;bullet bill cannon barrel
.db $ca, $cc, $cb, $cd ;bullet bill cannon top
.db $2a, $2a, $40, $40 ;bullet bill cannon bottom
.db $24, $24, $24, $24 ;blank used for jumpspring
.db $24, $47, $24, $47 ;half brick used for jumpspring
.db $82, $83, $84, $85 ;solid block (water level, green rock)
.db $24, $47, $24, $47 ;half brick (???)
.db x86, x8a, x87, x8b ;water pipe top
.db x8e, x91, x8f, x92 ;water pipe bottom
.db $24, $2f, $24, $3d ;flag ball (residual object) ;эх яхЁхъЁр°штрхь???
Palette2_MTiles:
.db $24, $24, $24, y35 ;cloud left
.db y36, y25, y37, y25 ;cloud middle
.db $24, y38, $24, $24 ;cloud right
.db $24, $24, $39, $24 ;cloud bottom left
.db $3a, $24, $3b, $24 ;cloud bottom middle
.db $3c, $24, $24, $24 ;cloud bottom right
.db $41, y26, $41, y26 ;water/lava top
.db y26, y26, y26, y26 ;water/lava
.db $b0, $b1, $b2, $b3 ;cloud level terrain
.db $77, $79, $77, $79 ;bowser's bridge
Palette3_MTiles:
.db $53, $55, $54, $56 ;question block (coin)
.db $53, $55, $54, $56 ;question block (power-up)
.db $a5, $a7, $a6, $a8 ;coin
.db $c2, $c4, $c3, $c5 ;underwater coin ;яюўхьє т 3-хщ ярышЄЁх???
.db $57, $59, $58, $5a ;empty block
.db $7b, $7d, $7c, $7e ;axe
;-------------------------------------------------------------------------------------
;VRAM BUFFER DATA FOR LOCATIONS IN PRG-ROM
WaterPaletteData:
.db $3f, $00, $20
.db $0f, $15, $12, $25
.db $0f, $3a, $1a, $0f
.db $0f, $30, $12, $0f
.db $0f, $27, $12, $0f
.db $22, $16, $27, $18
.db $0f, $10, $30, $27
.db $0f, $16, $30, $27
.db $0f, $0f, $30, $10
.db $00
GroundPaletteData:
.db $3f, $00, $20
.db $0f, $29, $1a, $0f
.db $0f, $36, $17, $0f
.db $0f, $30, $21, $0f
.db $0f, $27, $17, $0f
.db $0f, $16, $27, $18
.db $0f, $1a, $30, $27
.db $0f, $16, $30, $27
.db $0f, $0f, $36, $17
.db $00
UndergroundPaletteData:
.db $3f, $00, $20
.db $0f, $29, $1a, $09
.db $0f, $3c, $1c, $0f
.db $0f, $30, $21, $1c
.db $0f, $27, $17, $1c
.db $0f, $16, $27, $18
.db $0f, $1c, $36, $17
.db $0f, $16, $30, $27
.db $0f, $0c, $3c, $1c
.db $00
CastlePaletteData:
.db $3f, $00, $20
.db $0f, $30, $10, $00
.db $0f, $30, $10, $00
.db $0f, $30, $16, $00
.db $0f, $27, $17, $00
.db $0f, $16, $27, $18
.db $0f, $1c, $36, $17
.db $0f, $16, $30, $27
.db $0f, $00, $30, $10
.db $00
DaySnowPaletteData:
.db $3f, $00, $04
.db $22, $30, $00, $10
.db $00
NightSnowPaletteData:
.db $3f, $00, $04
.db $0f, $30, $00, $10
.db $00
MushroomPaletteData:
.db $3f, $00, $04
.db $22, $27, $16, $0f
.db $00
BowserPaletteData:
.db $3f, $14, $04
.db $0f, $1a, $30, $27
.db $00
MarioThanksMessage:
;"THANK YOU MARIO!"
.db $25, $48, $10
.db $1d, $11, $0a, $17, $14, $24
.db $22, $18, $1e, $24
.db $16, $0a, $1b, $12, $18, $2b
.db $00
LuigiThanksMessage:
;"THANK YOU LUIGI!"
.db $25, $48, $10
.db $1d, $11, $0a, $17, $14, $24
.db $22, $18, $1e, $24
.db $15, $1e, $12, $10, $12, $2b
.db $00
MushroomRetainerSaved:
;"BUT OUR PRINCESS IS IN"
.db $25, $c5, $16
.db $0b, $1e, $1d, $24, $18, $1e, $1b, $24
.db $19, $1b, $12, $17, $0c, $0e, $1c, $1c, $24
.db $12, $1c, $24, $12, $17
;"ANOTHER CASTLE!"
.db $26, $05, $0f
.db $0a, $17, $18, $1d, $11, $0e, $1b, $24
.db $0c, $0a, $1c, $1d, $15, $0e, $2b, $00
PrincessSaved1:
;"YOUR QUEST IS OVER."
.db $25, $a7, $13
.db $22, $18, $1e, $1b, $24
.db $1a, $1e, $0e, $1c, $1d, $24
.db $12, $1c, $24, $18, $1f, $0e, $1b, $af
.db $00
PrincessSaved2:
;"WE PRESENT YOU A NEW QUEST."
.db $25, $e3, $1b
.db $20, $0e, $24
.db $19, $1b, $0e, $1c, $0e, $17, $1d, $24
.db $22, $18, $1e, $24, $0a, $24, $17, $0e, $20, $24
.db $1a, $1e, $0e, $1c, $1d, $af
.db $00
WorldSelectMessage1:
;"PUSH BUTTON B"
.db $26, $4a, $0d
.db $19, $1e, $1c, $11, $24
.db $0b, $1e, $1d, $1d, $18, $17, $24, $0b
.db $00
WorldSelectMessage2:
;"TO SELECT A WORLD"
.db $26, $88, $11
.db $1d, $18, $24, $1c, $0e, $15, $0e, $0c, $1d, $24
.db $0a, $24, $20, $18, $1b, $15, $0d
.db $00
;-------------------------------------------------------------------------------------
;$04 - address low to jump address
;$05 - address high to jump address
;$06 - jump address low
;$07 - jump address high
;a=function number
;addresses of functions follow the jsr
JumpEngine:
if Z80
add a,a
pop hl ;pull saved return address from stack
add a,l
ld l,a
adc a,h
sub l
ld h,a
ld a,(hl)
inc hl
ld h,(hl)
ld l,a
or a ;CY=0 ;??? ;эх яюьюурхЄ т 1-2
jp (hl)
else
asl ;shift bit from contents of A
tay
pla ;pull saved return address from stack
sta SCRATCHPAD+$04 ;save to indirect
pla
sta SCRATCHPAD+$05 ;($04..05) = return addr???
iny ;y = A*2 + 1???
ldayindirect (SCRATCHPAD+$04),y ;load pointer from indirect
sta SCRATCHPAD+$06 ;note that if an RTS is performed in next routine
iny ;y = A*2 + 2??? ;it will return to the execution before the sub
ldayindirect (SCRATCHPAD+$04),y ;that called this routine
sta SCRATCHPAD+$07
jmpindirect (SCRATCHPAD+$06) ;jump to the address we loaded
endif
;-------------------------------------------------------------------------------------
InitializeNameTables:
if Z80==0
lda PPU_STATUS ;reset flip-flop
lda Mirror_PPU_CTRL_REG1 ;load mirror of ppu reg $2000
oran ++%00010000 ;set sprites for first 4k and background for second 4k
andn ++%11110000 ;clear rest of lower nybble, leave higher alone
jsr WritePPUReg1
endif
ldan ++$24 ;set vram address to start of name table 1
jsr WriteNTAddr
ldan ++$20 ;and then set it to name table 0
WriteNTAddr:
if Z80
ld hx,a
ld lx,0
else
sta PPU_ADDRESS
ldan ++$00
sta PPU_ADDRESS
endif
ldxn ++$04 ;clear name table with blank tile #24
ldyn ++$c0
ldan ++$24
InitNTLoop:
if Z80
ld (ix),a
inc ix
else
sta PPU_DATA ;count out exactly 768 tiles
endif
dey
bne InitNTLoop
dex
bne InitNTLoop
ldyn ++64 ;now to clear the attribute table (with zero this time)
txa
sta VRAM_Buffer1_Offset ;init vram buffer 1 offset
sta VRAM_Buffer1 ;init vram buffer 1
InitATLoop:
if Z80
ld (ix),a
inc ix
else
sta PPU_DATA
endif
dey
bne InitATLoop
sta HorizontalScroll ;reset scroll variables
sta VerticalScroll
jmp InitScroll ;initialize scroll registers to zero
;-------------------------------------------------------------------------------------
;$00 - temp joypad bit
;bit - button (ZX key)
;7 - A (A)
;6 - B (S)
;5 - Select (Space)
;4 - Start (Enter)
;3 - Up (7)
;2 - Down (6)
;1 - Left (5)
;0 - Right (8)
ReadJoypads:
ldan ++$01 ;reset and clear strobe of joypad ports
sta JOYPAD_PORT
lsr
tax ;start with joypad 1's port
sta JOYPAD_PORT
jsr ReadPortBits
inx ;increment for joypad 2's port
ReadPortBits:
if Z80
push bc
if OSCALLS
OS_GETKEYMATRIX ;out: bcdehlix = яюыєЁ ф√ cs...space
else
ld bc,0x7ffe
in a,(c)
ld lx,a ;lx=%???bnmS_
ld b,0xbf
in a,(c)
ld hx,a ;hx=%???hjklE
ld b,0xdf
in l,(c) ;l=%???yuiop
ld b,0xef
in h,(c) ;h=%???67890
ld b,0xf7
in e,(c) ;e=%???54321
ld b,0xfb
in d,(c) ;d=%???trewq
ld a,0xfd
in a,(0xfe);c=%???gfdsa
ld b,c;0xfe
in b,(c) ;b=%???vcxzC
ld c,a
endif
ld a,lx
or b
rra
jp nc,quit
if DEMO
bit 3,b ;'c'
call z,democontinue
bit 2,c ;'d'
call z,demooff
endif
rr c ;'a'
rla ;A
rr c ;'s'
rla ;B
ld c,lx
rr c ;'Space'
rla ;Select
ld c,hx
rr c ;'Enter'
rla ;Start
add a,a
bit 3,h ;7
jr z,$+3
inc a ;Up
add a,a
bit 4,h ;6
jr z,$+3
inc a ;Down
add a,a
bit 4,e ;5
jr z,$+3
inc a ;Left
add a,a
bit 2,h ;8
jr z,$+3
inc a ;Right
cpl
if DEMO
pop de ;x = joypad number
push de
dec e ;(фы joy1/2)
push bc
call nz,readdemo ;юэю цх writedemo (фы joy2)
pop bc
push af
bit 4,b ;'v'
call z,savedemo
pop af
endif
ld d,0
pop bc ;ld bc,0 ;x = joypad number
else
ldyn ++$08
PortLoop: pha ;push previous bit onto stack
ldax JOYPAD_PORT,x ;read current bit on joypad port
sta SCRATCHPAD+$00 ;check d1 and d0 of port output
lsr ;this is necessary on the old
orai SCRATCHPAD+$00 ;famicom systems in japan
lsr
;pla ;read bits from stack
;rol ;rotate bit from carry flag
plarol
dey
bne PortLoop ;count down bits left
endif
stax SavedJoypadBits,x ;save controller status here always
pha
andn ++%00110000 ;check for select or start
andx JoypadBitMask,x ;if neither saved state nor current state
beq Save8Bits ;have any of these two set, branch
pla
andn ++%11001111 ;otherwise store without select
stax SavedJoypadBits,x ;or start bits and leave
rts
Save8Bits: pla
stax JoypadBitMask,x ;save with all bits in another place and leave ;шёяюы№чєхЄё Єюы№ъю т ¤Єющ яЁюЎхфєЁх
rts
;-------------------------------------------------------------------------------------
;$00 - vram buffer address table low
;$01 - vram buffer address table high
;т сєЇхЁх:
;addrH, addrL, [SRLLLLLL (S=inc32, R=repeat, L=length),] S0BBBBBB?
if Z80
UpdateScreen:
ld hl,(SCRATCHPAD+$00)
UpdateScreen0:
ld a,(hl)
or a
ret z
;cp 0x40
;jr nc,$ ;ъюёЄ√ы№ - яюўхьє т яюЄюъх чэрўхэш $42???
ld d,a
inc hl
ld e,(hl) ;load next byte (second)
inc hl
ld a,(hl) ;load next byte (third)
ex de,hl
add a,a
ld c,32
jr c,$+4
ld c,1 ;if d7 of third byte was clear, ppu will only increment by 1
add a,a
jr nc,GetLength ;if d6 of third byte was clear, do not repeat byte
srl a
rra
ld lx,a
inc de
ld a,(de)
RepeatByte0:
ld (hl),a
add hl,bc
dec lx
jp nz,RepeatByte0
jp OutputToVRAM_q
GetLength:
;CY=0
rra ;shift back to the right to get proper length
rra ;note that d1 will now be in carry
ld lx,a
OutputToVRAM0:
inc de
ld a,(de)
ld (hl),a
add hl,bc
dec lx
jp nz,OutputToVRAM0
OutputToVRAM_q
ex de,hl
ld d,b;0
inc hl
jp UpdateScreen0
else
WriteBufferToScreen:
;a=addrH
;y=0
sta PPU_ADDRESS ;store high byte of vram address
iny
ldayindirect (SCRATCHPAD+$00),y ;load next byte (second)
sta PPU_ADDRESS ;store low byte of vram address
iny
ldayindirect (SCRATCHPAD+$00),y ;load next byte (third)
asl ;shift to left and save in stack
pha
lda Mirror_PPU_CTRL_REG1 ;load mirror of $2000,
oran ++%00000100 ;set ppu to increment by 32 by default
bcs SetupWrites ;if d7 of third byte was clear, ppu will
andn ++%11111011 ;only increment by 1
SetupWrites: jsr WritePPUReg1 ;write to register
pla ;pull from stack and shift to left again
asl
bcc GetLength ;if d6 of third byte was clear, do not repeat byte
oran ++%00000010 ;otherwise set d1 and increment Y
iny
GetLength: lsr ;shift back to the right to get proper length
lsr ;note that d1 will now be in carry
tax
OutputToVRAM: bcs RepeatByte ;if carry set, repeat loading the same byte
iny ;otherwise increment Y to load next byte
RepeatByte: ldayindirect (SCRATCHPAD+$00),y ;load more data from buffer and write to vram
sta PPU_DATA
dex ;done writing?
bne OutputToVRAM
sec
tya ;a=end shift
adci SCRATCHPAD+$00 ;add end length plus one to the indirect at $00
sta SCRATCHPAD+$00 ;to allow this routine to read another set of updates
ldan ++$00
adci SCRATCHPAD+$01
sta SCRATCHPAD+$01
ldan ++$3f ;sets vram address to $3f00 (ярышЄЁр Їюэр 16 срщЄ, ярышЄЁр ёяЁрщЄют 16 срщЄ) ;чрўхь???
sta PPU_ADDRESS
ldan ++$00
sta PPU_ADDRESS
sta PPU_ADDRESS ;then reinitializes it for some reason ;чрўхь ЄхяхЁ№ т 0???
sta PPU_ADDRESS
UpdateScreen:
ldx PPU_STATUS ;reset flip-flop
ldyn ++$00 ;load first byte from indirect as a pointer
ldayindirect (SCRATCHPAD+$00),y
checka
bne WriteBufferToScreen ;if byte is zero we have no further updates to make here
endif
InitScroll: sta PPU_SCROLL_REG_H ;store contents of A into scroll registers
sta PPU_SCROLL_REG_V ;and end whatever subroutine led us here
rts
;-------------------------------------------------------------------------------------
if Z80==0
WritePPUReg1:
sta PPU_CTRL_REG1 ;write contents of A to PPU register 1
sta Mirror_PPU_CTRL_REG1 ;and its mirror
rts
endif
;-------------------------------------------------------------------------------------
;$00 - used to store status bar nybbles
;$02 - used as temp vram offset
;$03 - used to store length of status bar number
;status bar name table offset and length data
StatusBarData:
.db $f0, $06 ; top score display on title screen
.db $62, $06 ; player score
.db $62, $06
.db $6d, $02 ; coin tally
.db $6d, $02
.db $7a, $03 ; game timer
StatusBarOffset:
.db $06, $0c, $12, $18, $1e, $24
PrintStatusBarNumbers:
sta SCRATCHPAD+$00 ;store player-specific offset
jsr OutputNumbers ;use first nybble to print the coin display
lda SCRATCHPAD+$00 ;move high nybble to low
lsr ;and print to score display
lsr
lsr
lsr
OutputNumbers:
clc ;add 1 to low nybble
adcn ++$01
andn ++%00001111 ;mask out high nybble
cmpn ++$06
cmpcy
bcs ExitOutputN
pha ;save incremented value to stack for now and
asl ;shift to left and use as offset
tay
ldx VRAM_Buffer1_Offset ;get current buffer pointer
ldan ++$20 ;put at top of screen by default
cpyn ++$00 ;are we writing top score on title screen?
bne SetupNums
ldan ++$22 ;if so, put further down on the screen
SetupNums: stax VRAM_Buffer1,x
lday StatusBarData,y ;write low vram address and length of thing
stax VRAM_Buffer1+1,x ;we're printing to the buffer
lday StatusBarData+1,y
stax VRAM_Buffer1+2,x
sta SCRATCHPAD+$03 ;save length byte in counter
stx SCRATCHPAD+$02 ;and buffer pointer elsewhere for now
pla ;pull original incremented value from stack
tax
ldax StatusBarOffset,x ;load offset to value we want to write
secsub
sbcy StatusBarData+1,y ;subtract from length byte we read before
tay ;use value as offset to display digits
ldx SCRATCHPAD+$02
DigitPLoop: lday DisplayDigits,y ;write digits to the buffer
stax VRAM_Buffer1+3,x
inx
iny
deci SCRATCHPAD+$03 ;do this until all the digits are written
bne DigitPLoop
ldan ++$00 ;put null terminator at end
stax VRAM_Buffer1+3,x
inx ;increment buffer pointer by 3
inx
inx
stx VRAM_Buffer1_Offset ;store it in case we want to use it again
ExitOutputN: rts
;-------------------------------------------------------------------------------------
;y=ёьх∙хэшх рфЁхёр
DigitsMathRoutine:
lda OperMode ;check mode of operation
cmpn ++TitleScreenModeValue
beq EraseDMods ;if in title screen mode, branch to lock score
ldxn ++$05
AddModLoop: ldax DigitModifier,x ;load digit amount to increment
clc
adcy DisplayDigits,y ;add to current digit
bmi BorrowOne ;if result is a negative number, branch to subtract
cmpn ++10
cmpcy
bcs CarryOne ;if digit greater than $09, branch to add
StoreNewD: stay DisplayDigits,y ;store as new score or game timer digit
dey ;move onto next digits in score or game timer
dex ;and digit amounts to increment
bpl AddModLoop ;loop back if we're not done yet
EraseDMods: ldan ++$00 ;store zero here
ldxn ++$06 ;start with the last digit
EraseMLoop: stax DigitModifier-1,x ;initialize the digit amounts to increment
dex
bpl EraseMLoop ;do this until they're all reset, then leave
rts
BorrowOne: decx DigitModifier-1,x ;decrement the previous digit, then put $09 in
ldan ++$09 ;the game timer digit we're currently on to "borrow
checka
bne StoreNewD ;the one", then do an unconditional branch back
CarryOne: secsub ;яхЁхэюё т т√ўшЄрэшш шэтхЁёэ√щ??? ;subtract ten from our digit to make it a
sbcn ++10 ;proper BCD number, then increment the digit
incx DigitModifier-1,x ;preceding current digit to "carry the one" properly
jmp StoreNewD ;go back to just after we branched here
;-------------------------------------------------------------------------------------
UpdateTopScore:
ldxn ++$05 ;start with mario's score
jsr TopScoreCheck
ldxn ++$0b ;now do luigi's score
TopScoreCheck:
ldyn ++$05 ;start with the lowest digit
secsub ;яхЁхэюё т т√ўшЄрэшш шэтхЁёэ√щ???
GetScoreDiff: ldax PlayerScoreDisplay,x ;subtract each player digit from each high score digit
if Z80
secsub
endif
sbcy TopScoreDisplay,y ;from lowest to highest, if any top score digit exceeds
dex ;any player digit, borrow will be set until a subsequent
dey ;subtraction clears it (player digit is higher than top)
bpl GetScoreDiff
cmpcy
bcc NoTopSc ;check to see if borrow is still set, if so, no new high score
inx ;increment X and Y once to the start of the score
iny
CopyScore: ldax PlayerScoreDisplay,x ;store player's score digits into high score memory area
stay TopScoreDisplay,y
inx
iny
cpyn ++$06 ;do this until we have stored them all
cmpcy
bcc CopyScore
NoTopSc: rts
;-------------------------------------------------------------------------------------
DefaultSprOffsets:
.db $04, $30, $48, $60, $78, $90, $a8, $c0
.db $d8, $e8, $24, $f8, $fc, $28, $2c
Sprite0Data:
.db $18, $ff, $23, $58
;-------------------------------------------------------------------------------------
InitializeGame:
ldyn16 ++GAMEDATA_end-AREADATA;++$6f ;clear all memory as in initialization procedure,
jsr InitializeMemory ;but this time, clear only as far as $076f
ldyn ++$1f
ClrSndLoop: stay SoundMemory,y ;clear out memory used
dey ;by the sound engines
bpl ClrSndLoop
ldan ++$18 ;set demo timer
sta DemoTimer
jsr LoadAreaPointer
InitializeArea:
ldyn16 ++AREADATA_end-AREADATA;++$4b ;clear all memory again, only as far as $074b
jsr InitializeMemory ;this is only necessary if branching from
ldxn ++$21
ldan ++$00
ClrTimersLoop: stax Timers,x ;clear out memory between
dex ;$0780 and $07a1
bpl ClrTimersLoop
lda HalfwayPage
ldy AltEntranceControl ;if AltEntranceControl not set, use halfway page, if any found
checky
beq StartPage
lda EntrancePage ;otherwise use saved entry page number here
StartPage: sta ScreenLeft_PageLoc ;set as value here
sta CurrentPageLoc ;also set as current page
sta BackloadingFlag ;set flag here if halfway page or saved entry page number found
jsr GetScreenPosition ;get pixel coordinates for screen borders
ldyn ++$20 ;if on odd numbered page, use $2480 as start of rendering
andn ++%00000001 ;otherwise use $2080, this address used later as name table
beq SetInitNTHigh ;address for rendering of game area
ldyn ++$24
SetInitNTHigh: sty CurrentNTAddr_High ;store name table address
ldyn ++$80
sty CurrentNTAddr_Low
asl ;store LSB of page number in high nybble
asl ;of block buffer column position
asl
asl
sta BlockBufferColumnPos
deci AreaObjectLength ;set area object lengths for all empty
deci AreaObjectLength+1
deci AreaObjectLength+2
ldan ++$0b ;set value for renderer to update 12 column sets
sta ColumnSets ;12 column sets = 24 metatile columns = 1 1/2 screens
jsr GetAreaDataAddrs ;get enemy and level addresses and load header
lda PrimaryHardMode ;check to see if primary hard mode has been activated
checka
bne SetSecHard ;if so, activate the secondary no matter where we're at
lda WorldNumber ;otherwise check world number
cmpn ++World5 ;if less than 5, do not activate secondary
cmpcy
bcc CheckHalfway
bne SetSecHard ;if not equal to, then world HIGH 5, thus activate
lda LevelNumber ;otherwise, world 5, so check level number
cmpn ++Level3 ;if 1 or 2, do not set secondary hard mode flag
cmpcy
bcc CheckHalfway
SetSecHard: inci SecondaryHardMode ;set secondary hard mode flag for areas 5-3 and beyond
CheckHalfway: lda HalfwayPage
checka
beq DoneInitArea
ldan ++$02 ;if halfway page set, overwrite start position from header
sta PlayerEntranceCtrl
DoneInitArea: ldan ++Silence ;silence music
sta AreaMusicQueue
ldan ++$01 ;disable screen output
sta DisableScreenFlag
inci OperMode_Task ;increment one of the modes
rts
;-------------------------------------------------------------------------------------
PrimaryGameSetup:
ldan ++$01
sta FetchNewGameTimerFlag ;set flag to load game timer from header
sta PlayerSize ;set player's size to small
ldan ++$02
sta NumberofLives ;give each player three lives
sta OffScr_NumberofLives
SecondaryGameSetup:
ldan ++$00
sta DisableScreenFlag ;enable screen output
tay
ClearVRLoop: stay VRAM_Buffer1-1,y ;clear buffer at $0300-$03ff
iny
bne ClearVRLoop
sta GameTimerExpiredFlag ;clear game timer exp flag
sta DisableIntermediate ;clear skip lives display flag
sta BackloadingFlag ;clear value here
ldan ++$ff
sta BalPlatformAlignment ;initialize balance platform assignment flag
lda ScreenLeft_PageLoc ;get left side page location
lsri Mirror_PPU_CTRL_REG1 ;shift LSB of ppu register #1 mirror out
if Z80==0
andn ++$01 ;mask out all but LSB of page location
endif
ror ;rotate LSB of page location into carry then onto mirror
roli Mirror_PPU_CTRL_REG1 ;this is to set the proper PPU name table
jsr GetAreaMusic ;load proper music into queue
ldan ++$38 ;load sprite shuffle amounts to be used later
sta SprShuffleAmt+2
ldan ++$48
sta SprShuffleAmt+1
ldan ++$58
sta SprShuffleAmt
ldxn ++$0e ;load default OAM offsets into $06e4-$06f2
ShufAmtLoop: ldax DefaultSprOffsets,x
stax SprDataOffset,x
dex ;do this until they're all set
bpl ShufAmtLoop
ldyn ++$03 ;set up sprite #0
ISpr0Loop: lday Sprite0Data,y
stay Sprite_Data,y
dey
bpl ISpr0Loop
jsr DoNothing2 ;these jsrs doesn't do anything useful
jsr DoNothing1
inci Sprite0HitDetectFlag ;set sprite #0 check flag ;ЄхяхЁ№ сєфхЄ ЁрсюЄрЄ№ ёяышЄёъЁшэ ёю ёъЁюыыюь
inci OperMode_Task ;increment to next task
rts
;-------------------------------------------------------------------------------------
;$06 - RAM address low
;$07 - RAM address high
;y = endaddr
;out: a=0
InitializeMemory:
if Z80
push de
ld hl,AREADATA
ld de,AREADATA+1
pop bc
dec bc
xor a
ld (hl),a
ldir
ld d,b;0
else
ldxn ++$07 ;set initial high byte to $0700-$07ff
ldan ++$00 ;set initial low byte to start of page (at $00 of page)
sta SCRATCHPAD+$06
InitPageLoop: stx SCRATCHPAD+$07
InitByteLoop: cpxn ++$01 ;check to see if we're on the stack ($0100-$01ff)
bne InitByte ;if not, go ahead anyway
cpyn ++$60 ;otherwise, check to see if we're at $0160-$01ff
cmpcy
bcs SkipByte ;if so, skip write
InitByte: stayindirect (SCRATCHPAD+$06),y ;otherwise, initialize byte with current low byte in Y
SkipByte: dey
cpyn ++$ff ;do this until all bytes in page have been erased
bne InitByteLoop
dex ;go onto the next page
bpl InitPageLoop ;do this until all pages of memory have been erased
endif
rts
;-------------------------------------------------------------------------------------
MusicSelectData:
.db WaterMusic, GroundMusic, UndergroundMusic, CastleMusic
.db CloudMusic, PipeIntroMusic
GetAreaMusic:
lda OperMode ;if in title screen mode, leave
checka
beq ExitGetM
lda AltEntranceControl ;check for specific alternate mode of entry
cmpn ++$02 ;if found, branch without checking starting position
beq ChkAreaType ;from area object data header
ldyn ++$05 ;select music for pipe intro scene by default
lda PlayerEntranceCtrl ;check value from level header for certain values
cmpn ++$06
beq StoreMusic ;load music for pipe intro scene if header
cmpn ++$07 ;start position either value $06 or $07
beq StoreMusic
ChkAreaType: ldy AreaType ;load area type as offset for music bit
lda CloudTypeOverride
checka
beq StoreMusic ;check for cloud type override
ldyn ++$04 ;select music for cloud type level if found
StoreMusic: lday MusicSelectData,y ;otherwise select appropriate music for level type
sta AreaMusicQueue ;store in queue and leave
ExitGetM: rts
;-------------------------------------------------------------------------------------
PlayerStarting_X_Pos:
.db $28, $18
.db $38, $28
AltYPosOffset:
.db $08, $00
PlayerStarting_Y_Pos:
.db $00, $20, $b0, $50, $00, $00, $b0, $b0
.db $f0
PlayerBGPriorityData:
.db $00, $20, $00, $00, $00, $00, $00, $00
GameTimerData:
.db $20 ;dummy byte, used as part of bg priority data
.db $04, $03, $02
Entrance_GameTimerSetup:
lda ScreenLeft_PageLoc ;set current page for area objects
sta Player_PageLoc ;as page location for player
ldan ++$28 ;store value here
sta VerticalForceDown ;for fractional movement downwards if necessary
ldan ++$01 ;set high byte of player position and
sta PlayerFacingDir ;set facing direction so that player faces right
sta Player_Y_HighPos
ldan ++$00 ;set player state to on the ground by default
sta Player_State
deci Player_CollisionBits ;initialize player's collision bits
ldyn ++$00 ;initialize halfway page
sty HalfwayPage
lda AreaType ;check area type
checka
bne ChkStPos ;if water type, set swimming flag, otherwise do not set
iny
ChkStPos: sty SwimmingFlag
ldx PlayerEntranceCtrl ;get starting position loaded from header
ldy AltEntranceControl ;check alternate mode of entry flag for 0 or 1
checky
beq SetStPos
cpyn ++$01
beq SetStPos
ldxy AltYPosOffset-2,y ;if not 0 or 1, override $0710 with new offset in X
SetStPos: lday PlayerStarting_X_Pos,y ;load appropriate horizontal position
sta Player_X_Position ;and vertical positions for the player, using
ldax PlayerStarting_Y_Pos,x ;AltEntranceControl as offset for horizontal and either $0710
sta Player_Y_Position ;or value that overwrote $0710 as offset for vertical
ldax PlayerBGPriorityData,x
sta Player_SprAttrib ;set player sprite attributes using offset in X
jsr GetPlayerColors ;get appropriate player palette
ldy GameTimerSetting ;get timer control value from header
checky
beq ChkOverR ;if set to zero, branch (do not use dummy byte for this)
lda FetchNewGameTimerFlag ;do we need to set the game timer? if not, use
checka
beq ChkOverR ;old game timer setting
lday GameTimerData,y ;if game timer is set and game timer flag is also set,
sta GameTimerDisplay ;use value of game timer control for first digit of game timer
ldan ++$01
sta GameTimerDisplay+2 ;set last digit of game timer to 1
lsr
sta GameTimerDisplay+1 ;set second digit of game timer
sta FetchNewGameTimerFlag ;clear flag for game timer reset
sta StarInvincibleTimer ;clear star mario timer
ChkOverR: ldy JoypadOverride ;if controller bits not set, branch to skip this part
checky
beq ChkSwimE
ldan ++$03 ;set player state to climbing
sta Player_State
ldxn ++$00 ;set offset for first slot, for block object
jsr InitBlock_XY_Pos
ldan ++$f0 ;set vertical coordinate for block object
sta Block_Y_Position
ldxn ++$05 ;set offset in X for last enemy object buffer slot
ldyn ++$00 ;set offset in Y for object coordinates used earlier
jsr Setup_Vine ;do a sub to grow vine
ChkSwimE: ldy AreaType ;if level not water-type,
checky
bne SetPESub ;skip this subroutine
jsr SetupBubble ;otherwise, execute sub to set up air bubbles
SetPESub: ldan ++$07 ;set to run player entrance subroutine
sta GameEngineSubroutine ;on the next frame of game engine
rts
;-------------------------------------------------------------------------------------
;page numbers are in order from -1 to -4
HalfwayPageNybbles:
.db $56, $40
.db $65, $70
.db $66, $40
.db $66, $40
.db $66, $40
.db $66, $60
.db $65, $70
.db $00, $00
PlayerLoseLife:
inci DisableScreenFlag ;disable screen and sprite 0 check
ldan ++$00
sta Sprite0HitDetectFlag
ldan ++Silence ;silence music
sta EventMusicQueue
if INFINITELIVES
xor a
else
deci NumberofLives ;take one life from player
endif
bpl StillInGame ;if player still has lives, branch
ldan ++$00
sta OperMode_Task ;initialize mode task,
ldan ++GameOverModeValue ;switch to game over mode
sta OperMode ;and leave
rts
StillInGame: lda WorldNumber ;multiply world number by 2 and use
asl ;as offset
tax
lda LevelNumber ;if in area -3 or -4, increment
andn ++$02 ;offset by one byte, otherwise
beq GetHalfway ;leave offset alone
inx
GetHalfway: ldyx HalfwayPageNybbles,x ;get halfway page number with offset
lda LevelNumber ;check area number's LSB
lsr
tya ;if in area -2 or -4, use lower nybble
bcs MaskHPNyb
lsr ;move higher nybble to lower if area
lsr ;number is -1 or -3
lsr
lsr
MaskHPNyb: andn ++%00001111 ;mask out all but lower nybble
cmpi ScreenLeft_PageLoc
cmpcy
beq SetHalfway ;left side of screen must be at the halfway page,
bcc SetHalfway ;otherwise player must start at the
ldan ++$00 ;beginning of the level
SetHalfway: sta HalfwayPage ;store as halfway page for player
jsr TransposePlayers ;switch players around if 2-player game
jmp ContinueGame ;continue the game
;-------------------------------------------------------------------------------------
GameOverMode:
lda OperMode_Task
jsr JumpEngine
.dw SetupGameOver
.dw ScreenRoutines
.dw RunGameOver
;-------------------------------------------------------------------------------------
SetupGameOver:
ldan ++$00 ;reset screen routine task control for title screen, game,
sta ScreenRoutineTask ;and game over modes
sta Sprite0HitDetectFlag ;disable sprite 0 check
ldan ++GameOverMusic
sta EventMusicQueue ;put game over music in secondary queue
inci DisableScreenFlag ;disable screen output
inci OperMode_Task ;set secondary mode to 1
rts
;-------------------------------------------------------------------------------------
RunGameOver:
ldan ++$00 ;reenable screen
sta DisableScreenFlag
lda SavedJoypad1Bits ;check controller for start pressed
andn ++Start_Button
bne TerminateGame
lda ScreenTimer ;if not pressed, wait for
checka
bne GameIsOn ;screen timer to expire
TerminateGame:
ldan ++Silence ;silence music
sta EventMusicQueue
jsr TransposePlayers ;check if other player can keep
bcc ContinueGame ;going, and do so if possible
lda WorldNumber ;otherwise put world number of current
sta ContinueWorld ;player into secret continue function variable
ldan ++$00
asl ;residual ASL instruction
sta OperMode_Task ;reset all modes to title screen and
sta ScreenTimer ;leave
sta OperMode
rts
ContinueGame:
jsr LoadAreaPointer ;update level pointer with
ldan ++$01 ;actual world and area numbers, then
sta PlayerSize ;reset player's size, status, and
inci FetchNewGameTimerFlag ;set game timer flag to reload
ldan ++$00 ;game timer from header
sta TimerControl ;also set flag for timers to count again
sta PlayerStatus
sta GameEngineSubroutine ;reset task for game core
sta OperMode_Task ;set modes and leave
ldan ++$01 ;if in game over mode, switch back to
sta OperMode ;game mode, because game is still on
GameIsOn: rts
TransposePlayers:
sec ;set carry flag by default to end game
lda NumberOfPlayers ;if only a 1 player game, leave
checka
beq ExTrans
lda OffScr_NumberofLives ;does offscreen player have any lives left?
checka
bmi ExTrans ;branch if not
lda CurrentPlayer ;invert bit to update
eorn ++%00000001 ;which player is on the screen
sta CurrentPlayer
ldxn ++$06
TransLoop: ldax OnscreenPlayerInfo,x ;transpose the information
pha ;of the onscreen player
ldax OffscreenPlayerInfo,x ;with that of the offscreen player
stax OnscreenPlayerInfo,x
pla
stax OffscreenPlayerInfo,x
dex
bpl TransLoop
clc ;clear carry flag to get game going
ExTrans: rts
;-------------------------------------------------------------------------------------
DoNothing1:
ldan ++$ff ;this is residual code, this value is
sta UnusedVariable;$06c9 ;not used anywhere in the program
DoNothing2:
rts
;-------------------------------------------------------------------------------------
AreaParserTaskHandler:
;ret
ldy AreaParserTaskNum ;check number of tasks here
checky
bne DoAPTasks ;if already set, go ahead
ldyn ++$08 ;яю эютюьє ъЁєує
sty AreaParserTaskNum ;otherwise, set eight by default
DoAPTasks: dey
tya
jsr AreaParserTasks
deci AreaParserTaskNum ;if all tasks not complete do not
if Z80OPT4==0
bne SkipATRender ;render attribute table yet
jsr RenderAttributeTables ;т√ч√трхЄё яюёых RenderAreaGraphics #1 (Є.х. 4-ую)?
SkipATRender:
endif
rts
AreaParserTasks:
jsr JumpEngine
;тёхуфр т√яюыэ ■Єё 7..0?
.dw IncrementColumnPos ;0
.dw RenderAreaGraphics ;1
.dw RenderAreaGraphics ;2
.dw AreaParserCore ;3
.dw IncrementColumnPos ;4
.dw RenderAreaGraphics ;5
.dw RenderAreaGraphics ;6
.dw AreaParserCore ;7
;-------------------------------------------------------------------------------------
IncrementColumnPos:
inci CurrentColumnPos ;increment column where we're at
lda CurrentColumnPos
andn ++%00001111 ;mask out higher nybble
bne NoColWrap
sta CurrentColumnPos ;if no bits left set, wrap back to zero (0-f)
inci CurrentPageLoc ;and increment page number where we're at
NoColWrap: inci BlockBufferColumnPos ;increment column offset where we're at
lda BlockBufferColumnPos
andn ++%00011111 ;mask out all but 5 LSB (0-1f)
sta BlockBufferColumnPos ;and save
rts
;-------------------------------------------------------------------------------------
;$00 - used as counter, store for low nybble for background, ceiling byte for terrain
;$01 - used to store floor byte for terrain
;$07 - used to store terrain metatile
;$06-$07 - used to store block buffer address
BSceneDataOffsets:
.db $00, $30, $60
BackSceneryData: ;metatiles???
.db $93, $00, $00, $11, $12, $12, $13, $00 ;clouds (palette 2???)
.db $00, $51, $52, $53, $00, $00, $00, $00
.db $00, $00, $01, $02, $02, $03, $00, $00
.db $00, $00, $00, $00, $91, $92, $93, $00
.db $00, $00, $00, $51, $52, $53, $41, $42
.db $43, $00, $00, $00, $00, $00, $91, $92
.db $97, $87, $88, $89, $99, $00, $00, $00 ;mountains and bushes
.db $11, $12, $13, $a4, $a5, $a5, $a5, $a6
.db $97, $98, $99, $01, $02, $03, $00, $a4
.db $a5, $a6, $00, $11, $12, $12, $12, $13
.db $00, $00, $00, $00, $01, $02, $02, $03
.db $00, $a4, $a5, $a5, $a6, $00, $00, $00
.db $11, $12, $12, $13, $00, $00, $00, $00 ;trees and fences
.db $00, $00, $00, $9c, $00, $8b, $aa, $aa
.db $aa, $aa, $11, $12, $13, $8b, $00, $9c
.db $9c, $00, $00, $01, $02, $03, $11, $12
.db $12, $13, $00, $00, $00, $00, $aa, $aa
.db $9c, $aa, $00, $8b, $00, $01, $02, $03
BackSceneryMetatiles:
.db $80, $83, $00 ;cloud left
.db $81, $84, $00 ;cloud middle
.db $82, $85, $00 ;cloud right
.db $02, $00, $00 ;bush left
.db $03, $00, $00 ;bush middle
.db $04, $00, $00 ;bush right
.db $00, $05, $06 ;mountain left
.db $07, $06, $0a ;mountain middle
.db $00, $08, $09 ;mountain right
.db $4d, $00, $00 ;fence
.db $0d, $0f, $4e ;tall tree
.db $0e, $4e, $4e ;short tree
FSceneDataOffsets:
.db $00, $0d, $1a
ForeSceneryData:
.db $86, $87, $87, $87, $87, $87, $87 ;in water
.db $87, $87, $87, $87, $69, $69
.db $00, $00, $00, $00, $00, $45, $47 ;wall
.db $47, $47, $47, $47, $00, $00
.db $00, $00, $00, $00, $00, $00, $00 ;over water
.db $00, $00, $00, $00, $86, $87
TerrainMetatiles:
.db $69, $54, $52, $62
TerrainRenderBits:
.db %00000000, %00000000 ;no ceiling or floor
.db %00000000, %00011000 ;no ceiling, floor 2
.db %00000001, %00011000 ;ceiling 1, floor 2
.db %00000111, %00011000 ;ceiling 3, floor 2
.db %00001111, %00011000 ;ceiling 4, floor 2
.db %11111111, %00011000 ;ceiling 8, floor 2
.db %00000001, %00011111 ;ceiling 1, floor 5
.db %00000111, %00011111 ;ceiling 3, floor 5
.db %00001111, %00011111 ;ceiling 4, floor 5
.db %10000001, %00011111 ;ceiling 1, floor 6
.db %00000001, %00000000 ;ceiling 1, no floor
.db %10001111, %00011111 ;ceiling 4, floor 6
.db %11110001, %00011111 ;ceiling 1, floor 9
.db %11111001, %00011000 ;ceiling 1, middle 5, floor 2
.db %11110001, %00011000 ;ceiling 1, middle 4, floor 2
.db %11111111, %00011111 ;completely solid top to bottom
AreaParserCore:
lda BackloadingFlag ;check to see if we are starting right of start
checka
beq RenderSceneryTerrain ;if not, go ahead and render background, foreground and terrain
jsr ProcessAreaData ;otherwise skip ahead and load level data
RenderSceneryTerrain:
ldxn ++$0c
ldan ++$00
ClrMTBuf: stax MetatileBuffer,x ;clear out metatile buffer
dex
bpl ClrMTBuf
;jr $
ldy BackgroundScenery ;do we need to render the background scenery?
checky
beq RendFore ;if not, skip to check the foreground
lda CurrentPageLoc ;otherwise check for every third page
ThirdP: cmpn ++$03
bmi RendBack ;if less than three we're there
secsub
sbcn ++$03 ;if 3 or more, subtract 3 and
bpl ThirdP ;do an unconditional branch
RendBack: asl ;move results to higher nybble
asl
asl
asl
adcy BSceneDataOffsets-1,y ;add to it offset loaded from here
adci CurrentColumnPos ;add to the result our current column position
tax
ldax BackSceneryData,x ;load data from sum of offsets
checka
beq RendFore ;if zero, no scenery for that part
pha
andn ++$0f ;save to stack and clear high nybble
secsub
sbcn ++$01 ;subtract one (because low nybble is $01-$0c)
sta SCRATCHPAD+$00 ;save low nybble
asl ;multiply by three (shift to left and add result to old one)
adci SCRATCHPAD+$00 ;note that since d7 was nulled, the carry flag is always clear
tax ;save as offset for background scenery metatile data
pla ;get high nybble from stack, move low
lsr
lsr
lsr
lsr
tay ;use as second offset (used to determine height)
ldan ++$03 ;use previously saved memory location for counter
sta SCRATCHPAD+$00
SceLoop1: ldax BackSceneryMetatiles,x ;load metatile data from offset of (lsb - 1) * 3
stay MetatileBuffer,y ;store into buffer from offset of (msb / 16)
inx
iny
cpyn ++$0b ;if at this location, leave loop
beq RendFore
deci SCRATCHPAD+$00 ;decrement until counter expires, barring exception
bne SceLoop1
RendFore: ldx ForegroundScenery ;check for foreground data needed or not
checkx
beq RendTerr ;if not, skip this part
ldyx FSceneDataOffsets-1,x ;load offset from location offset by header value, then
ldxn ++$00 ;reinit X
SceLoop2: lday ForeSceneryData,y ;load data until counter expires
checka
beq NoFore ;do not store if zero found
stax MetatileBuffer,x
NoFore: iny
inx
cpxn ++$0d ;store up to end of metatile buffer
bne SceLoop2
RendTerr: ldy AreaType ;check world type for water level
checky
bne TerMTile ;if not water level, skip this part
lda WorldNumber ;check world number, if not world number eight
cmpn ++World8 ;then skip this part
bne TerMTile
ldan ++$62 ;if set as water level and world number eight,
jmp StoreMT ;use castle wall metatile as terrain type
TerMTile: lday TerrainMetatiles,y ;otherwise get appropriate metatile for area type
ldy CloudTypeOverride ;check for cloud type override
checky
beq StoreMT ;if not set, keep value otherwise
ldan ++$88 ;use cloud block terrain
StoreMT: sta SCRATCHPAD+$07 ;store value here
ldxn ++$00 ;initialize X, use as metatile buffer offset
lda TerrainControl ;use yet another value from the header
asl ;multiply by 2 and use as yet another offset
tay
TerrLoop: lday TerrainRenderBits,y ;get one of the terrain rendering bit data
sta SCRATCHPAD+$00
iny ;increment Y and use as offset next time around
sty SCRATCHPAD+$01
lda CloudTypeOverride ;skip if value here is zero
checka
beq NoCloud2
cpxn ++$00 ;otherwise, check if we're doing the ceiling byte
beq NoCloud2
lda SCRATCHPAD+$00 ;if not, mask out all but d3
andn ++%00001000
sta SCRATCHPAD+$00
NoCloud2: ldyn ++$00 ;start at beginning of bitmasks
TerrBChk: lday Bitmasks,y ;load bitmask, then perform AND on contents of first byte
biti SCRATCHPAD+$00
beq NextTBit ;if not set, skip this part (do not write terrain to buffer)
lda SCRATCHPAD+$07
stax MetatileBuffer,x ;load terrain type metatile number and store into buffer here
NextTBit: inx ;continue until end of buffer
cpxn ++$0d
beq RendBBuf ;if we're at the end, break out of this loop
lda AreaType ;check world type for underground area
cmpn ++$02
bne EndUChk ;if not underground, skip this part
cpxn ++$0b
bne EndUChk ;if we're at the bottom of the screen, override
ldan ++$54 ;old terrain type with ground level terrain type
sta SCRATCHPAD+$07
EndUChk: iny ;increment bitmasks offset in Y
cpyn ++$08
bne TerrBChk ;if not all bits checked, loop back
ldy SCRATCHPAD+$01
checky
bne TerrLoop ;unconditional branch, use Y to load next byte
RendBBuf:
jsr ProcessAreaData ;do the area data loading routine now
if Z80OPT
ld a,(BlockBufferColumnPos)
ld hl,Block_Buffer_1
bit 4,a
jr z,$+5
ld hl,Block_Buffer_2
and 0x0f
ld c,a
add hl,bc
ld (SCRATCHPAD+$06),hl
else
lda BlockBufferColumnPos
jsr GetBlockBufferAddr ;get block buffer address from where we're at
endif
ldxn ++$00
ldyn ++$00 ;init index regs and start at beginning of smaller buffer
ChkMTLow: sty SCRATCHPAD+$00
ldax MetatileBuffer,x ;load stored metatile number
andn ++%11000000 ;mask out all but 2 MSB
asl
rol ;make %xx000000 into %000000xx
rol
tay ;use as offset in Y
ldax MetatileBuffer,x ;reload original unmasked value here
cmpy BlockBuffLowBounds,y ;check for certain values depending on bits set
cmpcy
bcs StrBlock ;if equal or greater, branch
ldan ++$00 ;if less, init value before storing
StrBlock: ldy SCRATCHPAD+$00 ;get offset for block buffer
stayindirect (SCRATCHPAD+$06),y ;store value into block buffer ;$06$07 = Block_Buffer_1(2)+BlockBufferColumnPos
;jr $
tya
clc ;add 16 (move down one row) to offset
adcn ++$10
tay
inx ;increment column value
cpxn ++$0d ;13 ёЄЁюъ яю 16 сыюъют
cmpcy
bcc ChkMTLow ;continue until we pass last row, then leave
rts
;numbers lower than these with the same attribute bits
;will not be stored in the block buffer
BlockBuffLowBounds:
.db $10, $51, $88, $c0
;-------------------------------------------------------------------------------------
;$00 - used to store area object identifier
;$07 - used as adder to find proper area object code
;TODO єёъюЁшЄ№
ProcessAreaData:
if Z80OPT3
ld c,++$02 ;start at the end of area object buffer
ProcADLoop:
if Z80OPT3ly
ld ly,c;stx ObjectOffset ;шёяюы№чєхЄё т DecodeAreaData
else
stx ObjectOffset ;шёяюы№чєхЄё т DecodeAreaData
endif
xor a ;reset flag
ld (BehindAreaParserFlag),a
ld hl,AreaDataOffset
ld e,(hl) ;get offset of area data pointer
ld hl,AreaObjectLength
add hl,bc
bit 7,(hl) ;check area object buffer flag
jp z,RdyDecode_AreaObjectLength_positive ;if buffer not negative, branch, otherwise
ld ix,(AreaData)
add ix,de ;TODO hl? хёыш єфюсэю сєфхЄ т DecodeAreaData
ld a,(ix) ;get first byte of area object
cp ++$fd ;if end-of-area, skip all this crap
jp z,RdyDecode_endofarea
bit 7,(ix+1) ;get second byte of area object
jr z,Chk1Row13 ;check for page select bit (d7), branch if not set
ld hl,AreaObjectPageSel
inc (hl) ;check page select
dec (hl)
jr nz,Chk1Row13
inc (hl) ;if not already set, set it now
ld hl,AreaObjectPageLoc
inc (hl) ;and increment page location
Chk1Row13:
;ld a,(ix) ;reread first byte of level object
and ++$0f ;mask out high nybble
cp ++$0d ;row 13?
jr nz,Chk1Row14
bit 6,(ix+1) ;if so, reread second byte of level object
jr nz,CheckRear ;check for d6 set (if not, object is page control)
ld a,(AreaObjectPageSel) ;if page select is set, do not reread
or a
jr nz,CheckRear
ld a,(ix+1) ;if d6 not set, reread second byte
and ++%00011111 ;mask out all but 5 LSB and store in page control
ld (AreaObjectPageLoc),a
ld hl,AreaObjectPageSel
inc (hl) ;increment page select
jmp NextAObj
Chk1Row14: cp ++$0e ;row 14?
jr nz,CheckRear
ld a,(BackloadingFlag) ;check flag for saved page number and branch if set
or a
jr nz,RdyDecode ;to render the object (otherwise bg might not look right)
CheckRear: ld a,(AreaObjectPageLoc) ;check to see if current page of level object is
ld hl,CurrentPageLoc
cp (hl) ;behind current page of renderer
jr c,SetBehind ;if so branch
RdyDecode:
;push ix
ldayindirect (AreaData),y ;get first byte of level object again
call DecodeAreaData ;do sub and do not turn on flag ;y трцхэ!!!
;pop ix
jp ChkLength
RdyDecode_AreaObjectLength_positive:
ldyx AreaObjOffsetBuffer,x ;if not, get offset from buffer
;push ix
ldayindirect (AreaData),y ;get first byte of level object again
call DecodeAreaData ;do sub and do not turn on flag ;y трцхэ!!!
;pop ix
jp ChkLength
SetBehind:
ld hl,BehindAreaParserFlag
inc (hl) ;turn on flag if object is behind renderer
NextAObj:
ld hl,AreaDataOffset
inc (hl)
inc (hl) ;яю эхьє ЁрёёўшЄ√трхЄё рфЁхё юс·хъЄр эр ёыхфє■∙хь яЁюїюфх
xor a
ld (AreaObjectPageSel),a
RdyDecode_endofarea:
ChkLength:
if Z80OPT3ly
;ld hl,ObjectOffset
ld c,ly;ld c,(hl) ;get buffer offset
else
ld hl,ObjectOffset
ld c,(hl) ;get buffer offset
endif
ld hl,AreaObjectLength
add hl,bc
ld a,(hl) ;check object length for anything stored here
rla
jr c,ProcLoopb ;if not, branch to handle loopback
dec (hl) ;otherwise decrement length or get rid of it
ProcLoopb: dec c ;decrement buffer offset
jp p,ProcADLoop ;and loopback unless exceeded buffer
ld a,(BehindAreaParserFlag) ;check for flag set if objects were behind renderer
or a
jp nz,ProcessAreaData ;branch if true to load more level data, otherwise
ld a,(BackloadingFlag) ;check for flag set if starting right of page $00
or a
jp nz,ProcessAreaData ;branch if true to load more level data, otherwise leave
ret
else ;~Z80
ldxn ++$02 ;start at the end of area object buffer
ProcADLoop: stx ObjectOffset
ldan ++$00 ;reset flag
sta BehindAreaParserFlag
ldy AreaDataOffset ;get offset of area data pointer
ldayindirect (AreaData),y ;get first byte of area object
cmpn ++$fd ;if end-of-area, skip all this crap
beq RdyDecode
ldax AreaObjectLength,x ;check area object buffer flag
checka
bpl RdyDecode ;if buffer not negative, branch, otherwise
iny
ldayindirect (AreaData),y ;get second byte of area object
asl ;check for page select bit (d7), branch if not set
bcc Chk1Row13
lda AreaObjectPageSel ;check page select
checka
bne Chk1Row13
inci AreaObjectPageSel ;if not already set, set it now
inci AreaObjectPageLoc ;and increment page location
Chk1Row13: dey
ldayindirect (AreaData),y ;reread first byte of level object
andn ++$0f ;mask out high nybble
cmpn ++$0d ;row 13?
bne Chk1Row14
iny ;if so, reread second byte of level object
ldayindirect (AreaData),y
dey ;decrement to get ready to read first byte
andn ++%01000000 ;check for d6 set (if not, object is page control)
bne CheckRear
lda AreaObjectPageSel ;if page select is set, do not reread
checka
bne CheckRear
iny ;if d6 not set, reread second byte
ldayindirect (AreaData),y
andn ++%00011111 ;mask out all but 5 LSB and store in page control
sta AreaObjectPageLoc
inci AreaObjectPageSel ;increment page select
jmp NextAObj
Chk1Row14: cmpn ++$0e ;row 14?
bne CheckRear
lda BackloadingFlag ;check flag for saved page number and branch if set
checka
bne RdyDecode ;to render the object (otherwise bg might not look right)
CheckRear: lda AreaObjectPageLoc ;check to see if current page of level object is
cmpi CurrentPageLoc ;behind current page of renderer
cmpcy
bcc SetBehind ;if so branch
RdyDecode: jsr DecodeAreaData ;do sub and do not turn on flag
jmp ChkLength
SetBehind: inci BehindAreaParserFlag ;turn on flag if object is behind renderer
NextAObj:
jsr IncAreaObjOffset ;increment buffer offset and move on
ChkLength: ldx ObjectOffset ;get buffer offset
ldax AreaObjectLength,x ;check object length for anything stored here
checka
bmi ProcLoopb ;if not, branch to handle loopback
decx AreaObjectLength,x ;otherwise decrement length or get rid of it
ProcLoopb: dex ;decrement buffer offset
bpl ProcADLoop ;and loopback unless exceeded buffer
lda BehindAreaParserFlag ;check for flag set if objects were behind renderer
checka
bne ProcessAreaData ;branch if true to load more level data, otherwise
lda BackloadingFlag ;check for flag set if starting right of page $00
checka
bne ProcessAreaData ;branch if true to load more level data, otherwise leave
EndAParse: rts
IncAreaObjOffset:
inci AreaDataOffset ;increment offset of level pointer
inci AreaDataOffset
ldan ++$00 ;reset page select
sta AreaObjectPageSel
rts
endif
;x трцхэ!
;y трцхэ!
DecodeAreaData:
if Z80OPT3
if 0
ld hl,AreaObjectLength
add hl,bc
ld a,(hl) ;check current buffer flag
or a
bmi Chk1stB
ldyx AreaObjOffsetBuffer,x ;if not, get offset from buffer
Chk1stB:
ldayindirect (AreaData),y ;get first byte of level object again
cp ++$fd
ret z ;if end of level, leave this routine
endif
and ++$0f ;otherwise, mask out low nybble
sub 15;cp $0f ;row 15?
jr z,ChkRow15ok ;if so, keep the offset of 16
inc a;cp $0e ;row 14?
jr nz,ChkRow13
ChkRow14ok
ld hy,a;0
ld a,++$2e ;and load A with another value
jp NormObj ;unconditional branch
ChkRow15ok
ld hy,16
jp SpecObj
ChkRow12ok
ld hy,8
jp SpecObj
ChkRow13: inc a;cp $0d ;row 13?
jr nz,ChkSRows
ChkRow13ok
ld hy,34;sta SCRATCHPAD+$07 ;if so, load offset with 34
iny ;get next byte
ldayindirect (AreaData),y
bit 6,a;and ++%01000000 ;mask out all but d6 (page control obj bit)
ret z;jr z,LeavePar ;if d6 clear, branch to leave (we handled this earlier)
and ++%01111111 ;mask out d7
cp ++$4b ;check for loop command in low nybble
jr nz,Mask2MSB ;(plus d6 set for object other than page control)
inci LoopCommand ;if loop command, set loop command flag
Mask2MSB
and ++%00111111 ;mask out d7 and d6
jp NormObj ;and jump
ChkSRows:
inc a;cp $0c ;row 12?
jr z,ChkRow12ok ;if so, keep the offset value of 8
ld hy,0 ;otherwise nullify value by default
iny ;if not, get second byte of level object
ldayindirect (AreaData),y
and ++%01110000 ;mask out all but d6-d4
jr nz,LrgObj ;if any bits set, branch to handle large object
ld hy,22;sta SCRATCHPAD+$07 ;otherwise set offset of 22 for small object
ldayindirect (AreaData),y ;reload second byte of level object
and ++%00001111 ;mask out higher nybble and jump
jmp NormObj
LrgObj: sta SCRATCHPAD+$00 ;store value here (branch for large objects)
cp ++$70 ;check for vertical pipe object
jr nz,NotWPipe
ldayindirect (AreaData),y ;if not, reload second byte
bit 3,a;and ++%00001000 ;mask out all but d3 (usage control bit)
jr z,NotWPipe ;if d3 clear, branch to get original value
ldan ++$00 ;otherwise, nullify value for warp pipe
sta SCRATCHPAD+$00
NotWPipe: lda SCRATCHPAD+$00 ;get value and jump ahead
jmp MoveAOId
SpecObj: iny ;branch here for rows 12-15
if Z80OPT3hy
else
ld a,hy
ld (SCRATCHPAD+$07),a
endif
ldayindirect (AreaData),y
andn ++%01110000 ;get next byte and mask out all but d6-d4
MoveAOId: lsr ;move d6-d4 to lower nybble
lsr
lsr
lsr
NormObj: sta SCRATCHPAD+$00 ;store value here (branch for small objects and rows 13 and 14)
if Z80OPT3hy
else
ld a,hy
ld (SCRATCHPAD+$07),a
endif
ldax AreaObjectLength,x ;is there something stored here already?
or a
jp p,RunAObj ;if so, branch to do its particular sub
lda AreaObjectPageLoc ;otherwise check to see if the object we've loaded is on the
cmpi CurrentPageLoc ;same page as the renderer, and if so, branch
beq InitRear
ldy AreaDataOffset ;if not, get old offset of level pointer
ldayindirect (AreaData),y ;and reload first byte
andn ++%00001111
cmpn ++$0e ;row 14?
ret nz
lda BackloadingFlag ;if so, check backloading flag
checka
bne StrAObj ;if set, branch to render object, else leave
ret
InitRear: lda BackloadingFlag ;check backloading flag to see if it's been initialized
or a
jr z,BackColC ;branch to column-wise check
xor a;ldan ++$00 ;if not, initialize both backloading and
sta BackloadingFlag ;behind-renderer flags and leave
sta BehindAreaParserFlag
if Z80OPT3ly
ld ly,a;0;sta ObjectOffset
else
sta ObjectOffset
endif
LoopCmdE: ret
BackColC: ldy AreaDataOffset ;get first byte again
ldayindirect (AreaData),y
and ++%11110000 ;mask out low nybble and move high to low
rrca
rrca
rrca
rrca
cmpi CurrentColumnPos ;is this where we're at?
ret nz ;if not, branch to leave
StrAObj:
ld hl,AreaDataOffset
ld a,(hl) ;if so, load area obj offset and store in buffer
inc (hl)
inc (hl)
stax AreaObjOffsetBuffer,x
xor a
ld (AreaObjectPageSel),a
RunAObj:
;CY=0
ld a,(SCRATCHPAD+$00) ;get stored value and add offset to it
if Z80OPT3hy
add a,hy ;adci SCRATCHPAD+$07
else
adci SCRATCHPAD+$07
endif
call JumpEngine
else ;~Z80
ldax AreaObjectLength,x ;check current buffer flag
checka
bmi Chk1stB
ldyx AreaObjOffsetBuffer,x ;if not, get offset from buffer
Chk1stB: ldxn ++$10 ;load offset of 16 for special row 15
ldayindirect (AreaData),y ;get first byte of level object again
cmpn ++$fd
beq EndAParse ;if end of level, leave this routine (TODO ret z)
andn ++$0f ;otherwise, mask out low nybble
cmpn ++$0f ;row 15?
beq ChkRow14 ;if so, keep the offset of 16
ldxn ++$08 ;otherwise load offset of 8 for special row 12
cmpn ++$0c ;row 12?
beq ChkRow14 ;if so, keep the offset value of 8
ldxn ++$00 ;otherwise nullify value by default
ChkRow14:
stx SCRATCHPAD+$07 ;store whatever value we just loaded here
ldx ObjectOffset ;get object offset again
cmpn ++$0e ;row 14?
bne ChkRow13
ldan ++$00 ;if so, load offset with $00
sta SCRATCHPAD+$07
ldan ++$2e ;and load A with another value
checka
bne NormObj ;unconditional branch
ChkRow13: cmpn ++$0d ;row 13?
bne ChkSRows
ldan ++$22 ;if so, load offset with 34
sta SCRATCHPAD+$07
iny ;get next byte
ldayindirect (AreaData),y
andn ++%01000000 ;mask out all but d6 (page control obj bit)
beq LeavePar ;if d6 clear, branch to leave (we handled this earlier)
ldayindirect (AreaData),y ;otherwise, get byte again
andn ++%01111111 ;mask out d7
cmpn ++$4b ;check for loop command in low nybble
bne Mask2MSB ;(plus d6 set for object other than page control)
inci LoopCommand ;if loop command, set loop command flag
Mask2MSB: andn ++%00111111 ;mask out d7 and d6
jmp NormObj ;and jump
ChkSRows: cmpn ++$0c ;row 12-15?
cmpcy
bcs SpecObj
iny ;if not, get second byte of level object
ldayindirect (AreaData),y
andn ++%01110000 ;mask out all but d6-d4
bne LrgObj ;if any bits set, branch to handle large object
ldan ++$16
sta SCRATCHPAD+$07 ;otherwise set offset of 24 for small object
ldayindirect (AreaData),y ;reload second byte of level object
andn ++%00001111 ;mask out higher nybble and jump
jmp NormObj
LrgObj: sta SCRATCHPAD+$00 ;store value here (branch for large objects)
cmpn ++$70 ;check for vertical pipe object
bne NotWPipe
ldayindirect (AreaData),y ;if not, reload second byte
andn ++%00001000 ;mask out all but d3 (usage control bit)
beq NotWPipe ;if d3 clear, branch to get original value
ldan ++$00 ;otherwise, nullify value for warp pipe
sta SCRATCHPAD+$00
NotWPipe: lda SCRATCHPAD+$00 ;get value and jump ahead
jmp MoveAOId
SpecObj: iny ;branch here for rows 12-15
ldayindirect (AreaData),y
andn ++%01110000 ;get next byte and mask out all but d6-d4
MoveAOId: lsr ;move d6-d4 to lower nybble
lsr
lsr
lsr
NormObj: sta SCRATCHPAD+$00 ;store value here (branch for small objects and rows 13 and 14)
ldax AreaObjectLength,x ;is there something stored here already?
checka
bpl RunAObj ;if so, branch to do its particular sub
lda AreaObjectPageLoc ;otherwise check to see if the object we've loaded is on the
cmpi CurrentPageLoc ;same page as the renderer, and if so, branch
beq InitRear
ldy AreaDataOffset ;if not, get old offset of level pointer
ldayindirect (AreaData),y ;and reload first byte
andn ++%00001111
cmpn ++$0e ;row 14?
bne LeavePar ;TODO ret nz
lda BackloadingFlag ;if so, check backloading flag
checka
bne StrAObj ;if set, branch to render object, else leave
LeavePar: rts
InitRear: lda BackloadingFlag ;check backloading flag to see if it's been initialized
checka
beq BackColC ;branch to column-wise check
ldan ++$00 ;if not, initialize both backloading and
sta BackloadingFlag ;behind-renderer flags and leave
sta BehindAreaParserFlag
sta ObjectOffset
LoopCmdE: rts
BackColC: ldy AreaDataOffset ;get first byte again
ldayindirect (AreaData),y
andn ++%11110000 ;mask out low nybble and move high to low
lsr
lsr
lsr
lsr
cmpi CurrentColumnPos ;is this where we're at?
bne LeavePar ;if not, branch to leave
StrAObj: lda AreaDataOffset ;if so, load area obj offset and store in buffer
stax AreaObjOffsetBuffer,x
jsr IncAreaObjOffset ;do sub to increment to next object data
RunAObj: lda SCRATCHPAD+$00 ;get stored value and add offset to it
clc ;then use the jump engine with current contents of A
adci SCRATCHPAD+$07
jsr JumpEngine
endif
;y=AreaData offset (фы GetLrgObjAttrib)
;x трцхэ т GetLrgObjAttrib
;яюЄюь x схЁ╕Єё шч hy;SCRATCHPAD+$07
;large objects (rows $00-$0b or 00-11, d6-d4 set)
.dw VerticalPipe ;used by warp pipes
.dw AreaStyleObject
.dw RowOfBricks
.dw RowOfSolidBlocks
.dw RowOfCoins
.dw ColumnOfBricks
.dw ColumnOfSolidBlocks
.dw VerticalPipe ;used by decoration pipes
;objects for special row $0c or 12
.dw Hole_Empty
.dw PulleyRopeObject
.dw Bridge_High
.dw Bridge_Middle
.dw Bridge_Low
.dw Hole_Water
.dw QuestionBlockRow_High
.dw QuestionBlockRow_Low
;objects for special row $0f or 15
.dw EndlessRope
.dw BalancePlatRope
.dw CastleObject
.dw StaircaseObject
.dw ExitPipe
.dw FlagBalls_Residual
;small objects (rows $00-$0b or 00-11, d6-d4 all clear)
.dw QuestionBlock ;power-up
.dw QuestionBlock ;coin
.dw QuestionBlock ;hidden, coin
.dw Hidden1UpBlock ;hidden, 1-up
.dw BrickWithItem ;brick, power-up
.dw BrickWithItem ;brick, vine
.dw BrickWithItem ;brick, star
.dw BrickWithCoins ;brick, coins
.dw BrickWithItem ;brick, 1-up
.dw WaterPipe
.dw EmptyBlock
.dw Jumpspring
;objects for special row $0d or 13 (d6 set)
.dw IntroPipe
.dw FlagpoleObject
.dw AxeObj
.dw ChainObj
.dw CastleBridgeObj
.dw ScrollLockObject_Warp
.dw ScrollLockObject
.dw ScrollLockObject
.dw AreaFrenzy ;flying cheep-cheeps
.dw AreaFrenzy ;bullet bills or swimming cheep-cheeps
.dw AreaFrenzy ;stop frenzy
.dw LoopCmdE ;ret
;object for special row $0e or 14
.dw AlterAreaAttributes
;-------------------------------------------------------------------------------------
;(these apply to all area object subroutines in this section unless otherwise stated)
;$00 - used to store offset used to find object code
;$07 - starts with adder from area parser, used to store row offset
AlterAreaAttributes:
ldyx AreaObjOffsetBuffer,x ;load offset for level object data saved in buffer
iny ;load second byte
ldayindirect (AreaData),y
pha ;save in stack for now
andn ++%01000000
bne Alter2 ;branch if d6 is set
pla
pha ;pull and push offset to copy to A
andn ++%00001111 ;mask out high nybble and store as
sta TerrainControl ;new terrain height type bits
pla
andn ++%00110000 ;pull and mask out all but d5 and d4
lsr ;move bits to lower nybble and store
lsr ;as new background scenery bits
lsr
lsr
sta BackgroundScenery ;then leave
rts
Alter2: pla
andn ++%00000111 ;mask out all but 3 LSB
cmpn ++$04 ;if four or greater, set color control bits
cmpcy
bcc SetFore ;and nullify foreground scenery bits
sta BackgroundColorCtrl
ldan ++$00
SetFore: sta ForegroundScenery ;otherwise set new foreground scenery bits
rts
;--------------------------------
ScrollLockObject_Warp:
ldxn ++$04 ;load value of 4 for game text routine as default
lda WorldNumber ;warp zone (4-3-2), then check world number
checka
beq WarpNum
inx ;if world number HIGH 1, increment for next warp zone (5)
ldy AreaType ;check area type
dey
bne WarpNum ;if ground area type, increment for last warp zone
inx ;(8-7-6) and move on
WarpNum: txa
sta WarpZoneControl ;store number here to be used by warp zone routine
jsr WriteGameText ;print text and warp zone numbers
ldan ++PiranhaPlant
jsr KillEnemies ;load identifier for piranha plants and do sub
ScrollLockObject:
lda ScrollLock ;invert scroll lock to turn it on
eorn ++%00000001
sta ScrollLock
rts
;--------------------------------
;$00 - used to store enemy identifier in KillEnemies
KillEnemies:
sta SCRATCHPAD+$00 ;store identifier here
ldan ++$00
ldxn ++$04 ;check for identifier in enemy object buffer
KillELoop: ldyx Enemy_ID,x
cpyi SCRATCHPAD+$00 ;if not found, branch
bne NoKillE
stax Enemy_Flag,x ;if found, deactivate enemy object flag
NoKillE: dex ;do this until all slots are checked
bpl KillELoop
rts
;--------------------------------
FrenzyIDData:
.db FlyCheepCheepFrenzy, BBill_CCheep_Frenzy, Stop_Frenzy
AreaFrenzy: ldx SCRATCHPAD+$00 ;use area object identifier bit as offset
ldax FrenzyIDData-8,x ;note that it starts at 8, thus weird address here
ldyn ++$05
FreCompLoop: dey ;check regular slots of enemy object buffer
bmi ExitAFrenzy ;if all slots checked and enemy object not found, branch to store
cmpy Enemy_ID,y ;check for enemy object in buffer versus frenzy object
bne FreCompLoop
ldan ++$00 ;if enemy object already present, nullify queue and leave
ExitAFrenzy: sta EnemyFrenzyQueue ;store enemy into frenzy queue
rts
;--------------------------------
;$06 - used by MushroomLedge to store length
AreaStyleObject:
lda AreaStyle ;load level object style and jump to the right sub
jsr JumpEngine
.dw TreeLedge ;also used for cloud type levels
.dw MushroomLedge
.dw BulletBillCannon
TreeLedge:
jsr GetLrgObjAttrib ;get row and length of green ledge
ldax AreaObjectLength,x ;check length counter for expiration
checka
beq EndTreeL
bpl MidTreeL
tya
stax AreaObjectLength,x ;store lower nybble into buffer flag as length of ledge
lda CurrentPageLoc
orai CurrentColumnPos ;are we at the start of the level?
beq MidTreeL
ldan ++$16 ;render start of tree ledge
jmp NoUnder
MidTreeL:
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07
else
ldx SCRATCHPAD+$07
endif
ldan ++$17 ;render middle of tree ledge
stax MetatileBuffer,x ;note that this is also used if ledge position is
ldan ++$4c ;at the start of level for continuous effect
jmp AllUnder ;now render the part underneath
EndTreeL: ldan ++$18 ;render end of tree ledge
jmp NoUnder
MushroomLedge:
jsr ChkLrgObjLength ;get shroom dimensions
sty SCRATCHPAD+$06 ;store length here for now
bcc EndMushL
ldax AreaObjectLength,x ;divide length by 2 and store elsewhere
lsr
stax MushroomLedgeHalfLen,x
ldan ++$19 ;render start of mushroom
jmp NoUnder
EndMushL: ldan ++$1b ;if at the end, render end of mushroom
ldyx AreaObjectLength,x
checky
beq NoUnder
ldax MushroomLedgeHalfLen,x ;get divided length and store where length
sta SCRATCHPAD+$06 ;was stored originally
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07
else
ldx SCRATCHPAD+$07
endif
ldan ++$1a
stax MetatileBuffer,x ;render middle of mushroom
cpyi SCRATCHPAD+$06 ;are we smack dab in the center?
bne MushLExit ;if not, branch to leave
inx
ldan ++$4f
stax MetatileBuffer,x ;render stem top of mushroom underneath the middle
ldan ++$50
AllUnder: inx
ldyn ++$0f ;set $0f to render all way down
jmp RenderUnderPart ;now render the stem of mushroom
NoUnder:
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07 ;load row of ledge
else
ldx SCRATCHPAD+$07 ;load row of ledge
endif
ldyn ++$00 ;set 0 for no bottom on this part
jmp RenderUnderPart
;--------------------------------
;tiles used by pulleys and rope object
PulleyRopeMetatiles:
.db $42, $41, $43
PulleyRopeObject:
jsr ChkLrgObjLength ;get length of pulley/rope object
ldyn ++$00 ;initialize metatile offset
bcs RenderPul ;if starting, render left pulley
iny
ldax AreaObjectLength,x ;if not at the end, render rope
checka
bne RenderPul
iny ;otherwise render right pulley
RenderPul: lday PulleyRopeMetatiles,y
sta MetatileBuffer ;render at the top of the screen
MushLExit: rts ;and leave
;--------------------------------
;$06 - used to store upper limit of rows for CastleObject
CastleMetatiles:
.db $00, $45, $45, $45, $00
.db $00, $48, $47, $46, $00
.db $45, $49, $49, $49, $45
.db $47, $47, $4a, $47, $47
.db $47, $47, $4b, $47, $47
.db $49, $49, $49, $49, $49
.db $47, $4a, $47, $4a, $47
.db $47, $4b, $47, $4b, $47
.db $47, $47, $47, $47, $47
.db $4a, $47, $4a, $47, $4a
.db $4b, $47, $4b, $47, $4b
CastleObject:
jsr GetLrgObjAttrib ;save lower nybble as starting row
if Z80OPT3hy
ld hy,e; sty SCRATCHPAD+$07 ;if starting row is above $0a, game will crash!!!
else
sty SCRATCHPAD+$07 ;if starting row is above $0a, game will crash!!!
endif
ldyn ++$04
jsr ChkLrgObjFixedLength ;load length of castle if not already loaded
txa
pha ;save obj buffer offset to stack
ldyx AreaObjectLength,x ;use current length as offset for castle data
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07 ;begin at starting row
else
ldx SCRATCHPAD+$07 ;begin at starting row
endif
ldan ++$0b
sta SCRATCHPAD+$06 ;load upper limit of number of rows to print
CRendLoop: lday CastleMetatiles,y ;load current byte using offset
stax MetatileBuffer,x
inx ;store in buffer and increment buffer offset
lda SCRATCHPAD+$06
checka
beq ChkCFloor ;have we reached upper limit yet?
iny ;if not, increment column-wise
iny ;to byte in next row
iny
iny
iny
deci SCRATCHPAD+$06 ;move closer to upper limit
ChkCFloor: cpxn ++$0b ;have we reached the row just before floor?
bne CRendLoop ;if not, go back and do another row
pla
tax ;get obj buffer offset from before
lda CurrentPageLoc
checka
beq ExitCastle ;if we're at page 0, we do not need to do anything else
ldax AreaObjectLength,x ;check length
cmpn ++$01 ;if length almost about to expire, put brick at floor
beq PlayerStop
if Z80OPT3hy
if Z80OPT3hybug
ld c,hy;ldy SCRATCHPAD+$07 ;check starting row for tall castle ($00) ;т Ёхышчх тъы■ўрЄ№ эх эрфю!
else
ld e,hy;ldy SCRATCHPAD+$07 ;check starting row for tall castle ($00)
endif
else
ldy SCRATCHPAD+$07 ;check starting row for tall castle ($00)
endif
checky
bne NotTall
cmpn ++$03 ;if found, then check to see if we're at the second column
beq PlayerStop
NotTall: cmpn ++$02 ;if not tall castle, check to see if we're at the third column
bne ExitCastle ;if we aren't and the castle is tall, don't create flag yet
jsr GetAreaObjXPosition ;otherwise, obtain and save horizontal pixel coordinate
pha
jsr FindEmptyEnemySlot ;find an empty place on the enemy object buffer
pla
stax Enemy_X_Position,x ;then write horizontal coordinate for star flag
lda CurrentPageLoc
stax Enemy_PageLoc,x ;set page location for star flag
ldan ++$01
stax Enemy_Y_HighPos,x ;set vertical high byte
stax Enemy_Flag,x ;set flag for buffer
ldan ++$90
stax Enemy_Y_Position,x ;set vertical coordinate
ldan ++StarFlagObject ;set star flag value in buffer itself
stax Enemy_ID,x
rts
PlayerStop: ldyn ++$52 ;put brick at floor to stop player at end of level
sty MetatileBuffer+10 ;this is only done if we're on the second column
ExitCastle: rts
;--------------------------------
WaterPipe:
jsr GetLrgObjAttrib ;get row and lower nybble
ldyx AreaObjectLength,x ;get length (residual code, water pipe is 1 col thick)
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07 ;get row
else
ldx SCRATCHPAD+$07 ;get row
endif
ldan ++$6b
stax MetatileBuffer,x ;draw something here and below it
ldan ++$6c
stax MetatileBuffer+1,x
rts
;--------------------------------
;$05 - used to store length of vertical shaft in RenderSidewaysPipe
;$06 - used to store leftover horizontal length in RenderSidewaysPipe
; and vertical length in VerticalPipe and GetPipeHeight
IntroPipe:
ldyn ++$03 ;check if length set, if not set, set it
jsr ChkLrgObjFixedLength
ldyn ++$0a ;set fixed value and render the sideways part
jsr RenderSidewaysPipe
bcs NoBlankP ;if carry flag set, not time to draw vertical pipe part
ldxn ++$06 ;blank everything above the vertical pipe part
VPipeSectLoop: ldan ++$00 ;all the way to the top of the screen
stax MetatileBuffer,x ;because otherwise it will look like exit pipe
dex
bpl VPipeSectLoop
lday VerticalPipeData,y ;draw the end of the vertical pipe part
sta MetatileBuffer+7
NoBlankP: rts
SidePipeShaftData:
.db $15, $14 ;used to control whether or not vertical pipe shaft
.db $00, $00 ;is drawn, and if so, controls the metatile number
SidePipeTopPart:
.db $15, $1e ;top part of sideways part of pipe
.db $1d, $1c
SidePipeBottomPart:
.db $15, $21 ;bottom part of sideways part of pipe
.db $20, $1f
ExitPipe:
ldyn ++$03 ;check if length set, if not set, set it
jsr ChkLrgObjFixedLength
jsr GetLrgObjAttrib ;get vertical length, then plow on through RenderSidewaysPipe
RenderSidewaysPipe:
dey ;decrement twice to make room for shaft at bottom
dey ;and store here for now as vertical length
sty SCRATCHPAD+$05
ldyx AreaObjectLength,x ;get length left over and store here
sty SCRATCHPAD+$06
ldx SCRATCHPAD+$05 ;get vertical length plus one, use as buffer offset
inx
lday SidePipeShaftData,y ;check for value $00 based on horizontal offset
cmpn ++$00
cmpcy
beq DrawSidePart ;if found, do not draw the vertical pipe shaft (CY6502=1)
ldxn ++$00
ldy SCRATCHPAD+$05 ;init buffer offset and get vertical length
jsr RenderUnderPart ;and render vertical shaft using tile number in A
clc ;clear carry flag to be used by IntroPipe
DrawSidePart:
push af
ldy SCRATCHPAD+$06 ;render side pipe part at the bottom
lday SidePipeTopPart,y
stax MetatileBuffer,x ;note that the pipe parts are stored
lday SidePipeBottomPart,y ;backwards horizontally
stax MetatileBuffer+1,x
pop af
rts
VerticalPipeData:
.db $11, $10 ;used by pipes that lead somewhere
.db $15, $14
.db $13, $12 ;used by decoration pipes
.db $15, $14
VerticalPipe:
jsr GetPipeHeight
lda SCRATCHPAD+$00 ;check to see if value was nullified earlier
checka
beq WarpPipe ;(if d3, the usage control bit of second byte, was set)
iny
iny
iny
iny ;add four if usage control bit was not set
WarpPipe: tya ;save value in stack
pha
lda AreaNumber
orai WorldNumber ;if at world 1-1, do not add piranha plant ever
if NOPIRANHAPLANT
xor a
endif
beq DrawPipe
ldyx AreaObjectLength,x ;if on second column of pipe, branch
checky
beq DrawPipe ;(because we only need to do this once)
jsr FindEmptyEnemySlot ;check for an empty moving data buffer space
bcs DrawPipe ;if not found, too many enemies, thus skip
jsr GetAreaObjXPosition ;get horizontal pixel coordinate
clc
adcn ++$08 ;add eight to put the piranha plant in the center
push af
stax Enemy_X_Position,x ;store as enemy's horizontal coordinate
lda CurrentPageLoc ;add carry to current page number
ld h,a
pop af
ld a,h
adcn ++$00
stax Enemy_PageLoc,x ;store as enemy's page coordinate
ldan ++$01
stax Enemy_Y_HighPos,x
stax Enemy_Flag,x ;activate enemy flag
jsr GetAreaObjYPosition ;get piranha plant's vertical coordinate and store here
stax Enemy_Y_Position,x
ldan ++PiranhaPlant ;write piranha plant's value into buffer
stax Enemy_ID,x
jsr InitPiranhaPlant
DrawPipe: pla ;get value saved earlier and use as Y
tay
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07 ;get buffer offset
else
ldx SCRATCHPAD+$07 ;get buffer offset
endif
lday VerticalPipeData,y ;draw the appropriate pipe with the Y we loaded earlier
stax MetatileBuffer,x ;render the top of the pipe
inx
lday VerticalPipeData+2,y ;render the rest of the pipe
ldy SCRATCHPAD+$06 ;subtract one from length and render the part underneath
dey
jmp RenderUnderPart
GetPipeHeight:
ldyn ++$01 ;check for length loaded, if not, load
jsr ChkLrgObjFixedLength ;pipe length of 2 (horizontal)
jsr GetLrgObjAttrib
tya ;get saved lower nybble as height
andn ++$07 ;save only the three lower bits as
sta SCRATCHPAD+$06 ;vertical length, then load Y with
ldyx AreaObjectLength,x ;length left over
rts
FindEmptyEnemySlot:
ldxn ++$00 ;start at first enemy slot
EmptyChkLoop: clc ;clear carry flag by default
ldax Enemy_Flag,x ;check enemy buffer for nonzero
checka
beq ExitEmptyChk ;if zero, leave
inx
cpxn ++$05 ;if nonzero, check next value
bne EmptyChkLoop
ExitEmptyChk: rts ;if all values nonzero, carry flag is set
;--------------------------------
Hole_Water:
jsr ChkLrgObjLength ;get low nybble and save as length
ldan ++$86 ;render waves
sta MetatileBuffer+10
ldxn ++$0b
ldyn ++$01 ;now render the water underneath
ldan ++$87
jmp RenderUnderPart
;--------------------------------
QuestionBlockRow_High:
ldan ++$03 ;start on the fourth row
jr QuestionBlockRow_go;.db $2c ;BIT instruction opcode
QuestionBlockRow_Low:
ldan ++$07 ;start on the eighth row
QuestionBlockRow_go
pha ;save whatever row to the stack for now
jsr ChkLrgObjLength ;get low nybble and save as length
pla
tax ;render question boxes with coins
ldan ++$c0
stax MetatileBuffer,x
rts
;--------------------------------
Bridge_High:
ldan ++$06 ;start on the seventh row from top of screen
jr Bridge_go;.db $2c ;BIT instruction opcode
Bridge_Middle:
ldan ++$07 ;start on the eighth row
jr Bridge_go;.db $2c ;BIT instruction opcode
Bridge_Low:
ldan ++$09 ;start on the tenth row
Bridge_go
pha ;save whatever row to the stack for now
jsr ChkLrgObjLength ;get low nybble and save as length
pla
tax ;render bridge railing
ldan ++$0b
stax MetatileBuffer,x
inx
ldyn ++$00 ;now render the bridge itself
ldan ++$63
jmp RenderUnderPart
;--------------------------------
FlagBalls_Residual:
jsr GetLrgObjAttrib ;get low nybble from object byte
ldxn ++$02 ;render flag balls on third row from top
ldan ++$6d ;of screen downwards based on low nybble
jmp RenderUnderPart
;--------------------------------
FlagpoleObject:
ldan ++$24 ;render flagpole ball on top
sta MetatileBuffer
ldxn ++$01 ;now render the flagpole shaft
ldyn ++$08
ldan ++$25
jsr RenderUnderPart
ldan ++$61 ;render solid block at the bottom
sta MetatileBuffer+10
jsr GetAreaObjXPosition
secsub ;get pixel coordinate of where the flagpole is,
sbcn ++$08 ;subtract eight pixels and use as horizontal
sta Enemy_X_Position+5 ;coordinate for the flag
lda CurrentPageLoc
sbcn ++$00 ;subtract borrow from page location and use as
sta Enemy_PageLoc+5 ;page location for the flag
ldan ++$30
sta Enemy_Y_Position+5 ;set vertical coordinate for flag
ldan ++$b0
sta FlagpoleFNum_Y_Pos ;set initial vertical coordinate for flagpole's floatey number
ldan ++FlagpoleFlagObject
sta Enemy_ID+5 ;set flag identifier, note that identifier and coordinates
inci Enemy_Flag+5 ;use last space in enemy object buffer
rts
;--------------------------------
EndlessRope:
ldxn ++$00 ;render rope from the top to the bottom of screen
ldyn ++$0f
jmp DrawRope
BalancePlatRope:
txa ;save object buffer offset for now
pha
ldxn ++$01 ;blank out all from second row to the bottom
ldyn ++$0f ;with blank used for balance platform rope
ldan ++$44
jsr RenderUnderPart
pla ;get back object buffer offset
tax
jsr GetLrgObjAttrib ;get vertical length from lower nybble
ldxn ++$01
DrawRope: ldan ++$40 ;render the actual rope
jmp RenderUnderPart
;--------------------------------
CoinMetatileData:
.db $c3, $c2, $c2, $c2
RowOfCoins:
ldy AreaType ;get area type
lday CoinMetatileData,y ;load appropriate coin metatile
jmp GetRow
;--------------------------------
C_ObjectRow:
.db $06, $07, $08
C_ObjectMetatile:
.db $c5, $0c, $89
CastleBridgeObj:
ldyn ++$0c ;load length of 13 columns
jsr ChkLrgObjFixedLength
jmp ChainObj
AxeObj:
ldan ++$08 ;load bowser's palette into sprite portion of palette
sta VRAM_Buffer_AddrCtrl
ChainObj:
ldy SCRATCHPAD+$00 ;get value loaded earlier from decoder
ldxy C_ObjectRow-2,y ;get appropriate row and metatile for object
lday C_ObjectMetatile-2,y
jmp ColObj
EmptyBlock:
jsr GetLrgObjAttrib ;get row location
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07
else
ldx SCRATCHPAD+$07
endif
ldan ++$c4
ColObj: ldyn ++$00 ;column length of 1
jmp RenderUnderPart
;--------------------------------
SolidBlockMetatiles:
.db $69, $61, $61, $62
BrickMetatiles:
.db $22, $51, $52, $52
.db $88 ;used only by row of bricks object
RowOfBricks:
ldy AreaType ;load area type obtained from area offset pointer
lda CloudTypeOverride ;check for cloud type override
checka
beq DrawBricks
ldyn ++$04 ;if cloud type, override area type
DrawBricks: lday BrickMetatiles,y ;get appropriate metatile
jmp GetRow ;and go render it
RowOfSolidBlocks:
ldy AreaType ;load area type obtained from area offset pointer
lday SolidBlockMetatiles,y ;get metatile
GetRow: pha ;store metatile here
jsr ChkLrgObjLength ;get row number, load length
DrawRow:
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07
else
ldx SCRATCHPAD+$07
endif
ldyn ++$00 ;set vertical height of 1
pla
jmp RenderUnderPart ;render object
ColumnOfBricks:
ldy AreaType ;load area type obtained from area offset
lday BrickMetatiles,y ;get metatile (no cloud override as for row)
jmp GetRow2
ColumnOfSolidBlocks:
ldy AreaType ;load area type obtained from area offset
lday SolidBlockMetatiles,y ;get metatile
GetRow2: pha ;save metatile to stack for now
jsr GetLrgObjAttrib ;get length and row
pla ;restore metatile
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07 ;get starting row
else
ldx SCRATCHPAD+$07 ;get starting row
endif
jmp RenderUnderPart ;now render the column
;--------------------------------
BulletBillCannon:
jsr GetLrgObjAttrib ;get row and length of bullet bill cannon
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07 ;start at first row
else
ldx SCRATCHPAD+$07 ;start at first row
endif
ldan ++$64 ;render bullet bill cannon
stax MetatileBuffer,x
inx
dey ;done yet?
bmi SetupCannon
ldan ++$65 ;if not, render middle part
stax MetatileBuffer,x
inx
dey ;done yet?
bmi SetupCannon
ldan ++$66 ;if not, render bottom until length expires
jsr RenderUnderPart
SetupCannon: ldx Cannon_Offset ;get offset for data used by cannons and whirlpools
jsr GetAreaObjYPosition ;get proper vertical coordinate for cannon
stax Cannon_Y_Position,x ;and store it here
lda CurrentPageLoc
stax Cannon_PageLoc,x ;store page number for cannon here
jsr GetAreaObjXPosition ;get proper horizontal coordinate for cannon
stax Cannon_X_Position,x ;and store it here
inx
cpxn ++$06 ;increment and check offset
cmpcy
bcc StrCOffset ;if not yet reached sixth cannon, branch to save offset
ldxn ++$00 ;otherwise initialize it
StrCOffset: stx Cannon_Offset ;save new offset and leave
rts
;--------------------------------
StaircaseHeightData:
.db $07, $07, $06, $05, $04, $03, $02, $01, $00
StaircaseRowData:
.db $03, $03, $04, $05, $06, $07, $08, $09, $0a
StaircaseObject:
jsr ChkLrgObjLength ;check and load length
bcc NextStair ;if length already loaded, skip init part
ldan ++$09 ;start past the end for the bottom
sta StaircaseControl ;of the staircase
NextStair: deci StaircaseControl ;move onto next step (or first if starting)
ldy StaircaseControl
ldxy StaircaseRowData,y ;get starting row and height to render
lday StaircaseHeightData,y
tay
ldan ++$61 ;now render solid block staircase
jmp RenderUnderPart
;--------------------------------
Jumpspring:
jsr GetLrgObjAttrib
jsr FindEmptyEnemySlot ;find empty space in enemy object buffer
jsr GetAreaObjXPosition ;get horizontal coordinate for jumpspring
stax Enemy_X_Position,x ;and store
lda CurrentPageLoc ;store page location of jumpspring
stax Enemy_PageLoc,x
jsr GetAreaObjYPosition ;get vertical coordinate for jumpspring
stax Enemy_Y_Position,x ;and store
stax Jumpspring_FixedYPos,x ;store as permanent coordinate here
ldan ++JumpspringObject
stax Enemy_ID,x ;write jumpspring object to enemy object buffer
ldyn ++$01
styx Enemy_Y_HighPos,x ;store vertical high byte
incx Enemy_Flag,x ;set flag for enemy object buffer
if Z80OPT3hy
ld c,hy;ldx SCRATCHPAD+$07
else
ldx SCRATCHPAD+$07
endif
ldan ++$67 ;draw metatiles in two rows where jumpspring is
stax MetatileBuffer,x
ldan ++$68
stax MetatileBuffer+1,x
rts
;--------------------------------
;$07 - used to save ID of brick object
Hidden1UpBlock:
lda Hidden1UpFlag ;if flag not set, do not render object
checka
beq ExitDecBlock
ldan ++$00 ;if set, init for the next one
sta Hidden1UpFlag
jmp BrickWithItem ;jump to code shared with unbreakable bricks
QuestionBlock:
jsr GetAreaObjectID ;get value from level decoder routine
jmp DrawQBlk ;go to render it
BrickWithCoins:
ldan ++$00 ;initialize multi-coin timer flag
sta BrickCoinTimerFlag
BrickWithItem:
jsr GetAreaObjectID ;save area object ID
if Z80OPT3hy
ld hy,e;sty SCRATCHPAD+$07
else
sty SCRATCHPAD+$07
endif
ldan ++$00 ;load default adder for bricks with lines
ldy AreaType ;check level type for ground level
dey
beq BWithL ;if ground type, do not start with 5
ldan ++$05 ;otherwise use adder for bricks without lines
BWithL:
if Z80OPT3hy
;clc ;add object ID to adder
add a,hy;adci SCRATCHPAD+$07
else
clc ;add object ID to adder
adci SCRATCHPAD+$07
endif
tay ;use as offset for metatile
DrawQBlk: lday BrickQBlockMetatiles,y ;get appropriate metatile for brick (question block
pha ;if branched to here from question block routine)
jsr GetLrgObjAttrib ;get row from location byte
jmp DrawRow ;now render the object
GetAreaObjectID:
lda SCRATCHPAD+$00 ;get value saved from area parser routine
secsub
sbcn ++$00 ;possibly residual code
tay ;save to Y
ExitDecBlock: rts
;--------------------------------
HoleMetatiles:
.db $87, $00, $00, $00
Hole_Empty:
jsr ChkLrgObjLength ;get lower nybble and save as length
bcc NoWhirlP ;skip this part if length already loaded
lda AreaType ;check for water type level
checka
bne NoWhirlP ;if not water type, skip this part
ldx Whirlpool_Offset ;get offset for data used by cannons and whirlpools
jsr GetAreaObjXPosition ;get proper vertical coordinate of where we're at
secsub
sbcn ++$10 ;subtract 16 pixels
push af
stax Whirlpool_LeftExtent,x ;store as left extent of whirlpool
pop af
lda CurrentPageLoc ;get page location of where we're at
sbcn ++$00 ;subtract borrow
stax Whirlpool_PageLoc,x ;save as page location of whirlpool
iny
iny ;increment length by 2
tya
asl ;multiply by 16 to get size of whirlpool
asl ;note that whirlpool will always be
asl ;two blocks bigger than actual size of hole
asl ;and extend one block beyond each edge
stax Whirlpool_Length,x ;save size of whirlpool here
inx
cpxn ++$05 ;increment and check offset
cmpcy
bcc StrWOffset ;if not yet reached fifth whirlpool, branch to save offset
ldxn ++$00 ;otherwise initialize it
StrWOffset: stx Whirlpool_Offset ;save new offset here
NoWhirlP: ldx AreaType ;get appropriate metatile, then
ldax HoleMetatiles,x ;render the hole proper
ldxn ++$08
ldyn ++$0f ;start at ninth row and go to bottom, run RenderUnderPart
;--------------------------------
RenderUnderPart:
sty AreaObjectHeight ;store vertical length to render
ldyx MetatileBuffer,x ;check current spot to see if there's something
checky
beq DrawThisRow ;we need to keep, if nothing, go ahead
cpyn ++$17
beq WaitOneRow ;if middle part (tree ledge), wait until next row
cpyn ++$1a
beq WaitOneRow ;if middle part (mushroom ledge), wait until next row
cpyn ++$c0
beq DrawThisRow ;if question block w/ coin, overwrite
cpyn ++$c0
cmpcy
bcs WaitOneRow ;if any other metatile with palette 3, wait until next row
cpyn ++$54
bne DrawThisRow ;if cracked rock terrain, overwrite
cmpn ++$50
beq WaitOneRow ;if stem top of mushroom, wait until next row
DrawThisRow: stax MetatileBuffer,x ;render contents of A from routine that called this
WaitOneRow: inx
cpxn ++$0d ;stop rendering if we're at the bottom of the screen
cmpcy
bcs ExitUPartR
ldy AreaObjectHeight ;decrement, and stop rendering if there is no more length
dey
bpl RenderUnderPart
ExitUPartR: rts
;--------------------------------
ChkLrgObjLength:
jsr GetLrgObjAttrib ;get row location and size (length if branched to from here)
ChkLrgObjFixedLength:
ldax AreaObjectLength,x ;check for set length counter
clc ;clear carry flag for not just starting
checka
bpl LenSet ;if counter not set, load it, otherwise leave alone
tya ;save length into length counter
stax AreaObjectLength,x
sec ;set carry flag if just starting
LenSet: rts
GetLrgObjAttrib:
;x=???
;y=AreaData offset
ldyx AreaObjOffsetBuffer,x ;get offset saved from area obj decoding routine
ldayindirect (AreaData),y ;get first byte of level object
andn ++%00001111
if Z80OPT3hy
ld hy,a;sta SCRATCHPAD+$07 ;save row location
else
sta SCRATCHPAD+$07 ;save row location
endif
iny
ldayindirect (AreaData),y ;get next byte, save lower nybble (length or height)
andn ++%00001111 ;as Y, then leave
tay
rts
;--------------------------------
GetAreaObjXPosition:
lda CurrentColumnPos ;multiply current offset where we're at by 16
asl ;to obtain horizontal pixel coordinate
asl
asl
asl
rts
;--------------------------------
GetAreaObjYPosition:
if Z80OPT3hy
ld a,hy;lda SCRATCHPAD+$07
else
lda SCRATCHPAD+$07
endif
asl ;multiply value by 16
asl ;this will give us the proper vertical pixel coordinate
asl
asl
clc
adcn ++32 ;add 32 pixels for the status bar
rts
;-------------------------------------------------------------------------------------
;$06-$07 - used to store block buffer address used as indirect
if Z80OPT
else
BlockBufferAddr:
.db LOW Block_Buffer_1, LOW Block_Buffer_2
.db HIGH Block_Buffer_1, HIGH Block_Buffer_2
;a=%000hllll
;out: $06$07 = BlockBufferAddr[i]+%llll
GetBlockBufferAddr:
pha ;take value of A, save
lsr ;move high nybble to low
lsr
lsr
lsr
tay ;use nybble as pointer to high byte
lday BlockBufferAddr+2,y ;of indirect here
sta SCRATCHPAD+$07
pla
andn ++%00001111 ;pull from stack, mask out high nybble
clc
adcy BlockBufferAddr,y ;add to low byte
sta SCRATCHPAD+$06 ;store here and leave
rts
endif
;-------------------------------------------------------------------------------------
;unused space
.db $ff, $ff
;-------------------------------------------------------------------------------------
AreaDataOfsLoopback:
.db $12, $36, $0e, $0e, $0e, $32, $32, $32, $0a, $26, $40
;-------------------------------------------------------------------------------------
LoadAreaPointer:
jsr FindAreaPointer ;find it and store it here
sta AreaPointer
GetAreaType: andn ++%01100000 ;mask out all but d6 and d5
asl
rol
rol
rol ;make %0xx00000 into %000000xx
sta AreaType ;save 2 MSB as area type
rts
FindAreaPointer:
ldy WorldNumber ;load offset from world variable
lday WorldAddrOffsets,y
clc ;add area number used to find data
adci AreaNumber
tay
lday AreaAddrOffsets,y ;from there we have our area pointer
rts
GetAreaDataAddrs:
lda AreaPointer ;use 2 MSB for Y
jsr GetAreaType
tay
lda AreaPointer ;mask out all but 5 LSB
andn ++%00011111
sta AreaAddrsLOffset ;save as low offset
lday EnemyAddrHOffsets,y ;load base value with 2 altered MSB,
clc ;then add base value to 5 LSB, result
adci AreaAddrsLOffset ;becomes offset for level data
tay
lday EnemyDataAddrLow,y ;use offset to load pointer
sta EnemyDataLow
lday EnemyDataAddrHigh,y
sta EnemyDataHigh
ldy AreaType ;use area type as offset
lday AreaDataHOffsets,y ;do the same thing but with different base value
clc
adci AreaAddrsLOffset
tay
lday AreaDataAddrLow,y ;use this offset to load another pointer
sta AreaDataLow
lday AreaDataAddrHigh,y
sta AreaDataHigh
ldyn ++$00 ;load first byte of header
ldayindirect (AreaData),y
pha ;save it to the stack for now
andn ++%00000111 ;save 3 LSB for foreground scenery or bg color control
cmpn ++$04
cmpcy
bcc StoreFore
sta BackgroundColorCtrl ;if 4 or greater, save value here as bg color control
ldan ++$00
StoreFore: sta ForegroundScenery ;if less, save value here as foreground scenery
pla ;pull byte from stack and push it back
pha
andn ++%00111000 ;save player entrance control bits
lsr ;shift bits over to LSBs
lsr
lsr
sta PlayerEntranceCtrl ;save value here as player entrance control
pla ;pull byte again but do not push it back
andn ++%11000000 ;save 2 MSB for game timer setting
clc
rol ;rotate bits over to LSBs
rol
rol
sta GameTimerSetting ;save value here as game timer setting
iny
ldayindirect (AreaData),y ;load second byte of header
pha ;save to stack
andn ++%00001111 ;mask out all but lower nybble
sta TerrainControl
pla ;pull and push byte to copy it to A
pha
andn ++%00110000 ;save 2 MSB for background scenery type
lsr
lsr ;shift bits to LSBs
lsr
lsr
sta BackgroundScenery ;save as background scenery
pla
andn ++%11000000
clc
rol ;rotate bits over to LSBs
rol
rol
cmpn ++%00000011 ;if set to 3, store here
bne StoreStyle ;and nullify other value
sta CloudTypeOverride ;otherwise store value in other place
ldan ++$00
StoreStyle: sta AreaStyle
lda AreaDataLow ;increment area data address by 2 bytes
clc
adcn ++$02
sta AreaDataLow
lda AreaDataHigh
adcn ++$00
sta AreaDataHigh
rts
;-------------------------------------------------------------------------------------
;GAME LEVELS DATA
WorldAddrOffsets:
.db World1Areas-AreaAddrOffsets, World2Areas-AreaAddrOffsets
.db World3Areas-AreaAddrOffsets, World4Areas-AreaAddrOffsets
.db World5Areas-AreaAddrOffsets, World6Areas-AreaAddrOffsets
.db World7Areas-AreaAddrOffsets, World8Areas-AreaAddrOffsets
AreaAddrOffsets:
World1Areas: .db $25, $29, $c0, $26, $60
World2Areas: .db $28, $29, $01, $27, $62
World3Areas: .db $24, $35, $20, $63
World4Areas: .db $22, $29, $41, $2c, $61
World5Areas: .db $2a, $31, $26, $62
World6Areas: .db $2e, $23, $2d, $60
World7Areas: .db $33, $29, $01, $27, $64
World8Areas: .db $30, $32, $21, $65
;bonus area data offsets, included here for comparison purposes
;underground bonus area - c2
;cloud area 1 (day) - 2b
;cloud area 2 (night) - 34
;water area (5-2/6-2) - 00
;water area (8-4) - 02
;warp zone area (4-2) - 2f
EnemyAddrHOffsets:
.db $1f, $06, $1c, $00
EnemyDataAddrLow:
.db LOW E_CastleArea1, LOW E_CastleArea2, LOW E_CastleArea3, LOW E_CastleArea4, LOW E_CastleArea5, LOW E_CastleArea6
.db LOW E_GroundArea1, LOW E_GroundArea2, LOW E_GroundArea3, LOW E_GroundArea4, LOW E_GroundArea5, LOW E_GroundArea6
.db LOW E_GroundArea7, LOW E_GroundArea8, LOW E_GroundArea9, LOW E_GroundArea10, LOW E_GroundArea11, LOW E_GroundArea12
.db LOW E_GroundArea13, LOW E_GroundArea14, LOW E_GroundArea15, LOW E_GroundArea16, LOW E_GroundArea17, LOW E_GroundArea18
.db LOW E_GroundArea19, LOW E_GroundArea20, LOW E_GroundArea21, LOW E_GroundArea22, LOW E_UndergroundArea1
.db LOW E_UndergroundArea2, LOW E_UndergroundArea3, LOW E_WaterArea1, LOW E_WaterArea2, LOW E_WaterArea3
EnemyDataAddrHigh:
.db HIGH E_CastleArea1, HIGH E_CastleArea2, HIGH E_CastleArea3, HIGH E_CastleArea4, HIGH E_CastleArea5, HIGH E_CastleArea6
.db HIGH E_GroundArea1, HIGH E_GroundArea2, HIGH E_GroundArea3, HIGH E_GroundArea4, HIGH E_GroundArea5, HIGH E_GroundArea6
.db HIGH E_GroundArea7, HIGH E_GroundArea8, HIGH E_GroundArea9, HIGH E_GroundArea10, HIGH E_GroundArea11, HIGH E_GroundArea12
.db HIGH E_GroundArea13, HIGH E_GroundArea14, HIGH E_GroundArea15, HIGH E_GroundArea16, HIGH E_GroundArea17, HIGH E_GroundArea18
.db HIGH E_GroundArea19, HIGH E_GroundArea20, HIGH E_GroundArea21, HIGH E_GroundArea22, HIGH E_UndergroundArea1
.db HIGH E_UndergroundArea2, HIGH E_UndergroundArea3, HIGH E_WaterArea1, HIGH E_WaterArea2, HIGH E_WaterArea3
AreaDataHOffsets:
.db $00, $03, $19, $1c
AreaDataAddrLow:
.db LOW L_WaterArea1, LOW L_WaterArea2, LOW L_WaterArea3, LOW L_GroundArea1, LOW L_GroundArea2, LOW L_GroundArea3
.db LOW L_GroundArea4, LOW L_GroundArea5, LOW L_GroundArea6, LOW L_GroundArea7, LOW L_GroundArea8, LOW L_GroundArea9
.db LOW L_GroundArea10, LOW L_GroundArea11, LOW L_GroundArea12, LOW L_GroundArea13, LOW L_GroundArea14, LOW L_GroundArea15
.db LOW L_GroundArea16, LOW L_GroundArea17, LOW L_GroundArea18, LOW L_GroundArea19, LOW L_GroundArea20, LOW L_GroundArea21
.db LOW L_GroundArea22, LOW L_UndergroundArea1, LOW L_UndergroundArea2, LOW L_UndergroundArea3, LOW L_CastleArea1
.db LOW L_CastleArea2, LOW L_CastleArea3, LOW L_CastleArea4, LOW L_CastleArea5, LOW L_CastleArea6
AreaDataAddrHigh:
.db HIGH L_WaterArea1, HIGH L_WaterArea2, HIGH L_WaterArea3, HIGH L_GroundArea1, HIGH L_GroundArea2, HIGH L_GroundArea3
.db HIGH L_GroundArea4, HIGH L_GroundArea5, HIGH L_GroundArea6, HIGH L_GroundArea7, HIGH L_GroundArea8, HIGH L_GroundArea9
.db HIGH L_GroundArea10, HIGH L_GroundArea11, HIGH L_GroundArea12, HIGH L_GroundArea13, HIGH L_GroundArea14, HIGH L_GroundArea15
.db HIGH L_GroundArea16, HIGH L_GroundArea17, HIGH L_GroundArea18, HIGH L_GroundArea19, HIGH L_GroundArea20, HIGH L_GroundArea21
.db HIGH L_GroundArea22, HIGH L_UndergroundArea1, HIGH L_UndergroundArea2, HIGH L_UndergroundArea3, HIGH L_CastleArea1
.db HIGH L_CastleArea2, HIGH L_CastleArea3, HIGH L_CastleArea4, HIGH L_CastleArea5, HIGH L_CastleArea6
;ENEMY OBJECT DATA
;level 1-4/6-4
E_CastleArea1:
.db $76, $dd, $bb, $4c, $ea, $1d, $1b, $cc, $56, $5d
.db $16, $9d, $c6, $1d, $36, $9d, $c9, $1d, $04, $db
.db $49, $1d, $84, $1b, $c9, $5d, $88, $95, $0f, $08
.db $30, $4c, $78, $2d, $a6, $28, $90, $b5
.db $ff
;level 4-4
E_CastleArea2:
.db $0f, $03, $56, $1b, $c9, $1b, $0f, $07, $36, $1b
.db $aa, $1b, $48, $95, $0f, $0a, $2a, $1b, $5b, $0c
.db $78, $2d, $90, $b5
.db $ff
;level 2-4/5-4
E_CastleArea3:
.db $0b, $8c, $4b, $4c, $77, $5f, $eb, $0c, $bd, $db
.db $19, $9d, $75, $1d, $7d, $5b, $d9, $1d, $3d, $dd
.db $99, $1d, $26, $9d, $5a, $2b, $8a, $2c, $ca, $1b
.db $20, $95, $7b, $5c, $db, $4c, $1b, $cc, $3b, $cc
.db $78, $2d, $a6, $28, $90, $b5
.db $ff
;level 3-4
E_CastleArea4:
.db $0b, $8c, $3b, $1d, $8b, $1d, $ab, $0c, $db, $1d
.db $0f, $03, $65, $1d, $6b, $1b, $05, $9d, $0b, $1b
.db $05, $9b, $0b, $1d, $8b, $0c, $1b, $8c, $70, $15
.db $7b, $0c, $db, $0c, $0f, $08, $78, $2d, $a6, $28
.db $90, $b5
.db $ff
;level 7-4
E_CastleArea5:
.db $27, $a9, $4b, $0c, $68, $29, $0f, $06, $77, $1b
.db $0f, $0b, $60, $15, $4b, $8c, $78, $2d, $90, $b5
.db $ff
;level 8-4
E_CastleArea6:
.db $0f, $03, $8e, $65, $e1, $bb, $38, $6d, $a8, $3e, $e5, $e7
.db $0f, $08, $0b, $02, $2b, $02, $5e, $65, $e1, $bb, $0e
.db $db, $0e, $bb, $8e, $db, $0e, $fe, $65, $ec, $0f, $0d
.db $4e, $65, $e1, $0f, $0e, $4e, $02, $e0, $0f, $10, $fe, $e5, $e1
.db $1b, $85, $7b, $0c, $5b, $95, $78, $2d, $90, $b5
.db $ff
;level 3-3
E_GroundArea1:
.db $a5, $86, $e4, $28, $18, $a8, $45, $83, $69, $03
.db $c6, $29, $9b, $83, $16, $a4, $88, $24, $e9, $28
.db $05, $a8, $7b, $28, $24, $8f, $c8, $03, $e8, $03
.db $46, $a8, $85, $24, $c8, $24
.db $ff
;level 8-3
E_GroundArea2:
.db $eb, $8e, $0f, $03, $fb, $05, $17, $85, $db, $8e
.db $0f, $07, $57, $05, $7b, $05, $9b, $80, $2b, $85
.db $fb, $05, $0f, $0b, $1b, $05, $9b, $05
.db $ff
;level 4-1
E_GroundArea3:
.db $2e, $c2, $66, $e2, $11, $0f, $07, $02, $11, $0f, $0c
.db $12, $11
.db $ff
;level 6-2
E_GroundArea4:
.db $0e, $c2, $a8, $ab, $00, $bb, $8e, $6b, $82, $de, $00, $a0
.db $33, $86, $43, $06, $3e, $b4, $a0, $cb, $02, $0f, $07
.db $7e, $42, $a6, $83, $02, $0f, $0a, $3b, $02, $cb, $37
.db $0f, $0c, $e3, $0e
.db $ff
;level 3-1
E_GroundArea5:
.db $9b, $8e, $ca, $0e, $ee, $42, $44, $5b, $86, $80, $b8
.db $1b, $80, $50, $ba, $10, $b7, $5b, $00, $17, $85
.db $4b, $05, $fe, $34, $40, $b7, $86, $c6, $06, $5b, $80
.db $83, $00, $d0, $38, $5b, $8e, $8a, $0e, $a6, $00
.db $bb, $0e, $c5, $80, $f3, $00
.db $ff
;level 1-1
E_GroundArea6:
.db $1e, $c2, $00, $6b, $06, $8b, $86, $63, $b7, $0f, $05
.db $03, $06, $23, $06, $4b, $b7, $bb, $00, $5b, $b7
.db $fb, $37, $3b, $b7, $0f, $0b, $1b, $37
.db $ff
;level 1-3/5-3
E_GroundArea7:
.db $2b, $d7, $e3, $03, $c2, $86, $e2, $06, $76, $a5
.db $a3, $8f, $03, $86, $2b, $57, $68, $28, $e9, $28
.db $e5, $83, $24, $8f, $36, $a8, $5b, $03
.db $ff
;level 2-3/7-3
E_GroundArea8:
.db $0f, $02, $78, $40, $48, $ce, $f8, $c3, $f8, $c3
.db $0f, $07, $7b, $43, $c6, $d0, $0f, $8a, $c8, $50
.db $ff
;level 2-1
E_GroundArea9:
.db $85, $86, $0b, $80, $1b, $00, $db, $37, $77, $80
.db $eb, $37, $fe, $2b, $20, $2b, $80, $7b, $38, $ab, $b8
.db $77, $86, $fe, $42, $20, $49, $86, $8b, $06, $9b, $80
.db $7b, $8e, $5b, $b7, $9b, $0e, $bb, $0e, $9b, $80
;end of data terminator here is also used by pipe intro area
E_GroundArea10:
.db $ff
;level 5-1
E_GroundArea11:
.db $0b, $80, $60, $38, $10, $b8, $c0, $3b, $db, $8e
.db $40, $b8, $f0, $38, $7b, $8e, $a0, $b8, $c0, $b8
.db $fb, $00, $a0, $b8, $30, $bb, $ee, $42, $88, $0f, $0b
.db $2b, $0e, $67, $0e
.db $ff
;cloud level used in levels 2-1 and 5-2
E_GroundArea12:
.db $0a, $aa, $0e, $28, $2a, $0e, $31, $88
.db $ff
;level 4-3
E_GroundArea13:
.db $c7, $83, $d7, $03, $42, $8f, $7a, $03, $05, $a4
.db $78, $24, $a6, $25, $e4, $25, $4b, $83, $e3, $03
.db $05, $a4, $89, $24, $b5, $24, $09, $a4, $65, $24
.db $c9, $24, $0f, $08, $85, $25
.db $ff
;level 6-3
E_GroundArea14:
.db $cd, $a5, $b5, $a8, $07, $a8, $76, $28, $cc, $25
.db $65, $a4, $a9, $24, $e5, $24, $19, $a4, $0f, $07
.db $95, $28, $e6, $24, $19, $a4, $d7, $29, $16, $a9
.db $58, $29, $97, $29
.db $ff
;level 6-1
E_GroundArea15:
.db $0f, $02, $02, $11, $0f, $07, $02, $11
.db $ff
;warp zone area used in level 4-2
E_GroundArea16:
.db $ff
;level 8-1
E_GroundArea17:
.db $2b, $82, $ab, $38, $de, $42, $e2, $1b, $b8, $eb
.db $3b, $db, $80, $8b, $b8, $1b, $82, $fb, $b8, $7b
.db $80, $fb, $3c, $5b, $bc, $7b, $b8, $1b, $8e, $cb
.db $0e, $1b, $8e, $0f, $0d, $2b, $3b, $bb, $b8, $eb, $82
.db $4b, $b8, $bb, $38, $3b, $b7, $bb, $02, $0f, $13
.db $1b, $00, $cb, $80, $6b, $bc
.db $ff
;level 5-2
E_GroundArea18:
.db $7b, $80, $ae, $00, $80, $8b, $8e, $e8, $05, $f9, $86
.db $17, $86, $16, $85, $4e, $2b, $80, $ab, $8e, $87, $85
.db $c3, $05, $8b, $82, $9b, $02, $ab, $02, $bb, $86
.db $cb, $06, $d3, $03, $3b, $8e, $6b, $0e, $a7, $8e
.db $ff
;level 8-2
E_GroundArea19:
.db $29, $8e, $52, $11, $83, $0e, $0f, $03, $9b, $0e
.db $2b, $8e, $5b, $0e, $cb, $8e, $fb, $0e, $fb, $82
.db $9b, $82, $bb, $02, $fe, $42, $e8, $bb, $8e, $0f, $0a
.db $ab, $0e, $cb, $0e, $f9, $0e, $88, $86, $a6, $06
.db $db, $02, $b6, $8e
.db $ff
;level 7-1
E_GroundArea20:
.db $ab, $ce, $de, $42, $c0, $cb, $ce, $5b, $8e, $1b, $ce
.db $4b, $85, $67, $45, $0f, $07, $2b, $00, $7b, $85
.db $97, $05, $0f, $0a, $92, $02
.db $ff
;cloud level used in levels 3-1 and 6-2
E_GroundArea21:
.db $0a, $aa, $0e, $24, $4a, $1e, $23, $aa
.db $ff
;level 3-2
E_GroundArea22:
.db $1b, $80, $bb, $38, $4b, $bc, $eb, $3b, $0f, $04
.db $2b, $00, $ab, $38, $eb, $00, $cb, $8e, $fb, $80
.db $ab, $b8, $6b, $80, $fb, $3c, $9b, $bb, $5b, $bc
.db $fb, $00, $6b, $b8, $fb, $38
.db $ff
;level 1-2
E_UndergroundArea1:
.db $0b, $86, $1a, $06, $db, $06, $de, $c2, $02, $f0, $3b
.db $bb, $80, $eb, $06, $0b, $86, $93, $06, $f0, $39
.db $0f, $06, $60, $b8, $1b, $86, $a0, $b9, $b7, $27
.db $bd, $27, $2b, $83, $a1, $26, $a9, $26, $ee, $25, $0b
.db $27, $b4
.db $ff
;level 4-2
E_UndergroundArea2:
.db $0f, $02, $1e, $2f, $60, $e0, $3a, $a5, $a7, $db, $80
.db $3b, $82, $8b, $02, $fe, $42, $68, $70, $bb, $25, $a7
.db $2c, $27, $b2, $26, $b9, $26, $9b, $80, $a8, $82
.db $b5, $27, $bc, $27, $b0, $bb, $3b, $82, $87, $34
.db $ee, $25, $6b
.db $ff
;underground bonus rooms area used in many levels
E_UndergroundArea3:
.db $1e, $a5, $0a, $2e, $28, $27, $2e, $33, $c7, $0f, $03, $1e, $40, $07
.db $2e, $30, $e7, $0f, $05, $1e, $24, $44, $0f, $07, $1e, $22, $6a
.db $2e, $23, $ab, $0f, $09, $1e, $41, $68, $1e, $2a, $8a, $2e, $23, $a2
.db $2e, $32, $ea
.db $ff
;water area used in levels 5-2 and 6-2
E_WaterArea1:
.db $3b, $87, $66, $27, $cc, $27, $ee, $31, $87, $ee, $23, $a7
.db $3b, $87, $db, $07
.db $ff
;level 2-2/7-2
E_WaterArea2:
.db $0f, $01, $2e, $25, $2b, $2e, $25, $4b, $4e, $25, $cb, $6b, $07
.db $97, $47, $e9, $87, $47, $c7, $7a, $07, $d6, $c7
.db $78, $07, $38, $87, $ab, $47, $e3, $07, $9b, $87
.db $0f, $09, $68, $47, $db, $c7, $3b, $c7
.db $ff
;water area used in level 8-4
E_WaterArea3:
.db $47, $9b, $cb, $07, $fa, $1d, $86, $9b, $3a, $87
.db $56, $07, $88, $1b, $07, $9d, $2e, $65, $f0
.db $ff
;AREA OBJECT DATA
;level 1-4/6-4
L_CastleArea1:
.db $9b, $07
.db $05, $32, $06, $33, $07, $34, $ce, $03, $dc, $51
.db $ee, $07, $73, $e0, $74, $0a, $7e, $06, $9e, $0a
.db $ce, $06, $e4, $00, $e8, $0a, $fe, $0a, $2e, $89
.db $4e, $0b, $54, $0a, $14, $8a, $c4, $0a, $34, $8a
.db $7e, $06, $c7, $0a, $01, $e0, $02, $0a, $47, $0a
.db $81, $60, $82, $0a, $c7, $0a, $0e, $87, $7e, $02
.db $a7, $02, $b3, $02, $d7, $02, $e3, $02, $07, $82
.db $13, $02, $3e, $06, $7e, $02, $ae, $07, $fe, $0a
.db $0d, $c4, $cd, $43, $ce, $09, $de, $0b, $dd, $42
.db $fe, $02, $5d, $c7
.db $fd
;level 4-4
L_CastleArea2:
.db $5b, $07
.db $05, $32, $06, $33, $07, $34, $5e, $0a, $68, $64
.db $98, $64, $a8, $64, $ce, $06, $fe, $02, $0d, $01
.db $1e, $0e, $7e, $02, $94, $63, $b4, $63, $d4, $63
.db $f4, $63, $14, $e3, $2e, $0e, $5e, $02, $64, $35
.db $88, $72, $be, $0e, $0d, $04, $ae, $02, $ce, $08
.db $cd, $4b, $fe, $02, $0d, $05, $68, $31, $7e, $0a
.db $96, $31, $a9, $63, $a8, $33, $d5, $30, $ee, $02
.db $e6, $62, $f4, $61, $04, $b1, $08, $3f, $44, $33
.db $94, $63, $a4, $31, $e4, $31, $04, $bf, $08, $3f
.db $04, $bf, $08, $3f, $cd, $4b, $03, $e4, $0e, $03
.db $2e, $01, $7e, $06, $be, $02, $de, $06, $fe, $0a
.db $0d, $c4, $cd, $43, $ce, $09, $de, $0b, $dd, $42
.db $fe, $02, $5d, $c7
.db $fd
;level 2-4/5-4
L_CastleArea3:
.db $9b, $07
.db $05, $32, $06, $33, $07, $34, $fe, $00, $27, $b1
.db $65, $32, $75, $0a, $71, $00, $b7, $31, $08, $e4
.db $18, $64, $1e, $04, $57, $3b, $bb, $0a, $17, $8a
.db $27, $3a, $73, $0a, $7b, $0a, $d7, $0a, $e7, $3a
.db $3b, $8a, $97, $0a, $fe, $08, $24, $8a, $2e, $00
.db $3e, $40, $38, $64, $6f, $00, $9f, $00, $be, $43
.db $c8, $0a, $c9, $63, $ce, $07, $fe, $07, $2e, $81
.db $66, $42, $6a, $42, $79, $0a, $be, $00, $c8, $64
.db $f8, $64, $08, $e4, $2e, $07, $7e, $03, $9e, $07
.db $be, $03, $de, $07, $fe, $0a, $03, $a5, $0d, $44
.db $cd, $43, $ce, $09, $dd, $42, $de, $0b, $fe, $02
.db $5d, $c7
.db $fd
;level 3-4
L_CastleArea4:
.db $9b, $07
.db $05, $32, $06, $33, $07, $34, $fe, $06, $0c, $81
.db $39, $0a, $5c, $01, $89, $0a, $ac, $01, $d9, $0a
.db $fc, $01, $2e, $83, $a7, $01, $b7, $00, $c7, $01
.db $de, $0a, $fe, $02, $4e, $83, $5a, $32, $63, $0a
.db $69, $0a, $7e, $02, $ee, $03, $fa, $32, $03, $8a
.db $09, $0a, $1e, $02, $ee, $03, $fa, $32, $03, $8a
.db $09, $0a, $14, $42, $1e, $02, $7e, $0a, $9e, $07
.db $fe, $0a, $2e, $86, $5e, $0a, $8e, $06, $be, $0a
.db $ee, $07, $3e, $83, $5e, $07, $fe, $0a, $0d, $c4
.db $41, $52, $51, $52, $cd, $43, $ce, $09, $de, $0b
.db $dd, $42, $fe, $02, $5d, $c7
.db $fd
;level 7-4
L_CastleArea5:
.db $5b, $07
.db $05, $32, $06, $33, $07, $34, $fe, $0a, $ae, $86
.db $be, $07, $fe, $02, $0d, $02, $27, $32, $46, $61
.db $55, $62, $5e, $0e, $1e, $82, $68, $3c, $74, $3a
.db $7d, $4b, $5e, $8e, $7d, $4b, $7e, $82, $84, $62
.db $94, $61, $a4, $31, $bd, $4b, $ce, $06, $fe, $02
.db $0d, $06, $34, $31, $3e, $0a, $64, $32, $75, $0a
.db $7b, $61, $a4, $33, $ae, $02, $de, $0e, $3e, $82
.db $64, $32, $78, $32, $b4, $36, $c8, $36, $dd, $4b
.db $44, $b2, $58, $32, $94, $63, $a4, $3e, $ba, $30
.db $c9, $61, $ce, $06, $dd, $4b, $ce, $86, $dd, $4b
.db $fe, $02, $2e, $86, $5e, $02, $7e, $06, $fe, $02
.db $1e, $86, $3e, $02, $5e, $06, $7e, $02, $9e, $06
.db $fe, $0a, $0d, $c4, $cd, $43, $ce, $09, $de, $0b
.db $dd, $42, $fe, $02, $5d, $c7
.db $fd
;level 8-4
L_CastleArea6:
.db $5b, $06
.db $05, $32, $06, $33, $07, $34, $5e, $0a, $ae, $02
.db $0d, $01, $39, $73, $0d, $03, $39, $7b, $4d, $4b
.db $de, $06, $1e, $8a, $ae, $06, $c4, $33, $16, $fe
.db $a5, $77, $fe, $02, $fe, $82, $0d, $07, $39, $73
.db $a8, $74, $ed, $4b, $49, $fb, $e8, $74, $fe, $0a
.db $2e, $82, $67, $02, $84, $7a, $87, $31, $0d, $0b
.db $fe, $02, $0d, $0c, $39, $73, $5e, $06, $c6, $76
.db $45, $ff, $be, $0a, $dd, $48, $fe, $06, $3d, $cb
.db $46, $7e, $ad, $4a, $fe, $82, $39, $f3, $a9, $7b
.db $4e, $8a, $9e, $07, $fe, $0a, $0d, $c4, $cd, $43
.db $ce, $09, $de, $0b, $dd, $42, $fe, $02, $5d, $c7
.db $fd
;level 3-3
L_GroundArea1:
.db $94, $11
.db $0f, $26, $fe, $10, $28, $94, $65, $15, $eb, $12
.db $fa, $41, $4a, $96, $54, $40, $a4, $42, $b7, $13
.db $e9, $19, $f5, $15, $11, $80, $47, $42, $71, $13
.db $80, $41, $15, $92, $1b, $1f, $24, $40, $55, $12
.db $64, $40, $95, $12, $a4, $40, $d2, $12, $e1, $40
.db $13, $c0, $2c, $17, $2f, $12, $49, $13, $83, $40
.db $9f, $14, $a3, $40, $17, $92, $83, $13, $92, $41
.db $b9, $14, $c5, $12, $c8, $40, $d4, $40, $4b, $92
.db $78, $1b, $9c, $94, $9f, $11, $df, $14, $fe, $11
.db $7d, $c1, $9e, $42, $cf, $20
.db $fd
;level 8-3
L_GroundArea2:
.db $90, $b1
.db $0f, $26, $29, $91, $7e, $42, $fe, $40, $28, $92
.db $4e, $42, $2e, $c0, $57, $73, $c3, $25, $c7, $27
.db $23, $84, $33, $20, $5c, $01, $77, $63, $88, $62
.db $99, $61, $aa, $60, $bc, $01, $ee, $42, $4e, $c0
.db $69, $11, $7e, $42, $de, $40, $f8, $62, $0e, $c2
.db $ae, $40, $d7, $63, $e7, $63, $33, $a7, $37, $27
.db $43, $04, $cc, $01, $e7, $73, $0c, $81, $3e, $42
.db $0d, $0a, $5e, $40, $88, $72, $be, $42, $e7, $87
.db $fe, $40, $39, $e1, $4e, $00, $69, $60, $87, $60
.db $a5, $60, $c3, $31, $fe, $31, $6d, $c1, $be, $42
.db $ef, $20
.db $fd
;level 4-1
L_GroundArea3:
.db $52, $21
.db $0f, $20, $6e, $40, $58, $f2, $93, $01, $97, $00
.db $0c, $81, $97, $40, $a6, $41, $c7, $40, $0d, $04
.db $03, $01, $07, $01, $23, $01, $27, $01, $ec, $03
.db $ac, $f3, $c3, $03, $78, $e2, $94, $43, $47, $f3
.db $74, $43, $47, $fb, $74, $43, $2c, $f1, $4c, $63
.db $47, $00, $57, $21, $5c, $01, $7c, $72, $39, $f1
.db $ec, $02, $4c, $81, $d8, $62, $ec, $01, $0d, $0d
.db $0f, $38, $c7, $07, $ed, $4a, $1d, $c1, $5f, $26
.db $fd
;level 6-2
L_GroundArea4:
.db $54, $21
.db $0f, $26, $a7, $22, $37, $fb, $73, $20, $83, $07
.db $87, $02, $93, $20, $c7, $73, $04, $f1, $06, $31
.db $39, $71, $59, $71, $e7, $73, $37, $a0, $47, $04
.db $86, $7c, $e5, $71, $e7, $31, $33, $a4, $39, $71
.db $a9, $71, $d3, $23, $08, $f2, $13, $05, $27, $02
.db $49, $71, $75, $75, $e8, $72, $67, $f3, $99, $71
.db $e7, $20, $f4, $72, $f7, $31, $17, $a0, $33, $20
.db $39, $71, $73, $28, $bc, $05, $39, $f1, $79, $71
.db $a6, $21, $c3, $06, $d3, $20, $dc, $00, $fc, $00
.db $07, $a2, $13, $21, $5f, $32, $8c, $00, $98, $7a
.db $c7, $63, $d9, $61, $03, $a2, $07, $22, $74, $72
.db $77, $31, $e7, $73, $39, $f1, $58, $72, $77, $73
.db $d8, $72, $7f, $b1, $97, $73, $b6, $64, $c5, $65
.db $d4, $66, $e3, $67, $f3, $67, $8d, $c1, $cf, $26
.db $fd
;level 3-1
L_GroundArea5:
.db $52, $31
.db $0f, $20, $6e, $66, $07, $81, $36, $01, $66, $00
.db $a7, $22, $08, $f2, $67, $7b, $dc, $02, $98, $f2
.db $d7, $20, $39, $f1, $9f, $33, $dc, $27, $dc, $57
.db $23, $83, $57, $63, $6c, $51, $87, $63, $99, $61
.db $a3, $06, $b3, $21, $77, $f3, $f3, $21, $f7, $2a
.db $13, $81, $23, $22, $53, $00, $63, $22, $e9, $0b
.db $0c, $83, $13, $21, $16, $22, $33, $05, $8f, $35
.db $ec, $01, $63, $a0, $67, $20, $73, $01, $77, $01
.db $83, $20, $87, $20, $b3, $20, $b7, $20, $c3, $01
.db $c7, $00, $d3, $20, $d7, $20, $67, $a0, $77, $07
.db $87, $22, $e8, $62, $f5, $65, $1c, $82, $7f, $38
.db $8d, $c1, $cf, $26
.db $fd
;level 1-1
L_GroundArea6:
.db $50, $21
.db $07, $81, $47, $24, $57, $00, $63, $01, $77, $01
.db $c9, $71, $68, $f2, $e7, $73, $97, $fb, $06, $83
.db $5c, $01, $d7, $22, $e7, $00, $03, $a7, $6c, $02
.db $b3, $22, $e3, $01, $e7, $07, $47, $a0, $57, $06
.db $a7, $01, $d3, $00, $d7, $01, $07, $81, $67, $20
.db $93, $22, $03, $a3, $1c, $61, $17, $21, $6f, $33
.db $c7, $63, $d8, $62, $e9, $61, $fa, $60, $4f, $b3
.db $87, $63, $9c, $01, $b7, $63, $c8, $62, $d9, $61
.db $ea, $60, $39, $f1, $87, $21, $a7, $01, $b7, $20
.db $39, $f1, $5f, $38, $6d, $c1, $af, $26
.db $fd
;level 1-3/5-3
L_GroundArea7:
.db $90, $11
.db $0f, $26, $fe, $10, $2a, $93, $87, $17, $a3, $14
.db $b2, $42, $0a, $92, $19, $40, $36, $14, $50, $41
.db $82, $16, $2b, $93, $24, $41, $bb, $14, $b8, $00
.db $c2, $43, $c3, $13, $1b, $94, $67, $12, $c4, $15
.db $53, $c1, $d2, $41, $12, $c1, $29, $13, $85, $17
.db $1b, $92, $1a, $42, $47, $13, $83, $41, $a7, $13
.db $0e, $91, $a7, $63, $b7, $63, $c5, $65, $d5, $65
.db $dd, $4a, $e3, $67, $f3, $67, $8d, $c1, $ae, $42
.db $df, $20
.db $fd
;level 2-3/7-3
L_GroundArea8:
.db $90, $11
.db $0f, $26, $6e, $10, $8b, $17, $af, $32, $d8, $62
.db $e8, $62, $fc, $3f, $ad, $c8, $f8, $64, $0c, $be
.db $43, $43, $f8, $64, $0c, $bf, $73, $40, $84, $40
.db $93, $40, $a4, $40, $b3, $40, $f8, $64, $48, $e4
.db $5c, $39, $83, $40, $92, $41, $b3, $40, $f8, $64
.db $48, $e4, $5c, $39, $f8, $64, $13, $c2, $37, $65
.db $4c, $24, $63, $00, $97, $65, $c3, $42, $0b, $97
.db $ac, $32, $f8, $64, $0c, $be, $53, $45, $9d, $48
.db $f8, $64, $2a, $e2, $3c, $47, $56, $43, $ba, $62
.db $f8, $64, $0c, $b7, $88, $64, $bc, $31, $d4, $45
.db $fc, $31, $3c, $b1, $78, $64, $8c, $38, $0b, $9c
.db $1a, $33, $18, $61, $28, $61, $39, $60, $5d, $4a
.db $ee, $11, $0f, $b8, $1d, $c1, $3e, $42, $6f, $20
.db $fd
;level 2-1
L_GroundArea9:
.db $52, $31
.db $0f, $20, $6e, $40, $f7, $20, $07, $84, $17, $20
.db $4f, $34, $c3, $03, $c7, $02, $d3, $22, $27, $e3
.db $39, $61, $e7, $73, $5c, $e4, $57, $00, $6c, $73
.db $47, $a0, $53, $06, $63, $22, $a7, $73, $fc, $73
.db $13, $a1, $33, $05, $43, $21, $5c, $72, $c3, $23
.db $cc, $03, $77, $fb, $ac, $02, $39, $f1, $a7, $73
.db $d3, $04, $e8, $72, $e3, $22, $26, $f4, $bc, $02
.db $8c, $81, $a8, $62, $17, $87, $43, $24, $a7, $01
.db $c3, $04, $08, $f2, $97, $21, $a3, $02, $c9, $0b
.db $e1, $69, $f1, $69, $8d, $c1, $cf, $26
.db $fd
;pipe intro area
L_GroundArea10:
.db $38, $11
.db $0f, $26, $ad, $40, $3d, $c7
.db $fd
;level 5-1
L_GroundArea11:
.db $95, $b1
.db $0f, $26, $0d, $02, $c8, $72, $1c, $81, $38, $72
.db $0d, $05, $97, $34, $98, $62, $a3, $20, $b3, $06
.db $c3, $20, $cc, $03, $f9, $91, $2c, $81, $48, $62
.db $0d, $09, $37, $63, $47, $03, $57, $21, $8c, $02
.db $c5, $79, $c7, $31, $f9, $11, $39, $f1, $a9, $11
.db $6f, $b4, $d3, $65, $e3, $65, $7d, $c1, $bf, $26
.db $fd
;cloud level used in levels 2-1 and 5-2
L_GroundArea12:
.db $00, $c1
.db $4c, $00, $f4, $4f, $0d, $02, $02, $42, $43, $4f
.db $52, $c2, $de, $00, $5a, $c2, $4d, $c7
.db $fd
;level 4-3
L_GroundArea13:
.db $90, $51
.db $0f, $26, $ee, $10, $0b, $94, $33, $14, $42, $42
.db $77, $16, $86, $44, $02, $92, $4a, $16, $69, $42
.db $73, $14, $b0, $00, $c7, $12, $05, $c0, $1c, $17
.db $1f, $11, $36, $12, $8f, $14, $91, $40, $1b, $94
.db $35, $12, $34, $42, $60, $42, $61, $12, $87, $12
.db $96, $40, $a3, $14, $1c, $98, $1f, $11, $47, $12
.db $9f, $15, $cc, $15, $cf, $11, $05, $c0, $1f, $15
.db $39, $12, $7c, $16, $7f, $11, $82, $40, $98, $12
.db $df, $15, $16, $c4, $17, $14, $54, $12, $9b, $16
.db $28, $94, $ce, $01, $3d, $c1, $5e, $42, $8f, $20
.db $fd
;level 6-3
L_GroundArea14:
.db $97, $11
.db $0f, $26, $fe, $10, $2b, $92, $57, $12, $8b, $12
.db $c0, $41, $f7, $13, $5b, $92, $69, $0b, $bb, $12
.db $b2, $46, $19, $93, $71, $00, $17, $94, $7c, $14
.db $7f, $11, $93, $41, $bf, $15, $fc, $13, $ff, $11
.db $2f, $95, $50, $42, $51, $12, $58, $14, $a6, $12
.db $db, $12, $1b, $93, $46, $43, $7b, $12, $8d, $49
.db $b7, $14, $1b, $94, $49, $0b, $bb, $12, $fc, $13
.db $ff, $12, $03, $c1, $2f, $15, $43, $12, $4b, $13
.db $77, $13, $9d, $4a, $15, $c1, $a1, $41, $c3, $12
.db $fe, $01, $7d, $c1, $9e, $42, $cf, $20
.db $fd
;level 6-1
L_GroundArea15:
.db $52, $21
.db $0f, $20, $6e, $44, $0c, $f1, $4c, $01, $aa, $35
.db $d9, $34, $ee, $20, $08, $b3, $37, $32, $43, $04
.db $4e, $21, $53, $20, $7c, $01, $97, $21, $b7, $07
.db $9c, $81, $e7, $42, $5f, $b3, $97, $63, $ac, $02
.db $c5, $41, $49, $e0, $58, $61, $76, $64, $85, $65
.db $94, $66, $a4, $22, $a6, $03, $c8, $22, $dc, $02
.db $68, $f2, $96, $42, $13, $82, $17, $02, $af, $34
.db $f6, $21, $fc, $06, $26, $80, $2a, $24, $36, $01
.db $8c, $00, $ff, $35, $4e, $a0, $55, $21, $77, $20
.db $87, $07, $89, $22, $ae, $21, $4c, $82, $9f, $34
.db $ec, $01, $03, $e7, $13, $67, $8d, $4a, $ad, $41
.db $0f, $a6
.db $fd
;warp zone area used in level 4-2
L_GroundArea16:
.db $10, $51
.db $4c, $00, $c7, $12, $c6, $42, $03, $92, $02, $42
.db $29, $12, $63, $12, $62, $42, $69, $14, $a5, $12
.db $a4, $42, $e2, $14, $e1, $44, $f8, $16, $37, $c1
.db $8f, $38, $02, $bb, $28, $7a, $68, $7a, $a8, $7a
.db $e0, $6a, $f0, $6a, $6d, $c5
.db $fd
;level 8-1
L_GroundArea17:
.db $92, $31
.db $0f, $20, $6e, $40, $0d, $02, $37, $73, $ec, $00
.db $0c, $80, $3c, $00, $6c, $00, $9c, $00, $06, $c0
.db $c7, $73, $06, $83, $28, $72, $96, $40, $e7, $73
.db $26, $c0, $87, $7b, $d2, $41, $39, $f1, $c8, $f2
.db $97, $e3, $a3, $23, $e7, $02, $e3, $07, $f3, $22
.db $37, $e3, $9c, $00, $bc, $00, $ec, $00, $0c, $80
.db $3c, $00, $86, $21, $a6, $06, $b6, $24, $5c, $80
.db $7c, $00, $9c, $00, $29, $e1, $dc, $05, $f6, $41
.db $dc, $80, $e8, $72, $0c, $81, $27, $73, $4c, $01
.db $66, $74, $0d, $11, $3f, $35, $b6, $41, $2c, $82
.db $36, $40, $7c, $02, $86, $40, $f9, $61, $39, $e1
.db $ac, $04, $c6, $41, $0c, $83, $16, $41, $88, $f2
.db $39, $f1, $7c, $00, $89, $61, $9c, $00, $a7, $63
.db $bc, $00, $c5, $65, $dc, $00, $e3, $67, $f3, $67
.db $8d, $c1, $cf, $26
.db $fd
;level 5-2
L_GroundArea18:
.db $55, $b1
.db $0f, $26, $cf, $33, $07, $b2, $15, $11, $52, $42
.db $99, $0b, $ac, $02, $d3, $24, $d6, $42, $d7, $25
.db $23, $84, $cf, $33, $07, $e3, $19, $61, $78, $7a
.db $ef, $33, $2c, $81, $46, $64, $55, $65, $65, $65
.db $ec, $74, $47, $82, $53, $05, $63, $21, $62, $41
.db $96, $22, $9a, $41, $cc, $03, $b9, $91, $39, $f1
.db $63, $26, $67, $27, $d3, $06, $fc, $01, $18, $e2
.db $d9, $07, $e9, $04, $0c, $86, $37, $22, $93, $24
.db $87, $84, $ac, $02, $c2, $41, $c3, $23, $d9, $71
.db $fc, $01, $7f, $b1, $9c, $00, $a7, $63, $b6, $64
.db $cc, $00, $d4, $66, $e3, $67, $f3, $67, $8d, $c1
.db $cf, $26
.db $fd
;level 8-2
L_GroundArea19:
.db $50, $b1
.db $0f, $26, $fc, $00, $1f, $b3, $5c, $00, $65, $65
.db $74, $66, $83, $67, $93, $67, $dc, $73, $4c, $80
.db $b3, $20, $c9, $0b, $c3, $08, $d3, $2f, $dc, $00
.db $2c, $80, $4c, $00, $8c, $00, $d3, $2e, $ed, $4a
.db $fc, $00, $d7, $a1, $ec, $01, $4c, $80, $59, $11
.db $d8, $11, $da, $10, $37, $a0, $47, $04, $99, $11
.db $e7, $21, $3a, $90, $67, $20, $76, $10, $77, $60
.db $87, $07, $d8, $12, $39, $f1, $ac, $00, $e9, $71
.db $0c, $80, $2c, $00, $4c, $05, $c7, $7b, $39, $f1
.db $ec, $00, $f9, $11, $0c, $82, $6f, $34, $f8, $11
.db $fa, $10, $7f, $b2, $ac, $00, $b6, $64, $cc, $01
.db $e3, $67, $f3, $67, $8d, $c1, $cf, $26
.db $fd
;level 7-1
L_GroundArea20:
.db $52, $b1
.db $0f, $20, $6e, $45, $39, $91, $b3, $04, $c3, $21
.db $c8, $11, $ca, $10, $49, $91, $7c, $73, $e8, $12
.db $88, $91, $8a, $10, $e7, $21, $05, $91, $07, $30
.db $17, $07, $27, $20, $49, $11, $9c, $01, $c8, $72
.db $23, $a6, $27, $26, $d3, $03, $d8, $7a, $89, $91
.db $d8, $72, $39, $f1, $a9, $11, $09, $f1, $63, $24
.db $67, $24, $d8, $62, $28, $91, $2a, $10, $56, $21
.db $70, $04, $79, $0b, $8c, $00, $94, $21, $9f, $35
.db $2f, $b8, $3d, $c1, $7f, $26
.db $fd
;cloud level used in levels 3-1 and 6-2
L_GroundArea21:
.db $06, $c1
.db $4c, $00, $f4, $4f, $0d, $02, $06, $20, $24, $4f
.db $35, $a0, $36, $20, $53, $46, $d5, $20, $d6, $20
.db $34, $a1, $73, $49, $74, $20, $94, $20, $b4, $20
.db $d4, $20, $f4, $20, $2e, $80, $59, $42, $4d, $c7
.db $fd
;level 3-2
L_GroundArea22:
.db $96, $31
.db $0f, $26, $0d, $03, $1a, $60, $77, $42, $c4, $00
.db $c8, $62, $b9, $e1, $d3, $06, $d7, $07, $f9, $61
.db $0c, $81, $4e, $b1, $8e, $b1, $bc, $01, $e4, $50
.db $e9, $61, $0c, $81, $0d, $0a, $84, $43, $98, $72
.db $0d, $0c, $0f, $38, $1d, $c1, $5f, $26
.db $fd
;level 1-2
L_UndergroundArea1:
.db $48, $0f
.db $0e, $01, $5e, $02, $a7, $00, $bc, $73, $1a, $e0
.db $39, $61, $58, $62, $77, $63, $97, $63, $b8, $62
.db $d6, $07, $f8, $62, $19, $e1, $75, $52, $86, $40
.db $87, $50, $95, $52, $93, $43, $a5, $21, $c5, $52
.db $d6, $40, $d7, $20, $e5, $06, $e6, $51, $3e, $8d
.db $5e, $03, $67, $52, $77, $52, $7e, $02, $9e, $03
.db $a6, $43, $a7, $23, $de, $05, $fe, $02, $1e, $83
.db $33, $54, $46, $40, $47, $21, $56, $04, $5e, $02
.db $83, $54, $93, $52, $96, $07, $97, $50, $be, $03
.db $c7, $23, $fe, $02, $0c, $82, $43, $45, $45, $24
.db $46, $24, $90, $08, $95, $51, $78, $fa, $d7, $73
.db $39, $f1, $8c, $01, $a8, $52, $b8, $52, $cc, $01
.db $5f, $b3, $97, $63, $9e, $00, $0e, $81, $16, $24
.db $66, $04, $8e, $00, $fe, $01, $08, $d2, $0e, $06
.db $6f, $47, $9e, $0f, $0e, $82, $2d, $47, $28, $7a
.db $68, $7a, $a8, $7a, $ae, $01, $de, $0f, $6d, $c5
.db $fd
;level 4-2
L_UndergroundArea2:
.db $48, $0f
.db $0e, $01, $5e, $02, $bc, $01, $fc, $01, $2c, $82
.db $41, $52, $4e, $04, $67, $25, $68, $24, $69, $24
.db $ba, $42, $c7, $04, $de, $0b, $b2, $87, $fe, $02
.db $2c, $e1, $2c, $71, $67, $01, $77, $00, $87, $01
.db $8e, $00, $ee, $01, $f6, $02, $03, $85, $05, $02
.db $13, $21, $16, $02, $27, $02, $2e, $02, $88, $72
.db $c7, $20, $d7, $07, $e4, $76, $07, $a0, $17, $06
.db $48, $7a, $76, $20, $98, $72, $79, $e1, $88, $62
.db $9c, $01, $b7, $73, $dc, $01, $f8, $62, $fe, $01
.db $08, $e2, $0e, $00, $6e, $02, $73, $20, $77, $23
.db $83, $04, $93, $20, $ae, $00, $fe, $0a, $0e, $82
.db $39, $71, $a8, $72, $e7, $73, $0c, $81, $8f, $32
.db $ae, $00, $fe, $04, $04, $d1, $17, $04, $26, $49
.db $27, $29, $df, $33, $fe, $02, $44, $f6, $7c, $01
.db $8e, $06, $bf, $47, $ee, $0f, $4d, $c7, $0e, $82
.db $68, $7a, $ae, $01, $de, $0f, $6d, $c5
.db $fd
;underground bonus rooms area used in many levels
L_UndergroundArea3:
.db $48, $01
.db $0e, $01, $00, $5a, $3e, $06, $45, $46, $47, $46
.db $53, $44, $ae, $01, $df, $4a, $4d, $c7, $0e, $81
.db $00, $5a, $2e, $04, $37, $28, $3a, $48, $46, $47
.db $c7, $07, $ce, $0f, $df, $4a, $4d, $c7, $0e, $81
.db $00, $5a, $33, $53, $43, $51, $46, $40, $47, $50
.db $53, $04, $55, $40, $56, $50, $62, $43, $64, $40
.db $65, $50, $71, $41, $73, $51, $83, $51, $94, $40
.db $95, $50, $a3, $50, $a5, $40, $a6, $50, $b3, $51
.db $b6, $40, $b7, $50, $c3, $53, $df, $4a, $4d, $c7
.db $0e, $81, $00, $5a, $2e, $02, $36, $47, $37, $52
.db $3a, $49, $47, $25, $a7, $52, $d7, $04, $df, $4a
.db $4d, $c7, $0e, $81, $00, $5a, $3e, $02, $44, $51
.db $53, $44, $54, $44, $55, $24, $a1, $54, $ae, $01
.db $b4, $21, $df, $4a, $e5, $07, $4d, $c7
.db $fd
;water area used in levels 5-2 and 6-2
L_WaterArea1:
.db $41, $01
.db $b4, $34, $c8, $52, $f2, $51, $47, $d3, $6c, $03
.db $65, $49, $9e, $07, $be, $01, $cc, $03, $fe, $07
.db $0d, $c9, $1e, $01, $6c, $01, $62, $35, $63, $53
.db $8a, $41, $ac, $01, $b3, $53, $e9, $51, $26, $c3
.db $27, $33, $63, $43, $64, $33, $ba, $60, $c9, $61
.db $ce, $0b, $e5, $09, $ee, $0f, $7d, $ca, $7d, $47
.db $fd
;level 2-2/7-2
L_WaterArea2:
.db $41, $01
.db $b8, $52, $ea, $41, $27, $b2, $b3, $42, $16, $d4
.db $4a, $42, $a5, $51, $a7, $31, $27, $d3, $08, $e2
.db $16, $64, $2c, $04, $38, $42, $76, $64, $88, $62
.db $de, $07, $fe, $01, $0d, $c9, $23, $32, $31, $51
.db $98, $52, $0d, $c9, $59, $42, $63, $53, $67, $31
.db $14, $c2, $36, $31, $87, $53, $17, $e3, $29, $61
.db $30, $62, $3c, $08, $42, $37, $59, $40, $6a, $42
.db $99, $40, $c9, $61, $d7, $63, $39, $d1, $58, $52
.db $c3, $67, $d3, $31, $dc, $06, $f7, $42, $fa, $42
.db $23, $b1, $43, $67, $c3, $34, $c7, $34, $d1, $51
.db $43, $b3, $47, $33, $9a, $30, $a9, $61, $b8, $62
.db $be, $0b, $d5, $09, $de, $0f, $0d, $ca, $7d, $47
.db $fd
;water area used in level 8-4
L_WaterArea3:
.db $49, $0f
.db $1e, $01, $39, $73, $5e, $07, $ae, $0b, $1e, $82
.db $6e, $88, $9e, $02, $0d, $04, $2e, $0b, $45, $09
.db $4e, $0f, $ed, $47
.db $fd
;-------------------------------------------------------------------------------------
if Z80==0
;unused space
.db $ff
endif
;-------------------------------------------------------------------------------------
;indirect jump routine called when
;$0770 is set to 1
GameMode:
lda OperMode_Task
jsr JumpEngine
.dw InitializeArea
.dw ScreenRoutines
.dw SecondaryGameSetup
.dw GameCoreRoutine
;-------------------------------------------------------------------------------------
GameCoreRoutine:
ldx CurrentPlayer ;get which player is on the screen
ldax SavedJoypadBits,x ;use appropriate player's controller bits
;or a
;jr nz,$
sta SavedJoypadBits ;as the master controller bits
jsr GameRoutines ;execute one of many possible subs ;9907t (3 Goomba + 2 юўъш + ╠рЁшю)
lda OperMode_Task ;check major task of operating mode
cmpn ++$03 ;if we are supposed to be here,
cmpcy
bcs GameEngine ;branch to the game engine itself
rts
GameEngine:
jsr ProcFireball_Bubble ;process fireballs and air bubbles
ldxn ++$00
ProcELoop: stx ObjectOffset ;put incremented offset in X as enemy object offset
jsr EnemiesAndLoopsCore ;process enemy objects ;8639+8388+9593+999+999+404t (3 Goomba + 2 юўъш + ╠рЁшю) ;<------------
jsr FloateyNumbersRoutine ;process floatey numbers ;73+941+73+73+73+73t (3 Goomba + 2 юўъш + ╠рЁшю)
inx
cpxn ++$06 ;do these two subroutines until the whole buffer is done
bne ProcELoop
jsr GetPlayerOffscreenBits ;get offscreen bits for player object ;726t (3 Goomba + 2 юўъш + ╠рЁшю)
jsr RelativePlayerPosition ;get relative coordinates for player object ;228t (3 Goomba + 2 юўъш + ╠рЁшю)
if 0;Z80
ld hl,(logicframe)
dec l
call z,PlayerGfxHandler ;Єюы№ъю т яюёыхфэхь ърфЁх ыюушъш (эхяюёЁхфёЄтхээю яхЁхф юЄюсЁрцхэшхь) ;эх яюыєўрхЄё - юўхэ№ ьхфыхээю шф╕Є ьшурэшх яЁш ЁюёЄх ш єьхэ№°хэшш
else
jsr PlayerGfxHandler ;draw the player ;3074t (3 Goomba + 2 юўъш + ╠рЁшю) ;<--------------
endif
jsr BlockObjMT_Updater ;replace block objects with metatiles if necessary ;250t (3 Goomba + 2 юўъш + ╠рЁшю)
ldxn ++$01
stx ObjectOffset ;set offset for second
jsr BlockObjectsCore ;process second block object ;101t (3 Goomba + 2 юўъш + ╠рЁшю)
dex
stx ObjectOffset ;set offset for first
jsr BlockObjectsCore ;process first block object ;101t (3 Goomba + 2 юўъш + ╠рЁшю)
jsr MiscObjectsCore ;process misc objects (hammer, jumping coins) ;3249t (3 Goomba + 2 юўъш + ╠рЁшю) ;<---------------
jsr ProcessCannons ;process bullet bill cannons ;431t (3 Goomba + 2 юўъш + ╠рЁшю)
jsr ProcessWhirlpools ;process whirlpools ;58t (3 Goomba + 2 юўъш + ╠рЁшю)
jsr FlagpoleRoutine ;process the flagpole ;96t (3 Goomba + 2 юўъш + ╠рЁшю)
jsr RunGameTimer ;count down the game timer ;174t (3 Goomba + 2 юўъш + ╠рЁшю)
jsr ColorRotation ;cycle one of the background colors ;57t (3 Goomba + 2 юўъш + ╠рЁшю)
lda Player_Y_HighPos
cmpn ++$02 ;if player is below the screen, don't bother with the music
bpl NoChgMus
lda StarInvincibleTimer ;if star mario invincibility timer at zero,
checka
beq ClrPlrPal ;skip this part
cmpn ++$04
bne NoChgMus ;if not yet at a certain point, continue
lda IntervalTimerControl ;if interval timer not yet expired,
checka
bne NoChgMus ;branch ahead, don't bother with the music
jsr GetAreaMusic ;to re-attain appropriate level music
NoChgMus: ldy StarInvincibleTimer ;get invincibility timer
lda FrameCounter ;get frame counter
cpyn ++$08 ;if timer still above certain point,
cmpcy
bcs CycleTwo ;branch to cycle player's palette quickly
lsr ;otherwise, divide by 8 to cycle every eighth frame
lsr
CycleTwo: lsr ;if branched here, divide by 2 to cycle every other frame
jsr CyclePlayerPalette ;do sub to cycle the palette (note: shares fire flower code)
jmp SaveAB ;then skip this sub to finish up the game engine
ClrPlrPal: jsr ResetPalStar ;do sub to clear player's palette bits in attributes
SaveAB: lda A_B_Buttons ;save current A and B button
sta PreviousA_B_Buttons ;into temp variable to be used on next frame
ldan ++$00
sta Left_Right_Buttons ;nullify left and right buttons temp variable
UpdScrollVar: lda VRAM_Buffer_AddrCtrl
cmpn ++$06 ;if vram address controller set to 6 (one of two $0341s) (VRAM_Buffer2)
beq ExitEng ;then branch to leave
lda AreaParserTaskNum ;otherwise check number of tasks
checka
bne RunParser
lda ScrollThirtyTwo ;get horizontal scroll in 0-31 or $00-$20 range
cmpn ++$20 ;check to see if exceeded $21
bmi ExitEng ;branch to leave if not
lda ScrollThirtyTwo
sbcn ++$20 ;otherwise subtract $20 to set appropriately
sta ScrollThirtyTwo ;and store
ldan ++$00 ;reset vram buffer offset used in conjunction with
sta VRAM_Buffer2_Offset ;level graphics buffer at $0341-$035f
RunParser: jsr AreaParserTaskHandler ;update the name table with more level graphics
ExitEng: rts ;and after all that, we're finally done!
;-------------------------------------------------------------------------------------
ScrollHandler:
lda Player_X_Scroll ;load value saved here
clc
adci Platform_X_Scroll ;add value used by left/right platforms
sta Player_X_Scroll ;save as new value here to impose force on scroll
lda ScrollLock ;check scroll lock flag
checka
bne InitScrlAmt ;skip a bunch of code here if set
lda Player_Pos_ForScroll
cmpn ++$50 ;check player's horizontal screen position
cmpcy
bcc InitScrlAmt ;if less than 80 pixels to the right, branch
lda SideCollisionTimer ;if timer related to player's side collision
checka
bne InitScrlAmt ;not expired, branch
ldy Player_X_Scroll ;get value and decrement by one
dey ;if value originally set to zero or otherwise
bmi InitScrlAmt ;negative for left movement, branch
iny
cpyn ++$02 ;if value $01, branch and do not decrement
cmpcy
bcc ChkNearMid
dey ;otherwise decrement by one
ChkNearMid: lda Player_Pos_ForScroll
cmpn ++$70 ;check player's horizontal screen position
cmpcy
bcc ScrollScreen ;if less than 112 pixels to the right, branch
ldy Player_X_Scroll ;otherwise get original value undecremented
ScrollScreen:
tya
sta ScrollAmount ;save value here
clc
adci ScrollThirtyTwo ;add to value already set here
sta ScrollThirtyTwo ;save as new value here
tya
clc
adci ScreenLeft_X_Pos ;add to left side coordinate
sta ScreenLeft_X_Pos ;save as new left side coordinate
sta HorizontalScroll ;save here also
lda ScreenLeft_PageLoc
adcn ++$00 ;add carry to page location for left
sta ScreenLeft_PageLoc ;side of the screen
andn ++$01 ;get LSB of page location
sta SCRATCHPAD+$00 ;save as temp variable for PPU register 1 mirror
lda Mirror_PPU_CTRL_REG1 ;get PPU register 1 mirror
andn ++%11111110 ;save all bits except d0
orai SCRATCHPAD+$00 ;get saved bit here and save in PPU register 1
sta Mirror_PPU_CTRL_REG1 ;mirror to be used to set name table later
jsr GetScreenPosition ;figure out where the right side is
ldan ++$08
sta ScrollIntervalTimer ;set scroll timer (residual, not used elsewhere)
jmp ChkPOffscr ;skip this part
InitScrlAmt: ldan ++$00
sta ScrollAmount ;initialize value here
ChkPOffscr: ldxn ++$00 ;set X for player offset
jsr GetXOffscreenBits ;get horizontal offscreen bits for player
sta SCRATCHPAD+$00 ;save them here
ldyn ++$00 ;load default offset (left side)
asl ;if d7 of offscreen bits are set, ;TODO >>4 т GetXOffscreenBits
bcs KeepOnscr ;branch with default offset
iny ;otherwise use different offset (right side)
lda SCRATCHPAD+$00
andn ++%00100000 ;check offscreen bits for d5 set ;TODO >>4 т GetXOffscreenBits
beq InitPlatScrl ;if not set, branch ahead of this part
KeepOnscr: lday ScreenEdge_X_Pos,y ;get left or right side coordinate based on offset
secsub
sbcy X_SubtracterData,y ;subtract amount based on offset
sta Player_X_Position ;store as player position to prevent movement further
ldaykeepcy ScreenEdge_PageLoc,y ;get left or right page location based on offset
sbcn ++$00 ;subtract borrow
sta Player_PageLoc ;save as player's page location
lda Left_Right_Buttons ;check saved controller bits
cmpy OffscrJoypadBitsData,y ;against bits based on offset
beq InitPlatScrl ;if not equal, branch
ldan ++$00
sta Player_X_Speed ;otherwise nullify horizontal speed of player
InitPlatScrl: ldan ++$00 ;nullify platform force imposed on scroll
sta Platform_X_Scroll
rts
X_SubtracterData:
.db $00, $10
OffscrJoypadBitsData:
.db $01, $02
;-------------------------------------------------------------------------------------
GetScreenPosition:
lda ScreenLeft_X_Pos ;get coordinate of screen's left boundary
clc
adcn ++$ff ;add 255 pixels
sta ScreenRight_X_Pos ;store as coordinate of screen's right boundary
lda ScreenLeft_PageLoc ;get page number where left boundary is
adcn ++$00 ;add carry from before
sta ScreenRight_PageLoc ;store as page number where right boundary is
rts
;-------------------------------------------------------------------------------------
GameRoutines:
lda GameEngineSubroutine ;run routine based on number (a few of these routines are
jsr JumpEngine ;merely placeholders as conditions for other routines)
.dw Entrance_GameTimerSetup
.dw Vine_AutoClimb
.dw SideExitPipeEntry
.dw VerticalPipeEntry
.dw FlagpoleSlide
.dw PlayerEndLevel
.dw PlayerLoseLife
.dw PlayerEntrance
.dw PlayerCtrlRoutine
.dw PlayerChangeSize
.dw PlayerInjuryBlink
.dw PlayerDeath
.dw PlayerFireFlower
;-------------------------------------------------------------------------------------
PlayerEntrance:
lda AltEntranceControl ;check for mode of alternate entry
cmpn ++$02
beq EntrMode2 ;if found, branch to enter from pipe or with vine
ldan ++$00
ldy Player_Y_Position ;if vertical position above a certain
cpyn ++$30 ;point, nullify controller bits and continue
cmpcy
bcc AutoControlPlayer ;with player movement code, do not return
lda PlayerEntranceCtrl ;check player entry bits from header
cmpn ++$06
beq ChkBehPipe ;if set to 6 or 7, execute pipe intro code
cmpn ++$07 ;otherwise branch to normal entry
bne PlayerRdy
ChkBehPipe: lda Player_SprAttrib ;check for sprite attributes
checka
bne IntroEntr ;branch if found
ldan ++$01
jmp AutoControlPlayer ;force player to walk to the right
IntroEntr: jsr EnterSidePipe ;execute sub to move player to the right
deci ChangeAreaTimer ;decrement timer for change of area
bne ExitEntr ;branch to exit if not yet expired
inci DisableIntermediate ;set flag to skip world and lives display
jmp NextArea ;jump to increment to next area and set modes
EntrMode2: lda JoypadOverride ;if controller override bits set here,
checka
bne VineEntr ;branch to enter with vine
ldan ++$ff ;otherwise, set value here then execute sub
jsr MovePlayerYAxis ;to move player upwards (note $ff = -1)
lda Player_Y_Position ;check to see if player is at a specific coordinate
cmpn ++$91 ;if player risen to a certain point (this requires pipes
cmpcy
bcc PlayerRdy ;to be at specific height to look/function right) branch
rts ;to the last part, otherwise leave
VineEntr: lda VineHeight
cmpn ++$60 ;check vine height
bne ExitEntr ;if vine not yet reached maximum height, branch to leave
lda Player_Y_Position ;get player's vertical coordinate
cmpn ++$99 ;check player's vertical coordinate against preset value
cmpcy
ldyn ++$00 ;load default values to be written to
ldan ++$01 ;this value moves player to the right off the vine
bcc OffVine ;if vertical coordinate LOW preset value, use defaults
ldan ++$03
sta Player_State ;otherwise set player state to climbing
iny ;increment value in Y
ldan ++$08 ;set block in block buffer to cover hole, then
sta Block_Buffer_1+$b4 ;use same value to force player to climb
OffVine: sty DisableCollisionDet ;set collision detection disable flag
jsr AutoControlPlayer ;use contents of A to move player up or right, execute sub
lda Player_X_Position
cmpn ++$48 ;check player's horizontal position
cmpcy
bcc ExitEntr ;if not far enough to the right, branch to leave
PlayerRdy: ldan ++$08 ;set routine to be executed by game engine next frame
sta GameEngineSubroutine
ldan ++$01 ;set to face player to the right
sta PlayerFacingDir
lsr ;init A
sta AltEntranceControl ;init mode of entry
sta DisableCollisionDet ;init collision detection disable flag
sta JoypadOverride ;nullify controller override bits
ExitEntr: rts ;leave!
;-------------------------------------------------------------------------------------
;$07 - used to hold upper limit of high byte when player falls down hole
AutoControlPlayer:
sta SavedJoypadBits ;override controller bits with contents of A if executing here
PlayerCtrlRoutine:
lda GameEngineSubroutine ;check task here
cmpn ++$0b ;if certain value is set, branch to skip controller bit loading (playerdeath)
beq SizeChk
lda AreaType ;are we in a water type area? є эрё ёэрўрыр 1???
checka
bne SaveJoyp ;if not, branch
ldy Player_Y_HighPos
dey ;if not in vertical area between
bne DisJoyp ;status bar and bottom, branch
lda Player_Y_Position
cmpn ++$d0 ;if nearing the bottom of the screen or
cmpcy
bcc SaveJoyp ;not in the vertical area between status bar or bottom,
DisJoyp: ldan ++$00 ;disable controller bits
sta SavedJoypadBits
SaveJoyp: lda SavedJoypadBits ;otherwise store A and B buttons in $0a
andn ++%11000000
sta A_B_Buttons
lda SavedJoypadBits ;store left and right buttons in $0c
andn ++%00000011
sta Left_Right_Buttons
lda SavedJoypadBits ;store up and down buttons in $0b
andn ++%00001100
sta Up_Down_Buttons
andn ++%00000100 ;check for pressing down
beq SizeChk ;if not, branch
lda Player_State ;check player's state
checka
bne SizeChk ;if not on the ground, branch
ldy Left_Right_Buttons ;check left and right
checky
beq SizeChk ;if neither pressed, branch
ldan ++$00
sta Left_Right_Buttons ;if pressing down while on the ground,
sta Up_Down_Buttons ;nullify directional bits
SizeChk: jsr PlayerMovementSubs ;run movement subroutines
ldyn ++$01 ;is player small?
lda PlayerSize
checka
bne ChkMoveDir
ldyn ++$00 ;check for if crouching
lda CrouchingFlag
checka
beq ChkMoveDir ;if not, branch ahead
ldyn ++$02 ;if big and crouching, load y with 2
ChkMoveDir: sty Player_BoundBoxCtrl ;set contents of Y as player's bounding box size control
ldan ++$01 ;set moving direction to right by default
ldy Player_X_Speed ;check player's horizontal speed
checky
beq PlayerSubs ;if not moving at all horizontally, skip this part
bpl SetMoveDir ;if moving to the right, use default moving direction
asl ;otherwise change to move to the left
SetMoveDir: sta Player_MovingDir ;set moving direction
PlayerSubs: jsr ScrollHandler ;move the screen if necessary
jsr GetPlayerOffscreenBits ;get player's offscreen bits
jsr RelativePlayerPosition ;get coordinates relative to the screen
ldxn ++$00 ;set offset for player object
jsr BoundingBoxCore ;get player's bounding box coordinates
jsr PlayerBGCollision ;do collision detection and process
lda Player_Y_Position
cmpn ++$40 ;check to see if player is higher than 64th pixel
cmpcy
bcc PlayerHole ;if so, branch ahead
lda GameEngineSubroutine
cmpn ++$05 ;if running end-of-level routine, branch ahead
beq PlayerHole
cmpn ++$07 ;if running player entrance routine, branch ahead
beq PlayerHole
cmpn ++$04 ;if running routines $00-$03, branch ahead
cmpcy
bcc PlayerHole
lda Player_SprAttrib
andn ++%11011111 ;otherwise nullify player's
sta Player_SprAttrib ;background priority flag
PlayerHole: lda Player_Y_HighPos ;check player's vertical high byte
cmpn ++$02 ;for below the screen
bmi ExitCtrl ;branch to leave if not that far down
ldxn ++$01
stx ScrollLock ;set scroll lock
ldyn ++$04
sty SCRATCHPAD+$07 ;set value here
ldxn ++$00 ;use X as flag, and clear for cloud level
ldy GameTimerExpiredFlag ;check game timer expiration flag
checky
bne HoleDie ;if set, branch
ldy CloudTypeOverride ;check for cloud type override
checky
bne ChkHoleX ;skip to last part if found
HoleDie: inx ;set flag in X for player death
ldy GameEngineSubroutine
cpyn ++$0b ;check for some other routine running (playerdeath)
beq ChkHoleX ;if so, branch ahead
ldy DeathMusicLoaded ;check value here
checky
bne HoleBottom ;if already set, branch to next part
iny
sty EventMusicQueue ;otherwise play death music
sty DeathMusicLoaded ;and set value here
HoleBottom: ldyn ++$06
sty SCRATCHPAD+$07 ;change value here
ChkHoleX: cmpi SCRATCHPAD+$07 ;compare vertical high byte with value set here
bmi ExitCtrl ;if less, branch to leave
dex ;otherwise decrement flag in X
bmi CloudExit ;if flag was clear, branch to set modes and other values
ldy EventMusicBuffer ;check to see if music is still playing
checky
bne ExitCtrl ;branch to leave if so
ldan ++$06 ;otherwise set to run lose life routine
sta GameEngineSubroutine ;on next frame
ExitCtrl: rts ;leave
CloudExit:
ldan ++$00
sta JoypadOverride ;clear controller override bits if any are set
jsr SetEntr ;do sub to set secondary mode
inci AltEntranceControl ;set mode of entry to 3
rts
;-------------------------------------------------------------------------------------
Vine_AutoClimb:
lda Player_Y_HighPos ;check to see whether player reached position
checka
bne AutoClimb ;above the status bar yet and if so, set modes
lda Player_Y_Position
cmpn ++$e4
cmpcy
bcc SetEntr
AutoClimb: ldan ++%00001000 ;set controller bits override to up
sta JoypadOverride
ldyn ++$03 ;set player state to climbing
sty Player_State
jmp AutoControlPlayer
SetEntr: ldan ++$02 ;set starting position to override
sta AltEntranceControl
jmp ChgAreaMode ;set modes
;-------------------------------------------------------------------------------------
VerticalPipeEntry:
ldan ++$01 ;set 1 as movement amount
jsr MovePlayerYAxis ;do sub to move player downwards
jsr ScrollHandler ;do sub to scroll screen with saved force if necessary
ldyn ++$00 ;load default mode of entry
lda WarpZoneControl ;check warp zone control variable/flag
checka
bne ChgAreaPipe ;if set, branch to use mode 0
iny
lda AreaType ;check for castle level type
cmpn ++$03
bne ChgAreaPipe ;if not castle type level, use mode 1
iny
jmp ChgAreaPipe ;otherwise use mode 2
MovePlayerYAxis:
clc
adci Player_Y_Position ;add contents of A to player position
sta Player_Y_Position
rts
;-------------------------------------------------------------------------------------
SideExitPipeEntry:
jsr EnterSidePipe ;execute sub to move player to the right
ldyn ++$02
ChgAreaPipe: deci ChangeAreaTimer ;decrement timer for change of area
bne ExitCAPipe
sty AltEntranceControl ;when timer expires set mode of alternate entry
ChgAreaMode: inci DisableScreenFlag ;set flag to disable screen output
ldan ++$00
sta OperMode_Task ;set secondary mode of operation
sta Sprite0HitDetectFlag ;disable sprite 0 check
ExitCAPipe: rts ;leave
EnterSidePipe:
ldan ++$08 ;set player's horizontal speed
sta Player_X_Speed
ldyn ++$01 ;set controller right button by default
lda Player_X_Position ;mask out higher nybble of player's
andn ++%00001111 ;horizontal position
bne RightPipe
sta Player_X_Speed ;if lower nybble = 0, set as horizontal speed
tay ;and nullify controller bit override here
RightPipe: tya ;use contents of Y to
jsr AutoControlPlayer ;execute player control routine with ctrl bits nulled
rts
;-------------------------------------------------------------------------------------
PlayerChangeSize:
lda TimerControl ;check master timer control
cmpn ++$f8 ;for specific moment in time
bne EndChgSize ;branch if before or after that point
jmp InitChangeSize ;otherwise run code to get growing/shrinking going
EndChgSize: cmpn ++$c4 ;check again for another specific moment
bne ExitChgSize ;and branch to leave if before or after that point
jsr DonePlayerTask ;otherwise do sub to init timer control and set routine
ExitChgSize: rts ;and then leave
;-------------------------------------------------------------------------------------
PlayerInjuryBlink:
lda TimerControl ;check master timer control
cmpn ++$f0 ;for specific moment in time
cmpcy
bcs ExitBlink ;branch if before that point
cmpn ++$c8 ;check again for another specific point
beq DonePlayerTask ;branch if at that point, and not before or after
jmp PlayerCtrlRoutine ;otherwise run player control routine
ExitBlink: bne ExitBoth ;do unconditional branch to leave
InitChangeSize:
ldy PlayerChangeSizeFlag ;if growing/shrinking flag already set
checky
bne ExitBoth ;then branch to leave
sty PlayerAnimCtrl ;otherwise initialize player's animation frame control
inci PlayerChangeSizeFlag ;set growing/shrinking flag
lda PlayerSize
eorn ++$01 ;invert player's size
sta PlayerSize
ExitBoth: rts ;leave
;-------------------------------------------------------------------------------------
;$00 - used in CyclePlayerPalette to store current palette to cycle
PlayerDeath:
lda TimerControl ;check master timer control
cmpn ++$f0 ;for specific moment in time
cmpcy
bcs ExitDeath ;branch to leave if before that point
jmp PlayerCtrlRoutine ;otherwise run player control routine
DonePlayerTask:
ldan ++$00
sta TimerControl ;initialize master timer control to continue timers
ldan ++$08
sta GameEngineSubroutine ;set player control routine to run next frame
rts ;leave
PlayerFireFlower:
lda TimerControl ;check master timer control
cmpn ++$c0 ;for specific moment in time
beq ResetPalFireFlower ;branch if at moment, not before or after
lda FrameCounter ;get frame counter
lsr
lsr ;divide by four to change every four frames
CyclePlayerPalette:
if Z80MARIOCYCLECOLOR
ld hl,(curpalette)
ld e,12*2
add hl,de
ld a,(hl)
xor 0xf3
ld (hl),a
inc hl
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
xor 0xf3
ld (hl),a
inc hl
ld (hl),a
else
andn ++$03 ;mask out all but d1-d0 (previously d3-d2)
sta SCRATCHPAD+$00 ;store result here to use as palette bits
lda Player_SprAttrib ;get player attributes
andn ++%11111100 ;save any other bits but palette bits
orai SCRATCHPAD+$00 ;add palette bits
sta Player_SprAttrib ;store as new player attributes
endif
rts ;and leave
ResetPalFireFlower:
jsr DonePlayerTask ;do sub to init timer control and run player control routine
ResetPalStar:
if Z80MARIOCYCLECOLOR
if 1
jp GetPlayerColors
else
ld hl,(curpalette)
ld e,12*2
add hl,de
ld a,(hl)
xor 0xf3
ld (hl),a
inc hl
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
xor 0xf3
ld (hl),a
inc hl
ld (hl),a
ret
endif
else
lda Player_SprAttrib ;get player attributes
andn ++%11111100 ;mask out palette bits to force palette 0
sta Player_SprAttrib ;store as new player attributes
rts ;and leave
endif
ExitDeath:
rts ;leave from death routine
;-------------------------------------------------------------------------------------
FlagpoleSlide:
lda Enemy_ID+5 ;check special use enemy slot
cmpn ++FlagpoleFlagObject ;for flagpole flag object
bne NoFPObj ;if not found, branch to something residual
lda FlagpoleSoundQueue ;load flagpole sound
sta Square1SoundQueue ;into square 1's sfx queue
ldan ++$00
sta FlagpoleSoundQueue ;init flagpole sound queue
ldy Player_Y_Position
cpyn ++$9e ;check to see if player has slid down
cmpcy
bcs SlidePlayer ;far enough, and if so, branch with no controller bits set
ldan ++$04 ;otherwise force player to climb down (to slide)
SlidePlayer: jmp AutoControlPlayer ;jump to player control routine
NoFPObj: inci GameEngineSubroutine ;increment to next routine (this may
rts ;be residual code)
;-------------------------------------------------------------------------------------
Hidden1UpCoinAmts:
.db $15, $23, $16, $1b, $17, $18, $23, $63
PlayerEndLevel:
if MUSICONINT ;ъюёЄ√ы№: фхырхь фю т√їюфр шч єЁютэ Ёхцшь MUSICONINT=0
;YIELD
;ld b,0
;ld d,b
halt
;ld hl,0xffff
;ld (EventMusicQueue_noint),hl
ld hl,SoundEngine
ld (soundenginepatch),hl
ld a,0x21
ld (soundenginecall),a
endif
ldan ++$01 ;force player to walk to the right
jsr AutoControlPlayer
lda Player_Y_Position ;check player's vertical position
cmpn ++$ae
cmpcy
bcc ChkStop ;if player is not yet off the flagpole, skip this part
lda ScrollLock ;if scroll lock not set, branch ahead to next part
checka
beq ChkStop ;because we only need to do this part once
ldan ++EndOfLevelMusic
sta EventMusicQueue ;load win level music in event music queue
ldan ++$00
sta ScrollLock ;turn off scroll lock to skip this part later
ChkStop: lda Player_CollisionBits ;get player collision bits
lsr ;check for d0 set
bcs RdyNextA ;if d0 set, skip to next part
lda StarFlagTaskControl ;if star flag task control already set,
checka
bne InCastle ;go ahead with the rest of the code
inci StarFlagTaskControl ;otherwise set task control now (this gets ball rolling!)
InCastle: ldan ++%00100000 ;set player's background priority bit to
sta Player_SprAttrib ;give illusion of being inside the castle
RdyNextA: lda StarFlagTaskControl
cmpn ++$05 ;if star flag task control not yet set
bne ExitNA ;beyond last valid task number, branch to leave
inci LevelNumber ;increment level number used for game logic
lda LevelNumber
cmpn ++$03 ;check to see if we have yet reached level -4
bne NextArea ;and skip this last part here if not
ldy WorldNumber ;get world number as offset
lda CoinTallyFor1Ups ;check third area coin tally for bonus 1-ups
cmpy Hidden1UpCoinAmts,y ;against minimum value, if player has not collected
cmpcy
bcc NextArea ;at least this number of coins, leave flag clear
inci Hidden1UpFlag ;otherwise set hidden 1-up box control flag
NextArea: inci AreaNumber ;increment area number used for address loader
jsr LoadAreaPointer ;get new level pointer
inci FetchNewGameTimerFlag ;set flag to load new game timer
jsr ChgAreaMode ;do sub to set secondary mode, disable screen and sprite 0
sta HalfwayPage ;reset halfway page to 0 (beginning)
ldan ++Silence
sta EventMusicQueue ;silence music and leave
ExitNA: rts
;-------------------------------------------------------------------------------------
PlayerMovementSubs:
ldan ++$00 ;set A to init crouch flag by default
ldy PlayerSize ;is player small?
checky
bne SetCrouch ;if so, branch
lda Player_State ;check state of player
checka
bne ProcMove ;if not on the ground, branch
lda Up_Down_Buttons ;load controller bits for up and down
andn ++%00000100 ;single out bit for down button
SetCrouch: sta CrouchingFlag ;store value in crouch flag
ProcMove: jsr PlayerPhysicsSub ;run sub related to jumping and swimming
lda PlayerChangeSizeFlag ;if growing/shrinking flag set,
checka
bne NoMoveSub ;branch to leave
lda Player_State
cmpn ++$03 ;get player state
beq MoveSubs ;if climbing, branch ahead, leave timer unset
ldyn ++$18
sty ClimbSideTimer ;otherwise reset timer now
MoveSubs: jsr JumpEngine
.dw OnGroundStateSub
.dw JumpSwimSub
.dw FallingSub
.dw ClimbingSub
NoMoveSub: rts
;-------------------------------------------------------------------------------------
;$00 - used by ClimbingSub to store high vertical adder
OnGroundStateSub:
jsr GetPlayerAnimSpeed ;do a sub to set animation frame timing
lda Left_Right_Buttons
checka
beq GndMove ;if left/right controller bits not set, skip instruction
;jr $ ;ё■фр Ёхфъю яюярфрхь яю ъэюяърь left,right
sta PlayerFacingDir ;otherwise set new facing direction
GndMove: jsr ImposeFriction ;do a sub to impose friction on player's walk/run
jsr MovePlayerHorizontally ;do another sub to move player horizontally
sta Player_X_Scroll ;set returned value as player's movement speed for scroll
rts
;--------------------------------
FallingSub:
lda VerticalForceDown
sta VerticalForce ;dump vertical movement force for falling into main one
jmp LRAir ;movement force, then skip ahead to process left/right movement
;--------------------------------
JumpSwimSub:
ldy Player_Y_Speed ;if player's vertical speed zero
checky
bpl DumpFall ;or moving downwards, branch to falling
lda A_B_Buttons
andn ++A_Button ;check to see if A button is being pressed
andi PreviousA_B_Buttons ;and was pressed in previous frame
bne ProcSwim ;if so, branch elsewhere
lda JumpOrigin_Y_Position ;get vertical position player jumped from
secsub
sbci Player_Y_Position ;subtract current from original vertical coordinate
cmpi DiffToHaltJump ;compare to value set here to see if player is in mid-jump
cmpcy
bcc ProcSwim ;or just starting to jump, if just starting, skip ahead
DumpFall: lda VerticalForceDown ;otherwise dump falling into main fractional
sta VerticalForce
ProcSwim: lda SwimmingFlag ;if swimming flag not set, ;є эрё ёэрўрыр 0???
checka
beq LRAir ;branch ahead to last part
jsr GetPlayerAnimSpeed ;do a sub to get animation frame timing
lda Player_Y_Position
cmpn ++$14 ;check vertical position against preset value
cmpcy
bcs LRWater ;if not yet reached a certain position, branch ahead
ldan ++$18
sta VerticalForce ;otherwise set fractional
LRWater: lda Left_Right_Buttons ;check left/right controller bits (check for swimming)
checka
beq LRAir ;if not pressing any, skip
sta PlayerFacingDir ;otherwise set facing direction accordingly
;ё■фр тїюф х∙╕ шч FallingSub
LRAir: lda Left_Right_Buttons ;check left/right controller bits (check for jumping/falling)
checka
beq JSMove ;if not pressing any, skip
jsr ImposeFriction ;otherwise process horizontal movement
JSMove: jsr MovePlayerHorizontally ;do a sub to move player horizontally
sta Player_X_Scroll ;set player's speed here, to be used for scroll later
lda GameEngineSubroutine
cmpn ++$0b ;check for specific routine selected (playerdeath)
bne ExitMov1 ;branch if not set to run
ldan ++$28
sta VerticalForce ;otherwise set fractional
ExitMov1: jmp MovePlayerVertically ;jump to move player vertically, then leave
;--------------------------------
ClimbAdderLow:
.db $0e, $04, $fc, $f2
ClimbAdderHigh:
.db $00, $00, $ff, $ff
ClimbingSub:
lda Player_YMF_Dummy
clc ;add movement force to dummy variable
adci Player_Y_MoveForce ;save with carry
sta Player_YMF_Dummy
ldyn ++$00 ;set default adder here
lda Player_Y_Speed ;get player's vertical speed
checka
bpl MoveOnVine ;if not moving upwards, branch
dey ;otherwise set adder to $ff
MoveOnVine: sty SCRATCHPAD+$00 ;store adder here
adci Player_Y_Position ;add carry to player's vertical position
sta Player_Y_Position ;and store to move player up or down
lda Player_Y_HighPos
adci SCRATCHPAD+$00 ;add carry to player's page location
sta Player_Y_HighPos ;and store
lda Left_Right_Buttons ;compare left/right controller bits
andi Player_CollisionBits ;to collision flag
beq InitCSTimer ;if not set, skip to end
ldy ClimbSideTimer ;otherwise check timer
checky
bne ExitCSub ;if timer not expired, branch to leave
ldyn ++$18
sty ClimbSideTimer ;otherwise set timer now
ldxn ++$00 ;set default offset here
ldy PlayerFacingDir ;get facing direction
lsr ;move right button controller bit to carry
bcs ClimbFD ;if controller right pressed, branch ahead
inx
inx ;otherwise increment offset by 2 bytes
ClimbFD: dey ;check to see if facing right
beq CSetFDir ;if so, branch, do not increment
inx ;otherwise increment by 1 byte
CSetFDir: lda Player_X_Position
clc ;add or subtract from player's horizontal position
adcx ClimbAdderLow,x ;using value here as adder and X as offset
sta Player_X_Position
lda Player_PageLoc ;add or subtract carry or borrow using value here
adcx ClimbAdderHigh,x ;from the player's page location
sta Player_PageLoc
lda Left_Right_Buttons ;get left/right controller bits again
eorn ++%00000011 ;invert them and store them while player
sta PlayerFacingDir ;is on vine to face player in opposite direction
ExitCSub: rts ;then leave
InitCSTimer: sta ClimbSideTimer ;initialize timer here
rts
;-------------------------------------------------------------------------------------
;$00 - used to store offset to friction data
JumpMForceData:
.db $20, $20, $1e, $28, $28, $0d, $04
FallMForceData:
.db $70, $70, $60, $90, $90, $0a, $09
PlayerYSpdData:
.db $fc, $fc, $fc, $fb, $fb, $fe, $ff
InitMForceData:
.db $00, $00, $00, $00, $00, $80, $00
MaxLeftXSpdData:
.db $d8, $e8, $f0
MaxRightXSpdData:
.db $28, $18, $10
.db $0c ;used for pipe intros
FrictionData:
.db $e4, $98, $d0
Climb_Y_SpeedData:
.db $00, $ff, $01
Climb_Y_MForceData:
.db $00, $20, $ff
PlayerPhysicsSub:
lda Player_State ;check player state
cmpn ++$03
bne CheckForJumping ;if not climbing, branch
ldyn ++$00
lda Up_Down_Buttons ;get controller bits for up/down
andi Player_CollisionBits ;check against player's collision detection bits
beq ProcClimb ;if not pressing up or down, branch
iny
andn ++%00001000 ;check for pressing up
bne ProcClimb
iny
ProcClimb: ldxy Climb_Y_MForceData,y ;load value here
stx Player_Y_MoveForce ;store as vertical movement force
ldan ++$08 ;load default animation timing
ldxy Climb_Y_SpeedData,y ;load some other value here
stx Player_Y_Speed ;store as vertical speed
checkx
bmi SetCAnim ;if climbing down, use default animation timing value
lsr ;otherwise divide timer setting by 2
SetCAnim: sta PlayerAnimTimerSet ;store animation timer setting and leave
rts
CheckForJumping:
lda JumpspringAnimCtrl ;if jumpspring animating,
checka
bne NoJump ;skip ahead to something else
lda A_B_Buttons ;check for A button press
andn ++A_Button
beq NoJump ;if not, branch to something else
andi PreviousA_B_Buttons ;if button not pressed in previous frame, branch
beq ProcJumping
NoJump: jmp X_Physics ;otherwise, jump to something else
ProcJumping:
lda Player_State ;check player state
checka
beq InitJS ;if on the ground, branch
lda SwimmingFlag ;if swimming flag not set, jump to do something else
checka
beq NoJump ;to prevent midair jumping, otherwise continue
lda JumpSwimTimer ;if jump/swim timer nonzero, branch
checka
bne InitJS
lda Player_Y_Speed ;check player's vertical speed
checka
bpl InitJS ;if player's vertical speed motionless or down, branch
jmp X_Physics ;if timer at zero and player still rising, do not swim
InitJS: ldan ++$20 ;set jump/swim timer
sta JumpSwimTimer
ldyn ++$00 ;initialize vertical force and dummy variable
sty Player_YMF_Dummy
sty Player_Y_MoveForce
lda Player_Y_HighPos ;get vertical high and low bytes of jump origin
sta JumpOrigin_Y_HighPos ;and store them next to each other here
lda Player_Y_Position
sta JumpOrigin_Y_Position
ldan ++$01 ;set player state to jumping/swimming
sta Player_State
lda Player_XSpeedAbsolute ;check value related to walking/running speed
cmpn ++$09
cmpcy
bcc ChkWtr ;branch if below certain values, increment Y
iny ;for each amount equal or exceeded
cmpn ++$10
cmpcy
bcc ChkWtr
iny
cmpn ++$19
cmpcy
bcc ChkWtr
iny
cmpn ++$1c
cmpcy
bcc ChkWtr ;note that for jumping, range is 0-4 for Y
iny
ChkWtr: ldan ++$01 ;set value here (apparently always set to 1)
sta DiffToHaltJump
lda SwimmingFlag ;if swimming flag disabled, branch
checka
beq GetYPhy
ldyn ++$05 ;otherwise set Y to 5, range is 5-6
lda Whirlpool_Flag ;if whirlpool flag not set, branch
checka
beq GetYPhy
iny ;otherwise increment to 6
GetYPhy: lday JumpMForceData,y ;store appropriate jump/swim
sta VerticalForce ;data here
lday FallMForceData,y
sta VerticalForceDown
lday InitMForceData,y
sta Player_Y_MoveForce
lday PlayerYSpdData,y
sta Player_Y_Speed
lda SwimmingFlag ;if swimming flag disabled, branch
checka
beq PJumpSnd
ldan ++Sfx_EnemyStomp ;load swim/goomba stomp sound into
sta Square1SoundQueue ;square 1's sfx queue
lda Player_Y_Position
cmpn ++$14 ;check vertical low byte of player position
cmpcy
bcs X_Physics ;if below a certain point, branch
ldan ++$00 ;otherwise reset player's vertical speed
sta Player_Y_Speed ;and jump to something else to keep player
jmp X_Physics ;from swimming above water level
PJumpSnd: ldan ++Sfx_BigJump ;load big mario's jump sound by default
ldy PlayerSize ;is mario big?
checky
beq SJumpSnd
ldan ++Sfx_SmallJump ;if not, load small mario's jump sound
SJumpSnd: sta Square1SoundQueue ;store appropriate jump sound in square 1 sfx queue
X_Physics: ldyn ++$00
sty SCRATCHPAD+$00 ;init value here
lda Player_State ;if mario is on the ground, branch
checka
beq ProcPRun
lda Player_XSpeedAbsolute ;check something that seems to be related
cmpn ++$19 ;to mario's speed
cmpcy
bcs GetXPhy ;if => $19 branch here
bcc ChkRFast ;if not branch elsewhere
ProcPRun: iny ;if mario on the ground, increment Y
lda AreaType ;check area type
checka
beq ChkRFast ;if water type, branch
dey ;decrement Y by default for non-water type area
lda Left_Right_Buttons ;get left/right controller bits
cmpi Player_MovingDir ;check against moving direction
bne ChkRFast ;if controller bits <> moving direction, skip this part
lda A_B_Buttons ;check for b button pressed
andn ++B_Button
bne SetRTmr ;if pressed, skip ahead to set timer
lda RunningTimer ;check for running timer set
checka
bne GetXPhy ;if set, branch
ChkRFast: iny ;if running timer not set or level type is water,
inci SCRATCHPAD+$00 ;increment Y again and temp variable in memory
lda RunningSpeed
checka
bne FastXSp ;if running speed set here, branch
lda Player_XSpeedAbsolute
cmpn ++$21 ;otherwise check player's walking/running speed
cmpcy
bcc GetXPhy ;if less than a certain amount, branch ahead
FastXSp: inci SCRATCHPAD+$00 ;if running speed set or speed => $21 increment $00
jmp GetXPhy ;and jump ahead
SetRTmr: ldan ++$0a ;if b button pressed, set running timer
sta RunningTimer
GetXPhy: lday MaxLeftXSpdData,y ;get maximum speed to the left
sta MaximumLeftSpeed
lda GameEngineSubroutine ;check for specific routine running
cmpn ++$07 ;(player entrance)
bne GetXPhy2 ;if not running, skip and use old value of Y
ldyn ++$03 ;otherwise set Y to 3
GetXPhy2: lday MaxRightXSpdData,y ;get maximum speed to the right
sta MaximumRightSpeed
ldy SCRATCHPAD+$00 ;get other value in memory
lday FrictionData,y ;get value using value in memory as offset
sta FrictionAdderLow
ldan ++$00
sta FrictionAdderHigh ;init something here
lda PlayerFacingDir
cmpi Player_MovingDir ;check facing direction against moving direction
beq ExitPhy ;if the same, branch to leave
asli FrictionAdderLow ;otherwise shift d7 of friction adder low into carry
roli FrictionAdderHigh ;then rotate carry onto d0 of friction adder high
ExitPhy: rts ;and then leave
;-------------------------------------------------------------------------------------
PlayerAnimTmrData:
.db $02, $04, $07
GetPlayerAnimSpeed:
ldyn ++$00 ;initialize offset in Y
lda Player_XSpeedAbsolute ;check player's walking/running speed
cmpn ++$1c ;against preset amount
cmpcy
bcs SetRunSpd ;if greater than a certain amount, branch ahead
iny ;otherwise increment Y
cmpn ++$0e ;compare against lower amount
cmpcy
bcs ChkSkid ;if greater than this but not greater than first, skip increment
iny ;otherwise increment Y again
ChkSkid: lda SavedJoypadBits ;get controller bits
andn ++%01111111 ;mask out A button
beq SetAnimSpd ;if no other buttons pressed, branch ahead of all this
andn ++$03 ;mask out all others except left and right
cmpi Player_MovingDir ;check against moving direction
bne ProcSkid ;if left/right controller bits <> moving direction, branch
ldan ++$00 ;otherwise set zero value here
SetRunSpd: sta RunningSpeed ;store zero or running speed here
jmp SetAnimSpd
ProcSkid: lda Player_XSpeedAbsolute ;check player's walking/running speed
cmpn ++$0b ;against one last amount
cmpcy
bcs SetAnimSpd ;if greater than this amount, branch
lda PlayerFacingDir
sta Player_MovingDir ;otherwise use facing direction to set moving direction
ldan ++$00
sta Player_X_Speed ;nullify player's horizontal speed
sta Player_X_MoveForce ;and dummy variable for player (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
SetAnimSpd: lday PlayerAnimTmrData,y ;get animation timer setting using Y as offset
sta PlayerAnimTimerSet
rts
;-------------------------------------------------------------------------------------
ImposeFriction:
andi Player_CollisionBits ;perform AND between left/right controller bits and collision flag
cmpn ++$00 ;then compare to zero (this instruction is redundant)
bne JoypFrict ;if any bits set, branch to next part
lda Player_X_Speed
checka
beq SetAbsSpd ;if player has no horizontal speed, branch ahead to last part
bpl RghtFrict ;if player moving to the right, branch to slow
bmi LeftFrict ;otherwise logic dictates player moving left, branch to slow
JoypFrict: lsr ;put right controller bit into carry
bcc RghtFrict ;if left button pressed, carry = 0, thus branch
LeftFrict: lda Player_X_MoveForce ;load value set here (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
clc
adci FrictionAdderLow ;add to it another value set here (яюўхьє??? ¤Єю цх эх ьырф°р ўрёЄ№ ёъюЁюёЄш!)
sta Player_X_MoveForce ;store here (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
lda Player_X_Speed
adci FrictionAdderHigh ;add value plus carry to horizontal speed
sta Player_X_Speed ;set as new horizontal speed
cmpi MaximumRightSpeed ;compare against maximum value for right movement
bmi XSpdSign ;if horizontal speed greater negatively, branch
lda MaximumRightSpeed ;otherwise set preset value as horizontal speed
sta Player_X_Speed ;thus slowing the player's left movement down
jmp SetAbsSpd ;skip to the end
RghtFrict: lda Player_X_MoveForce ;load value set here (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
secsub
sbci FrictionAdderLow ;subtract from it another value set here (яюўхьє??? ¤Єю цх эх ьырф°р ўрёЄ№ ёъюЁюёЄш!)
sta Player_X_MoveForce ;store here (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
lda Player_X_Speed
sbci FrictionAdderHigh ;subtract value plus borrow from horizontal speed
sta Player_X_Speed ;set as new horizontal speed
cmpi MaximumLeftSpeed ;compare against maximum value for left movement
bpl XSpdSign ;if horizontal speed greater positively, branch
lda MaximumLeftSpeed ;otherwise set preset value as horizontal speed
sta Player_X_Speed ;thus slowing the player's right movement down
XSpdSign: cmpn ++$00 ;if player not moving or moving to the right,
bpl SetAbsSpd ;branch and leave horizontal speed value unmodified
eorn ++$ff
clc ;otherwise get two's compliment to get absolute
adcn ++$01 ;unsigned walking/running speed
SetAbsSpd: sta Player_XSpeedAbsolute ;store walking/running speed here and leave
rts
;-------------------------------------------------------------------------------------
;$00 - used to store downward movement force in FireballObjCore
;$02 - used to store maximum vertical speed in FireballObjCore
;$07 - used to store pseudorandom bit in BubbleCheck
ProcFireball_Bubble:
lda PlayerStatus ;check player's status
cmpn ++$02
cmpcy
bcc ProcAirBubbles ;if not fiery, branch
lda A_B_Buttons
andn ++B_Button ;check for b button pressed
beq ProcFireballs ;branch if not pressed
andi PreviousA_B_Buttons
bne ProcFireballs ;if button pressed in previous frame, branch
lda FireballCounter ;load fireball counter
andn ++%00000001 ;get LSB and use as offset for buffer
tax
ldax Fireball_State,x ;load fireball state
checka
bne ProcFireballs ;if not inactive, branch
ldy Player_Y_HighPos ;if player too high or too low, branch
dey
bne ProcFireballs
lda CrouchingFlag ;if player crouching, branch
checka
bne ProcFireballs
lda Player_State ;if player's state = climbing, branch
cmpn ++$03
beq ProcFireballs
ldan ++Sfx_Fireball ;play fireball sound effect
sta Square1SoundQueue
ldan ++$02 ;load state
stax Fireball_State,x
ldy PlayerAnimTimerSet ;copy animation frame timer setting
sty FireballThrowingTimer ;into fireball throwing timer
dey
sty PlayerAnimTimer ;decrement and store in player's animation timer
inci FireballCounter ;increment fireball counter
ProcFireballs:
ldxn ++$00
jsr FireballObjCore ;process first fireball object
ldxn ++$01
jsr FireballObjCore ;process second fireball object, then do air bubbles
ProcAirBubbles:
lda AreaType ;if not water type level, skip the rest of this
checka
bne BublExit
ldxn ++$02 ;otherwise load counter and use as offset
BublLoop: stx ObjectOffset ;store offset
jsr BubbleCheck ;check timers and coordinates, create air bubble
jsr RelativeBubblePosition ;get relative coordinates
jsr GetBubbleOffscreenBits ;get offscreen information
jsr DrawBubble ;draw the air bubble
dex
bpl BublLoop ;do this until all three are handled
BublExit: rts ;then leave
FireballXSpdData:
.db $40, $c0
FireballObjCore:
stx ObjectOffset ;store offset as current object
ldax Fireball_State,x ;check for d7 = 1
asl
bcs FireballExplosion ;if so, branch to get relative coordinates and draw explosion
ldyx Fireball_State,x ;if fireball inactive, branch to leave
checky
beq NoFBall
dey ;if fireball state set to 1, skip this part and just run it
beq RunFB
lda Player_X_Position ;get player's horizontal position
or a
adcn ++$04 ;add four pixels and store as fireball's horizontal position
push af
stax Fireball_X_Position,x
pop af
lda Player_PageLoc ;get player's page location
adcn ++$00 ;add carry and store as fireball's page location
stax Fireball_PageLoc,x
lda Player_Y_Position ;get player's vertical position and store
stax Fireball_Y_Position,x
ldan ++$01 ;set high byte of vertical position
stax Fireball_Y_HighPos,x
ldy PlayerFacingDir ;get player's facing direction
dey ;decrement to use as offset here
lday FireballXSpdData,y ;set horizontal speed of fireball accordingly
stax Fireball_X_Speed,x
ldan ++$04 ;set vertical speed of fireball
stax Fireball_Y_Speed,x
ldan ++$07
stax Fireball_BoundBoxCtrl,x ;set bounding box size control for fireball
decx Fireball_State,x ;decrement state to 1 to skip this part from now on
RunFB: txa ;add 7 to offset to use
clc ;as fireball offset for next routines
adcn ++$07
tax
ldan ++$50 ;set downward movement force here
sta SCRATCHPAD+$00
ldan ++$03 ;set maximum speed here
sta SCRATCHPAD+$02
ldan ++$00
jsr ImposeGravity ;do sub here to impose gravity on fireball and move vertically
jsr MoveObjectHorizontally ;do another sub to move it horizontally
ldx ObjectOffset ;return fireball offset to X
jsr RelativeFireballPosition ;get relative coordinates
jsr GetFireballOffscreenBits ;get offscreen information
jsr GetFireballBoundBox ;get bounding box coordinates
jsr FireballBGCollision ;do fireball to background collision detection
lda FBall_OffscreenBits ;get fireball offscreen bits
andn ++%11001100 ;mask out certain bits
bne EraseFB ;if any bits still set, branch to kill fireball
jsr FireballEnemyCollision ;do fireball to enemy collision detection and deal with collisions
jmp DrawFireball ;draw fireball appropriately and leave
EraseFB: ldan ++$00 ;erase fireball state
stax Fireball_State,x
NoFBall: rts ;leave
FireballExplosion:
jsr RelativeFireballPosition
jmp DrawExplosion_Fireball
BubbleCheck:
ldax PseudoRandomBitReg+1,x ;get part of LSFR
andn ++$01
sta SCRATCHPAD+$07 ;store pseudorandom bit here
ldax Bubble_Y_Position,x ;get vertical coordinate for air bubble
cmpn ++$f8 ;if offscreen coordinate not set,
bne MoveBubl ;branch to move air bubble
lda AirBubbleTimer ;if air bubble timer not expired,
checka
bne ExitBubl ;branch to leave, otherwise create new air bubble
SetupBubble:
ldyn ++$00 ;load default value here
lda PlayerFacingDir ;get player's facing direction
lsr ;move d0 to carry
bcc PosBubl ;branch to use default value if facing left
ldyn ++$08 ;otherwise load alternate value here
PosBubl: tya ;use value loaded as adder
adci Player_X_Position ;add to player's horizontal position
push af
stax Bubble_X_Position,x ;save as horizontal position for airbubble
pop af
lda Player_PageLoc
adcn ++$00 ;add carry to player's page location
stax Bubble_PageLoc,x ;save as page location for airbubble
lda Player_Y_Position
clc ;add eight pixels to player's vertical position
adcn ++$08
stax Bubble_Y_Position,x ;save as vertical position for air bubble
ldan ++$01
stax Bubble_Y_HighPos,x ;set vertical high byte for air bubble
ldy SCRATCHPAD+$07 ;get pseudorandom bit, use as offset
lday BubbleTimerData,y ;get data for air bubble timer
sta AirBubbleTimer ;set air bubble timer
MoveBubl: ldy SCRATCHPAD+$07 ;get pseudorandom bit again, use as offset
ldax Bubble_YMF_Dummy,x
secsub ;subtract pseudorandom amount from dummy variable
sbcy Bubble_MForceData,y
push af
stax Bubble_YMF_Dummy,x ;save dummy variable
ldax Bubble_Y_Position,x
ld h,a
pop af
ld a,h
sbcn ++$00 ;subtract borrow from airbubble's vertical coordinate
cmpn ++$20 ;if below the status bar,
cmpcy
bcs Y_Bubl ;branch to go ahead and use to move air bubble upwards
ldan ++$f8 ;otherwise set offscreen coordinate
Y_Bubl: stax Bubble_Y_Position,x ;store as new vertical coordinate for air bubble
ExitBubl: rts ;leave
Bubble_MForceData:
.db $ff, $50
BubbleTimerData:
.db $40, $20
;-------------------------------------------------------------------------------------
RunGameTimer:
lda OperMode ;get primary mode of operation
checka
beq ExGTimer ;branch to leave if in title screen mode
lda GameEngineSubroutine
cmpn ++$08 ;if routine number less than eight running,
cmpcy
bcc ExGTimer ;branch to leave
cmpn ++$0b ;if running death routine,
beq ExGTimer ;branch to leave
lda Player_Y_HighPos
cmpn ++$02 ;if player below the screen,
cmpcy
bcs ExGTimer ;branch to leave regardless of level type
lda GameTimerCtrlTimer ;if game timer control not yet expired,
checka
bne ExGTimer ;branch to leave
lda GameTimerDisplay
orai GameTimerDisplay+1 ;otherwise check game timer digits
orai GameTimerDisplay+2
beq TimeUpOn ;if game timer digits at 000, branch to time-up code
ldy GameTimerDisplay ;otherwise check first digit
dey ;if first digit not on 1,
bne ResGTCtrl ;branch to reset game timer control
lda GameTimerDisplay+1 ;otherwise check second and third digits
orai GameTimerDisplay+2
bne ResGTCtrl ;if timer not at 100, branch to reset game timer control
ldan ++TimeRunningOutMusic
sta EventMusicQueue ;otherwise load time running out music
ResGTCtrl: ldan ++$18 ;reset game timer control
sta GameTimerCtrlTimer
ldyn ++$23 ;set offset for last digit
ldan ++$ff ;set value to decrement game timer digit
sta DigitModifier+5
jsr DigitsMathRoutine ;do sub to decrement game timer slowly
ldan ++$a4 ;set status nybbles to update game timer display
jmp PrintStatusBarNumbers ;do sub to update the display
TimeUpOn: sta PlayerStatus ;init player status (note A will always be zero here)
jsr ForceInjury ;do sub to kill the player (note player is small here)
inci GameTimerExpiredFlag ;set game timer expiration flag
ExGTimer: rts ;leave
;-------------------------------------------------------------------------------------
WarpZoneObject:
lda ScrollLock ;check for scroll lock flag
checka
beq ExGTimer ;branch if not set to leave
lda Player_Y_Position ;check to see if player's vertical coordinate has
andi Player_Y_HighPos ;same bits set as in vertical high byte (why?)
bne ExGTimer ;if so, branch to leave
sta ScrollLock ;otherwise nullify scroll lock flag
inci WarpZoneControl ;increment warp zone flag to make warp pipes for warp zone
jmp EraseEnemyObject ;kill this object
;-------------------------------------------------------------------------------------
;$00 - used in WhirlpoolActivate to store whirlpool length / 2, page location of center of whirlpool
;and also to store movement force exerted on player
;$01 - used in ProcessWhirlpools to store page location of right extent of whirlpool
;and in WhirlpoolActivate to store center of whirlpool
;$02 - used in ProcessWhirlpools to store right extent of whirlpool and in
;WhirlpoolActivate to store maximum vertical speed
ProcessWhirlpools:
lda AreaType ;check for water type level
checka
bne ExitWh ;branch to leave if not found
sta Whirlpool_Flag ;otherwise initialize whirlpool flag
lda TimerControl ;if master timer control set,
checka
bne ExitWh ;branch to leave
ldyn ++$04 ;otherwise start with last whirlpool data
WhLoop: lday Whirlpool_LeftExtent,y ;get left extent of whirlpool
clc
adcy Whirlpool_Length,y ;add length of whirlpool
sta SCRATCHPAD+$02 ;store result as right extent here
ldaykeepcy Whirlpool_PageLoc,y ;get page location
checka
beq NextWh ;if none or page 0, branch to get next data
adcn ++$00 ;add carry
sta SCRATCHPAD+$01 ;store result as page location of right extent here
lda Player_X_Position ;get player's horizontal position
secsub
sbcy Whirlpool_LeftExtent,y ;subtract left extent
lda Player_PageLoc ;get player's page location
sbcy Whirlpool_PageLoc,y ;subtract borrow
bmi NextWh ;if player too far left, branch to get next data
lda SCRATCHPAD+$02 ;otherwise get right extent
secsub
sbci Player_X_Position ;subtract player's horizontal coordinate
lda SCRATCHPAD+$01 ;get right extent's page location
sbci Player_PageLoc ;subtract borrow
bpl WhirlpoolActivate ;if player within right extent, branch to whirlpool code
NextWh: dey ;move onto next whirlpool data
bpl WhLoop ;do this until all whirlpools are checked
ExitWh: rts ;leave
WhirlpoolActivate:
lday Whirlpool_Length,y ;get length of whirlpool
lsr ;divide by 2
sta SCRATCHPAD+$00 ;save here
lday Whirlpool_LeftExtent,y ;get left extent of whirlpool
clc
adci SCRATCHPAD+$00 ;add length divided by 2
sta SCRATCHPAD+$01 ;save as center of whirlpool
ldaykeepcy Whirlpool_PageLoc,y ;get page location
adcn ++$00 ;add carry
sta SCRATCHPAD+$00 ;save as page location of whirlpool center
lda FrameCounter ;get frame counter
lsr ;shift d0 into carry (to run on every other frame)
bcc WhPull ;if d0 not set, branch to last part of code
lda SCRATCHPAD+$01 ;get center
secsub
sbci Player_X_Position ;subtract player's horizontal coordinate
lda SCRATCHPAD+$00 ;get page location of center
sbci Player_PageLoc ;subtract borrow
bpl LeftWh ;if player to the left of center, branch
lda Player_X_Position ;otherwise slowly pull player left, towards the center
secsub
sbcn ++$01 ;subtract one pixel
sta Player_X_Position ;set player's new horizontal coordinate
lda Player_PageLoc
sbcn ++$00 ;subtract borrow
jmp SetPWh ;jump to set player's new page location
LeftWh: lda Player_CollisionBits ;get player's collision bits
lsr ;shift d0 into carry
bcc WhPull ;if d0 not set, branch
lda Player_X_Position ;otherwise slowly pull player right, towards the center
clc
adcn ++$01 ;add one pixel
sta Player_X_Position ;set player's new horizontal coordinate
lda Player_PageLoc
adcn ++$00 ;add carry
SetPWh: sta Player_PageLoc ;set player's new page location
WhPull: ldan ++$10
sta SCRATCHPAD+$00 ;set vertical movement force
ldan ++$01
sta Whirlpool_Flag ;set whirlpool flag to be used later
sta SCRATCHPAD+$02 ;also set maximum vertical speed
lsr
tax ;set X for player offset
jmp ImposeGravity ;jump to put whirlpool effect on player vertically, do not return
;-------------------------------------------------------------------------------------
FlagpoleScoreMods:
.db $05, $02, $08, $04, $01
FlagpoleScoreDigits:
.db $03, $03, $04, $04, $04
FlagpoleRoutine:
ldxn ++$05 ;set enemy object offset
stx ObjectOffset ;to special use slot
ldax Enemy_ID,x
cmpn ++FlagpoleFlagObject ;if flagpole flag not found,
bne ExitFlagP ;branch to leave
lda GameEngineSubroutine
cmpn ++$04 ;if flagpole slide routine not running,
bne SkipScore ;branch to near the end of code
lda Player_State
cmpn ++$03 ;if player state not climbing,
bne SkipScore ;branch to near the end of code
ldax Enemy_Y_Position,x ;check flagpole flag's vertical coordinate
cmpn ++$aa ;if flagpole flag down to a certain point,
cmpcy
bcs GiveFPScr ;branch to end the level
lda Player_Y_Position ;check player's vertical coordinate
cmpn ++$a2 ;if player down to a certain point,
cmpcy
bcs GiveFPScr ;branch to end the level
ldax Enemy_YMF_Dummy,x
or a
adcn ++$ff ;add movement amount to dummy variable
push af
stax Enemy_YMF_Dummy,x ;save dummy variable
ldax Enemy_Y_Position,x ;get flag's vertical coordinate
ld h,a
pop af
ld a,h
adcn ++$01 ;add 1 plus carry to move flag, and
stax Enemy_Y_Position,x ;store vertical coordinate
lda FlagpoleFNum_YMFDummy
secsub ;subtract movement amount from dummy variable
sbcn ++$ff
sta FlagpoleFNum_YMFDummy ;save dummy variable
lda FlagpoleFNum_Y_Pos
sbcn ++$01 ;subtract one plus borrow to move floatey number,
sta FlagpoleFNum_Y_Pos ;and store vertical coordinate here
SkipScore: jmp FPGfx ;jump to skip ahead and draw flag and floatey number
GiveFPScr: ldy FlagpoleScore ;get score offset from earlier (when player touched flagpole)
lday FlagpoleScoreMods,y ;get amount to award player points
ldxy FlagpoleScoreDigits,y ;get digit with which to award points
stax DigitModifier,x ;store in digit modifier
jsr AddToScore ;do sub to award player points depending on height of collision
ldan ++$05
sta GameEngineSubroutine ;set to run end-of-level subroutine on next frame
FPGfx: jsr GetEnemyOffscreenBits ;get offscreen information
jsr RelativeEnemyPosition ;get relative coordinates
jsr FlagpoleGfxHandler ;draw flagpole flag and floatey number
ExitFlagP: rts
;-------------------------------------------------------------------------------------
Jumpspring_Y_PosData:
.db $08, $10, $08, $00
JumpspringHandler:
jsr GetEnemyOffscreenBits ;get offscreen information
lda TimerControl ;check master timer control
checka
bne DrawJSpr ;branch to last section if set
lda JumpspringAnimCtrl ;check jumpspring frame control
checka
beq DrawJSpr ;branch to last section if not set
tay
dey ;subtract one from frame control,
tya ;the only way a poor nmos 6502 can
andn ++%00000010 ;mask out all but d1, original value still in Y
bne DownJSpr ;if set, branch to move player up
inci Player_Y_Position
inci Player_Y_Position ;move player's vertical position down two pixels
jmp PosJSpr ;skip to next part
DownJSpr: deci Player_Y_Position ;move player's vertical position up two pixels
deci Player_Y_Position
PosJSpr: ldax Jumpspring_FixedYPos,x ;get permanent vertical position
clc
adcy Jumpspring_Y_PosData,y ;add value using frame control as offset
stax Enemy_Y_Position,x ;store as new vertical position
cpyn ++$01 ;check frame control offset (second frame is $00)
cmpcy
bcc BounceJS ;if offset not yet at third frame ($01), skip to next part
lda A_B_Buttons
andn ++A_Button ;check saved controller bits for A button press
beq BounceJS ;skip to next part if A not pressed
andi PreviousA_B_Buttons ;check for A button pressed in previous frame
bne BounceJS ;skip to next part if so
ldan ++$f4
sta JumpspringForce ;otherwise write new jumpspring force here
BounceJS: cpyn ++$03 ;check frame control offset again
bne DrawJSpr ;skip to last part if not yet at fifth frame ($03)
lda JumpspringForce
sta Player_Y_Speed ;store jumpspring force as player's new vertical speed
ldan ++$00
sta JumpspringAnimCtrl ;initialize jumpspring frame control
DrawJSpr: jsr RelativeEnemyPosition ;get jumpspring's relative coordinates
jsr EnemyGfxHandler ;draw jumpspring
jsr OffscreenBoundsCheck ;check to see if we need to kill it
lda JumpspringAnimCtrl ;if frame control at zero, don't bother
checka
beq ExJSpring ;trying to animate it, just leave
lda JumpspringTimer
checka
bne ExJSpring ;if jumpspring timer not expired yet, leave
ldan ++$04
sta JumpspringTimer ;otherwise initialize jumpspring timer
inci JumpspringAnimCtrl ;increment frame control to animate jumpspring
ExJSpring: rts ;leave
;-------------------------------------------------------------------------------------
Setup_Vine:
ldan ++VineObject ;load identifier for vine object
stax Enemy_ID,x ;store in buffer
ldan ++$01
stax Enemy_Flag,x ;set flag for enemy object buffer
lday Block_PageLoc,y
stax Enemy_PageLoc,x ;copy page location from previous object
lday Block_X_Position,y
stax Enemy_X_Position,x ;copy horizontal coordinate from previous object
lday Block_Y_Position,y
stax Enemy_Y_Position,x ;copy vertical coordinate from previous object
ldy VineFlagOffset ;load vine flag/offset to next available vine slot
checky
bne NextVO ;if set at all, don't bother to store vertical
sta VineStart_Y_Position ;otherwise store vertical coordinate here
NextVO: txa ;store object offset to next available vine slot
stay VineObjOffset,y ;using vine flag as offset
inci VineFlagOffset ;increment vine flag offset
ldan ++Sfx_GrowVine
sta Square2SoundQueue ;load vine grow sound
rts
;-------------------------------------------------------------------------------------
;$06-$07 - used as address to block buffer data
;$02 - used as vertical high nybble of block buffer offset
VineHeightData:
.db $30, $60
VineObjectHandler:
cpxn ++$05 ;check enemy offset for special use slot
bne ExitVH ;if not in last slot, branch to leave
ldy VineFlagOffset
dey ;decrement vine flag in Y, use as offset
lda VineHeight
cmpy VineHeightData,y ;if vine has reached certain height,
beq RunVSubs ;branch ahead to skip this part
lda FrameCounter ;get frame counter
lsr ;shift d1 into carry
lsr
bcc RunVSubs ;if d1 not set (2 frames every 4) skip this part
lda Enemy_Y_Position+5
or a
sbcn ++$01 ;subtract vertical position of vine
sta Enemy_Y_Position+5 ;one pixel every frame it's time
inci VineHeight ;increment vine height
RunVSubs: lda VineHeight ;if vine still very small,
cmpn ++$08 ;branch to leave
cmpcy
bcc ExitVH
jsr RelativeEnemyPosition ;get relative coordinates of vine,
jsr GetEnemyOffscreenBits ;and any offscreen bits
ldyn ++$00 ;initialize offset used in draw vine sub
VDrawLoop: jsr DrawVine ;draw vine
iny ;increment offset
cpyi VineFlagOffset ;if offset in Y and offset here
bne VDrawLoop ;do not yet match, loop back to draw more vine
lda Enemy_OffscreenBits
andn ++%00001100 ;mask offscreen bits
beq WrCMTile ;if none of the saved offscreen bits set, skip ahead
dey ;otherwise decrement Y to get proper offset again
KillVine: ldxy VineObjOffset,y ;get enemy object offset for this vine object
jsr EraseEnemyObject ;kill this vine object
dey ;decrement Y
bpl KillVine ;if any vine objects left, loop back to kill it
sta VineFlagOffset ;initialize vine flag/offset
sta VineHeight ;initialize vine height
WrCMTile: lda VineHeight ;check vine height
cmpn ++$20 ;if vine small (less than 32 pixels tall)
cmpcy
bcc ExitVH ;then branch ahead to leave
ldxn ++$06 ;set offset in X to last enemy slot
ldan ++$01 ;set A to obtain horizontal in $04, but we don't care
ldyn ++$1b ;set Y to offset to get block at ($04, $10) of coordinates
jsr BlockBufferCollision ;do a sub to get block buffer address set, return contents
ldy SCRATCHPAD+$02
cpyn ++$d0 ;if vertical high nybble offset beyond extent of
cmpcy
bcs ExitVH ;current block buffer, branch to leave, do not write
ldayindirect (SCRATCHPAD+$06),y ;otherwise check contents of block buffer at
checka
bne ExitVH ;current offset, if not empty, branch to leave
ldan ++$26
stayindirect (SCRATCHPAD+$06),y ;otherwise, write climbing metatile to block buffer
ExitVH: ldx ObjectOffset ;get enemy object offset and leave
rts
;-------------------------------------------------------------------------------------
CannonBitmasks:
.db %00001111, %00000111
ProcessCannons:
lda AreaType ;get area type
checka
beq ExCannon ;if water type area, branch to leave
ldxn ++$02
ThreeSChk: stx ObjectOffset ;start at third enemy slot
ldax Enemy_Flag,x ;check enemy buffer flag
checka
bne Chk_BB ;if set, branch to check enemy
ldax PseudoRandomBitReg+1,x ;otherwise get part of LSFR
ldy SecondaryHardMode ;get secondary hard mode flag, use as offset
andy CannonBitmasks,y ;mask out bits of LSFR as decided by flag
cmpn ++$06 ;check to see if lower nybble is above certain value
cmpcy
bcs Chk_BB ;if so, branch to check enemy
tay ;transfer masked contents of LSFR to Y as pseudorandom offset
lday Cannon_PageLoc,y ;get page location
checka
beq Chk_BB ;if not set or on page 0, branch to check enemy
lday Cannon_Timer,y ;get cannon timer
checka
beq FireCannon ;if expired, branch to fire cannon
scf
sbcn ++$00 ;otherwise subtract borrow (note carry will always be clear here)
stay Cannon_Timer,y ;to count timer down
jmp Chk_BB ;then jump ahead to check enemy
FireCannon:
lda TimerControl ;if master timer control set,
checka
bne Chk_BB ;branch to check enemy
ldan ++$0e ;otherwise we start creating one
stay Cannon_Timer,y ;first, reset cannon timer
lday Cannon_PageLoc,y ;get page location of cannon
stax Enemy_PageLoc,x ;save as page location of bullet bill
lday Cannon_X_Position,y ;get horizontal coordinate of cannon
stax Enemy_X_Position,x ;save as horizontal coordinate of bullet bill
lday Cannon_Y_Position,y ;get vertical coordinate of cannon
secsub
sbcn ++$08 ;subtract eight pixels (because enemies are 24 pixels tall)
stax Enemy_Y_Position,x ;save as vertical coordinate of bullet bill
ldan ++$01
stax Enemy_Y_HighPos,x ;set vertical high byte of bullet bill
stax Enemy_Flag,x ;set buffer flag
lsr ;shift right once to init A
stax Enemy_State,x ;then initialize enemy's state
ldan ++$09
stax Enemy_BoundBoxCtrl,x ;set bounding box size control for bullet bill
ldan ++BulletBill_CannonVar
stax Enemy_ID,x ;load identifier for bullet bill (cannon variant)
jmp Next3Slt ;move onto next slot
Chk_BB: ldax Enemy_ID,x ;check enemy identifier for bullet bill (cannon variant)
cmpn ++BulletBill_CannonVar
bne Next3Slt ;if not found, branch to get next slot
jsr OffscreenBoundsCheck ;otherwise, check to see if it went offscreen
ldax Enemy_Flag,x ;check enemy buffer flag
checka
beq Next3Slt ;if not set, branch to get next slot
jsr GetEnemyOffscreenBits ;otherwise, get offscreen information
jsr BulletBillHandler ;then do sub to handle bullet bill
Next3Slt: dex ;move onto next slot
bpl ThreeSChk ;do this until first three slots are checked
ExCannon: rts ;then leave
;--------------------------------
BulletBillXSpdData:
.db $18, $e8
BulletBillHandler:
lda TimerControl ;if master timer control set,
checka
bne RunBBSubs ;branch to run subroutines except movement sub
ldax Enemy_State,x
checka
bne ChkDSte ;if bullet bill's state set, branch to check defeated state
lda Enemy_OffscreenBits ;otherwise load offscreen bits
andn ++%00001100 ;mask out bits
cmpn ++%00001100 ;check to see if all bits are set
beq KillBB ;if so, branch to kill this object
ldyn ++$01 ;set to move right by default
jsr PlayerEnemyDiff ;get horizontal difference between player and bullet bill
bmi SetupBB ;if enemy to the left of player, branch
iny ;otherwise increment to move left
SetupBB:
push af
styx Enemy_MovingDir,x ;set bullet bill's moving direction
dey ;decrement to use as offset
lday BulletBillXSpdData,y ;get horizontal speed based on moving direction
stax Enemy_X_Speed,x ;and store it
lda SCRATCHPAD+$00 ;get horizontal difference
ld h,a
pop af
ld a,h
adcn ++$28 ;add 40 pixels
cmpn ++$50 ;if less than a certain amount, player is too close
cmpcy
bcc KillBB ;to cannon either on left or right side, thus branch
ldan ++$01
stax Enemy_State,x ;otherwise set bullet bill's state
ldan ++$0a
stax EnemyFrameTimer,x ;set enemy frame timer
ldan ++Sfx_Blast
sta Square2SoundQueue ;play fireworks/gunfire sound
ChkDSte: ldax Enemy_State,x ;check enemy state for d5 set
andn ++%00100000
beq BBFly ;if not set, skip to move horizontally
jsr MoveD_EnemyVertically ;otherwise do sub to move bullet bill vertically
BBFly: jsr MoveEnemyHorizontally ;do sub to move bullet bill horizontally
RunBBSubs: jsr GetEnemyOffscreenBits ;get offscreen information
jsr RelativeEnemyPosition ;get relative coordinates
jsr GetEnemyBoundBox ;get bounding box coordinates
jsr PlayerEnemyCollision ;handle player to enemy collisions
jmp EnemyGfxHandler ;draw the bullet bill and leave
KillBB: jsr EraseEnemyObject ;kill bullet bill and leave
rts
;-------------------------------------------------------------------------------------
HammerEnemyOfsData:
.db $04, $04, $04, $05, $05, $05
.db $06, $06, $06
HammerXSpdData:
.db $10, $f0
SpawnHammerObj:
lda PseudoRandomBitReg+1 ;get pseudorandom bits from
andn ++%00000111 ;second part of LSFR
bne SetMOfs ;if any bits are set, branch and use as offset
lda PseudoRandomBitReg+1
andn ++%00001000 ;get d3 from same part of LSFR
SetMOfs: tay ;use either d3 or d2-d0 for offset here
lday Misc_State,y ;if any values loaded in
checka
bne NoHammer ;$2a-$32 where offset is then leave with carry clear
ldxy HammerEnemyOfsData,y ;get offset of enemy slot to check using Y as offset
ldax Enemy_Flag,x ;check enemy buffer flag at offset
checka
bne NoHammer ;if buffer flag set, branch to leave with carry clear
ldx ObjectOffset ;get original enemy object offset
txa
stay HammerEnemyOffset,y ;save here
ldan ++$90
stay Misc_State,y ;save hammer's state here
ldan ++$07
stay Misc_BoundBoxCtrl,y ;set something else entirely, here
sec ;return with carry set
rts
NoHammer: ldx ObjectOffset ;get original enemy object offset
clc ;return with carry clear
rts
;--------------------------------
;$00 - used to set downward force
;$01 - used to set upward force (residual)
;$02 - used to set maximum speed
ProcHammerObj:
lda TimerControl ;if master timer control set
checka
bne RunHSubs ;skip all of this code and go to last subs at the end
ldax Misc_State,x ;otherwise get hammer's state
andn ++%01111111 ;mask out d7
ldyx HammerEnemyOffset,x ;get enemy object offset that spawned this hammer
cmpn ++$02 ;check hammer's state
cmpcy
beq SetHSpd ;if currently at 2, branch
bcs SetHPos ;if greater than 2, branch elsewhere
txa
clc ;add 13 bytes to use
adcn ++$0d ;proper misc object
tax ;return offset to X
ldan ++$10
sta SCRATCHPAD+$00 ;set downward movement force
ldan ++$0f
sta SCRATCHPAD+$01 ;set upward movement force (not used)
ldan ++$04
sta SCRATCHPAD+$02 ;set maximum vertical speed
ldan ++$00 ;set A to impose gravity on hammer
jsr ImposeGravity ;do sub to impose gravity on hammer and move vertically
jsr MoveObjectHorizontally ;do sub to move it horizontally
ldx ObjectOffset ;get original misc object offset
jmp RunAllH ;branch to essential subroutines
SetHSpd: ldan ++$fe
stax Misc_Y_Speed,x ;set hammer's vertical speed
lday Enemy_State,y ;get enemy object state
andn ++%11110111 ;mask out d3
stay Enemy_State,y ;store new state
ldxy Enemy_MovingDir,y ;get enemy's moving direction
dex ;decrement to use as offset
ldax HammerXSpdData,x ;get proper speed to use based on moving direction
ldx ObjectOffset ;reobtain hammer's buffer offset
stax Misc_X_Speed,x ;set hammer's horizontal speed
SetHPos: decx Misc_State,x ;decrement hammer's state
lday Enemy_X_Position,y ;get enemy's horizontal position
clc
adcn ++$02 ;set position 2 pixels to the right
push af
stax Misc_X_Position,x ;store as hammer's horizontal position
lday Enemy_PageLoc,y ;get enemy's page location
ld h,a
pop af
ld a,h
adcn ++$00 ;add carry
stax Misc_PageLoc,x ;store as hammer's page location
lday Enemy_Y_Position,y ;get enemy's vertical position
secsub
sbcn ++$0a ;move position 10 pixels upward
stax Misc_Y_Position,x ;store as hammer's vertical position
ldan ++$01
stax Misc_Y_HighPos,x ;set hammer's vertical high byte
checka
bne RunHSubs ;unconditional branch to skip first routine
RunAllH: jsr PlayerHammerCollision ;handle collisions
RunHSubs: jsr GetMiscOffscreenBits ;get offscreen information
jsr RelativeMiscPosition ;get relative coordinates
jsr GetMiscBoundBox ;get bounding box coordinates
jsr DrawHammer ;draw the hammer
rts ;and we are done here
;-------------------------------------------------------------------------------------
;$02 - used to store vertical high nybble offset from block buffer routine
;$06 - used to store low byte of block buffer address
CoinBlock:
jsr FindEmptyMiscSlot ;set offset for empty or last misc object buffer slot
;CY = 1(no subtract borrow)???
ldax Block_PageLoc,x ;get page location of block object
stay Misc_PageLoc,y ;store as page location of misc object
ldax Block_X_Position,x ;get horizontal coordinate of block object
oran ++$05 ;add 5 pixels
stay Misc_X_Position,y ;store as horizontal coordinate of misc object
ldax Block_Y_Position,x ;get vertical coordinate of block object
or a ;???
sbcn ++$10 ;subtract 16 pixels
stay Misc_Y_Position,y ;store as vertical coordinate of misc object
jmp JCoinC ;jump to rest of code as applies to this misc object
SetupJumpCoin:
jsr FindEmptyMiscSlot ;set offset for empty or last misc object buffer slot
ldax Block_PageLoc2,x ;get page location saved earlier
stay Misc_PageLoc,y ;and save as page location for misc object
lda SCRATCHPAD+$06 ;get low byte of block buffer offset
asl
asl ;multiply by 16 to use lower nybble
asl
asl
push af
oran ++$05 ;add five pixels
stay Misc_X_Position,y ;save as horizontal coordinate for misc object
lda SCRATCHPAD+$02 ;get vertical high nybble offset from earlier
ld h,a
pop af
ld a,h
adcn ++$20 ;add 32 pixels for the status bar
stay Misc_Y_Position,y ;store as vertical coordinate
JCoinC: ldan ++$fb
stay Misc_Y_Speed,y ;set vertical speed
ldan ++$01
stay Misc_Y_HighPos,y ;set vertical high byte
stay Misc_State,y ;set state for misc object
sta Square2SoundQueue ;load coin grab sound
stx ObjectOffset ;store current control bit as misc object offset
jsr GiveOneCoin ;update coin tally on the screen and coin amount variable
inci CoinTallyFor1Ups ;increment coin tally used to activate 1-up block flag
rts
FindEmptyMiscSlot:
ldyn ++$08 ;start at end of misc objects buffer
FMiscLoop: lday Misc_State,y ;get misc object state
checka
beq UseMiscS ;branch if none found to use current offset
dey ;decrement offset
cpyn ++$05 ;do this for three slots
bne FMiscLoop ;do this until all slots are checked
ldyn ++$08 ;if no empty slots found, use last slot
UseMiscS: sty JumpCoinMiscOffset ;store offset of misc object buffer here (residual)
rts
;-------------------------------------------------------------------------------------
MiscObjectsCore:
ldxn ++$08 ;set at end of misc object buffer
MiscLoop: stx ObjectOffset ;store misc object offset here
ldax Misc_State,x ;check misc object state
checka
beq MiscLoopBack ;branch to check next slot
asl ;otherwise shift d7 into carry
bcc ProcJumpCoin ;if d7 not set, jumping coin, thus skip to rest of code here
jsr ProcHammerObj ;otherwise go to process hammer,
jmp MiscLoopBack ;then check next slot
;--------------------------------
;$00 - used to set downward force
;$01 - used to set upward force (residual)
;$02 - used to set maximum speed
ProcJumpCoin:
ldyx Misc_State,x ;check misc object state
dey ;decrement to see if it's set to 1
beq JCoinRun ;if so, branch to handle jumping coin
incx Misc_State,x ;otherwise increment state to either start off or as timer
ldax Misc_X_Position,x ;get horizontal coordinate for misc object
clc ;whether its jumping coin (state 0 only) or floatey number
adci ScrollAmount ;add current scroll speed
push af
stax Misc_X_Position,x ;store as new horizontal coordinate
ldax Misc_PageLoc,x ;get page location
ld h,a
pop af
ld a,h
adcn ++$00 ;add carry
stax Misc_PageLoc,x ;store as new page location
ldax Misc_State,x
cmpn ++$30 ;check state of object for preset value
bne RunJCSubs ;if not yet reached, branch to subroutines
ldan ++$00
stax Misc_State,x ;otherwise nullify object state
jmp MiscLoopBack ;and move onto next slot
JCoinRun: txa
clc ;add 13 bytes to offset for next subroutine
adcn ++$0d
tax
ldan ++$50 ;set downward movement amount
sta SCRATCHPAD+$00
ldan ++$06 ;set maximum vertical speed
sta SCRATCHPAD+$02
lsr ;divide by 2 and set
sta SCRATCHPAD+$01 ;as upward movement amount (apparently residual)
ldan ++$00 ;set A to impose gravity on jumping coin
jsr ImposeGravity ;do sub to move coin vertically and impose gravity on it
ldx ObjectOffset ;get original misc object offset
ldax Misc_Y_Speed,x ;check vertical speed
cmpn ++$05
bne RunJCSubs ;if not moving downward fast enough, keep state as-is
incx Misc_State,x ;otherwise increment state to change to floatey number
RunJCSubs: jsr RelativeMiscPosition ;get relative coordinates
jsr GetMiscOffscreenBits ;get offscreen information
jsr GetMiscBoundBox ;get bounding box coordinates (why?)
jsr JCoinGfxHandler ;draw the coin or floatey number
MiscLoopBack:
dex ;decrement misc object offset
bpl MiscLoop ;loop back until all misc objects handled
rts ;then leave
;-------------------------------------------------------------------------------------
CoinTallyOffsets:
.db $17, $1d
ScoreOffsets:
.db $0b, $11
StatusBarNybbles:
.db $02, $13
GiveOneCoin:
ldan ++$01 ;set digit modifier to add 1 coin
sta DigitModifier+5 ;to the current player's coin tally
ldx CurrentPlayer ;get current player on the screen
ldyx CoinTallyOffsets,x ;get offset for player's coin tally
jsr DigitsMathRoutine ;update the coin tally
inci CoinTally ;increment onscreen player's coin amount
lda CoinTally
cmpn ++100 ;does player have 100 coins yet?
bne CoinPoints ;if not, skip all of this
ldan ++$00
sta CoinTally ;otherwise, reinitialize coin amount
inci NumberofLives ;give the player an extra life
ldan ++Sfx_ExtraLife
sta Square2SoundQueue ;play 1-up sound
CoinPoints:
ldan ++$02 ;set digit modifier to award
sta DigitModifier+4 ;200 points to the player
AddToScore:
ldx CurrentPlayer ;get current player
ldyx ScoreOffsets,x ;get offset for player's score
jsr DigitsMathRoutine ;update the score internally with value in digit modifier
GetSBNybbles:
ldy CurrentPlayer ;get current player
lday StatusBarNybbles,y ;get nybbles based on player, use to update score and coins
UpdateNumber:
jsr PrintStatusBarNumbers ;print status bar numbers based on nybbles, whatever they be
ldy VRAM_Buffer1_Offset
lday VRAM_Buffer1-6,y ;check highest digit of score
checka
bne NoZSup ;if zero, overwrite with space tile for zero suppression
ldan ++$24
stay VRAM_Buffer1-6,y
NoZSup: ldx ObjectOffset ;get enemy object buffer offset
rts
;-------------------------------------------------------------------------------------
SetupPowerUp:
;jr $
ldan ++PowerUpObject ;load power-up identifier into
sta Enemy_ID+5 ;special use slot of enemy object buffer
ldax Block_PageLoc,x ;store page location of block object
sta Enemy_PageLoc+5 ;as page location of power-up object
ldax Block_X_Position,x ;store horizontal coordinate of block object
sta Enemy_X_Position+5 ;as horizontal coordinate of power-up object
ldan ++$01
sta Enemy_Y_HighPos+5 ;set vertical high byte of power-up object
ldax Block_Y_Position,x ;get vertical coordinate of block object
secsub
sbcn ++$08 ;subtract 8 pixels
sta Enemy_Y_Position+5 ;and use as vertical coordinate of power-up object
PwrUpJmp: ldan ++$01 ;this is a residual jump point in enemy object jump table
sta Enemy_State+5 ;set power-up object's state
sta Enemy_Flag+5 ;set buffer flag
ldan ++$03
sta Enemy_BoundBoxCtrl+5 ;set bounding box size control for power-up object
lda PowerUpType
cmpn ++$02 ;check currently loaded power-up type
cmpcy
bcs PutBehind ;if star or 1-up, branch ahead
lda PlayerStatus ;otherwise check player's current status
cmpn ++$02
cmpcy
bcc StrType ;if player not fiery (<2), use status as power-up type
;jr $
lsr ;otherwise shift right to force fire flower type
StrType: sta PowerUpType ;store type here
PutBehind: ldan ++%00100000
sta Enemy_SprAttrib+5 ;set background priority bit
ldan ++Sfx_GrowPowerUp
sta Square2SoundQueue ;load power-up reveal sound and leave
rts
;-------------------------------------------------------------------------------------
PowerUpObjHandler:
;jr $
ldxn ++$05 ;set object offset for last slot in enemy object buffer
stx ObjectOffset
lda Enemy_State+5 ;check power-up object's state
checka
beq ExitPUp ;if not set, branch to leave
asl ;shift to check if d7 was set in object state
bcc GrowThePowerUp ;if not set, branch ahead to skip this part
lda TimerControl ;if master timer control set,
checka
bne RunPUSubs ;branch ahead to enemy object routines
lda PowerUpType ;check power-up type
checka
beq ShroomM ;if normal mushroom, branch ahead to move it
cmpn ++$03
beq ShroomM ;if 1-up mushroom, branch ahead to move it
cmpn ++$02
bne RunPUSubs ;if not star, branch elsewhere to skip movement
jsr MoveJumpingEnemy ;otherwise impose gravity on star power-up and make it jump
jsr EnemyJump ;note that green paratroopa shares the same code here
jmp RunPUSubs ;then jump to other power-up subroutines
ShroomM: jsr MoveNormalEnemy ;do sub to make mushrooms move
jsr EnemyToBGCollisionDet ;deal with collisions
jmp RunPUSubs ;run the other subroutines
GrowThePowerUp:
lda FrameCounter ;get frame counter
andn ++$03 ;mask out all but 2 LSB
bne ChkPUSte ;if any bits set here, branch
deci Enemy_Y_Position+5 ;otherwise decrement vertical coordinate slowly
lda Enemy_State+5 ;load power-up object state
inci Enemy_State+5 ;increment state for next frame (to make power-up rise)
cmpn ++$11 ;if power-up object state not yet past 16th pixel,
cmpcy
bcc ChkPUSte ;branch ahead to last part here
ldan ++$10
stax Enemy_X_Speed,x ;otherwise set horizontal speed
ldan ++%10000000
sta Enemy_State+5 ;and then set d7 in power-up object's state
asl ;shift once to init A
sta Enemy_SprAttrib+5 ;initialize background priority bit set here
rol ;rotate A to set right moving direction
stax Enemy_MovingDir,x ;set moving direction
ChkPUSte: lda Enemy_State+5 ;check power-up object's state
cmpn ++$06 ;for if power-up has risen enough
cmpcy
bcc ExitPUp ;if not, don't even bother running these routines
RunPUSubs: jsr RelativeEnemyPosition ;get coordinates relative to screen
jsr GetEnemyOffscreenBits ;get offscreen bits
jsr GetEnemyBoundBox ;get bounding box coordinates
jsr DrawPowerUp ;draw the power-up object
jsr PlayerEnemyCollision ;check for collision with player
jsr OffscreenBoundsCheck ;check to see if it went offscreen
ExitPUp: rts ;and we're done
;-------------------------------------------------------------------------------------
;These apply to all routines in this section unless otherwise noted:
;$00 - used to store metatile from block buffer routine
;$02 - used to store vertical high nybble offset from block buffer routine
;$05 - used to store metatile stored in A at beginning of PlayerHeadCollision
;$06-$07 - used as block buffer address indirect
BlockYPosAdderData:
.db $04, $12
PlayerHeadCollision:
pha ;store metatile number to stack
ldan ++$11 ;load unbreakable block object state by default
ldx SprDataOffset_Ctrl ;load offset control bit here
ldy PlayerSize ;check player's size
checky
bne DBlockSte ;if small, branch
ldan ++$12 ;otherwise load breakable block object state
DBlockSte: stax Block_State,x ;store into block object buffer
jsr DestroyBlockMetatile ;store blank metatile in vram buffer to write to name table
ldx SprDataOffset_Ctrl ;load offset control bit
lda SCRATCHPAD+$02 ;get vertical high nybble offset used in block buffer routine
stax Block_Orig_YPos,x ;set as vertical coordinate for block object
tay
lda SCRATCHPAD+$06 ;get low byte of block buffer address used in same routine
stax Block_BBuf_Low,x ;save as offset here to be used later
ldayindirect (SCRATCHPAD+$06),y ;get contents of block buffer at old address at $06, $07
jsr BlockBumpedChk ;do a sub to check which block player bumped head on
sta SCRATCHPAD+$00 ;store metatile here
ldy PlayerSize ;check player's size
checky
bne ChkBrick ;if small, use metatile itself as contents of A
tya ;otherwise init A (note: big = 0)
ChkBrick: bcc PutMTileB ;if no match was found in previous sub, skip ahead
;jr $ ;ё■фр яюярфрхь, хёыш Єюыъэєыш уюыютющ ърьхэ№
ldyn ++$11 ;otherwise load unbreakable state into block object buffer
styx Block_State,x ;note this applies to both player sizes
ldan ++$c4 ;load empty block metatile into A for now
ldy SCRATCHPAD+$00 ;get metatile from before
cpyn ++$58 ;is it brick with coins (with line)?
beq StartBTmr ;if so, branch
cpyn ++$5d ;is it brick with coins (without line)?
bne PutMTileB ;if not, branch ahead to store empty block metatile
StartBTmr: lda BrickCoinTimerFlag ;check brick coin timer flag
checka
bne ContBTmr ;if set, timer expired or counting down, thus branch
ldan ++$0b
sta BrickCoinTimer ;if not set, set brick coin timer
inci BrickCoinTimerFlag ;and set flag linked to it
ContBTmr: lda BrickCoinTimer ;check brick coin timer
checka
bne PutOldMT ;if not yet expired, branch to use current metatile
ldyn ++$c4 ;otherwise use empty block metatile
PutOldMT: tya ;put metatile into A
PutMTileB: stax Block_Metatile,x ;store whatever metatile be appropriate here
jsr InitBlock_XY_Pos ;get block object horizontal coordinates saved
ldy SCRATCHPAD+$02 ;get vertical high nybble offset
ldan ++$23
stayindirect (SCRATCHPAD+$06),y ;write blank metatile $23 to block buffer
ldan ++$10
sta BlockBounceTimer ;set block bounce timer
pla ;pull original metatile from stack
sta SCRATCHPAD+$05 ;and save here
ldyn ++$00 ;set default offset
lda CrouchingFlag ;is player crouching?
checka
bne SmallBP ;if so, branch to increment offset
lda PlayerSize ;is player big?
checka
beq BigBP ;if so, branch to use default offset
SmallBP: iny ;increment for small or big and crouching
BigBP: lda Player_Y_Position ;get player's vertical coordinate
clc
adcy BlockYPosAdderData,y ;add value determined by size
andn ++$f0 ;mask out low nybble to get 16-pixel correspondence
stax Block_Y_Position,x ;save as vertical coordinate for block object
ldyx Block_State,x ;get block object state
cpyn ++$11
beq Unbreak ;if set to value loaded for unbreakable, branch
jsr BrickShatter ;execute code for breakable brick
jmp InvOBit ;skip subroutine to do last part of code here
Unbreak: jsr BumpBlock ;execute code for unbreakable brick or question block
InvOBit: lda SprDataOffset_Ctrl ;invert control bit used by block objects
eorn ++$01 ;and floatey numbers
sta SprDataOffset_Ctrl
rts ;leave!
;--------------------------------
InitBlock_XY_Pos:
lda Player_X_Position ;get player's horizontal coordinate
clc
adcn ++$08 ;add eight pixels
push af
andn ++$f0 ;mask out low nybble to give 16-pixel correspondence
stax Block_X_Position,x ;save as horizontal coordinate for block object
pop af
lda Player_PageLoc
adcn ++$00 ;add carry to page location of player
stax Block_PageLoc,x ;save as page location of block object
stax Block_PageLoc2,x ;save elsewhere to be used later
lda Player_Y_HighPos
stax Block_Y_HighPos,x ;save vertical high byte of player into
rts ;vertical high byte of block object and leave
;--------------------------------
BumpBlock:
jsr CheckTopOfBlock ;check to see if there's a coin directly above this block
ldan ++Sfx_Bump
sta Square1SoundQueue ;play bump sound
ldan ++$00
stax Block_X_Speed,x ;initialize horizontal speed for block object
stax Block_Y_MoveForce,x ;init fractional movement force
sta Player_Y_Speed ;init player's vertical speed
ldan ++$fe
stax Block_Y_Speed,x ;set vertical speed for block object
lda SCRATCHPAD+$05 ;get original metatile from stack
jsr BlockBumpedChk ;do a sub to check which block player bumped head on
bcc ExitBlockChk ;if no match was found, branch to leave
tya ;move block number to A
cmpn ++$09 ;if block number was within 0-8 range,
cmpcy
bcc BlockCode ;branch to use current number
cmpcy
sbcn ++$05 ;otherwise subtract 5 for second set to get proper number
BlockCode: jsr JumpEngine ;run appropriate subroutine depending on block number
.dw MushFlowerBlock
.dw CoinBlock
.dw CoinBlock
.dw ExtraLifeMushBlock
.dw MushFlowerBlock
.dw VineBlock
.dw StarBlock
.dw CoinBlock
.dw ExtraLifeMushBlock
;--------------------------------
MushFlowerBlock:
ldan ++$00 ;load mushroom/fire flower into power-up type
jr bonusblock_go;.db $2c ;BIT instruction opcode
StarBlock:
ldan ++$02 ;load star into power-up type
jr bonusblock_go;.db $2c ;BIT instruction opcode
ExtraLifeMushBlock:
ldan ++$03 ;load 1-up mushroom into power-up type
bonusblock_go
sta SCRATCHPAD+$39 ;store correct power-up type
jmp SetupPowerUp
VineBlock:
ldxn ++$05 ;load last slot for enemy object buffer
ldy SprDataOffset_Ctrl ;get control bit
jsr Setup_Vine ;set up vine object
ExitBlockChk:
rts ;leave
;--------------------------------
BrickQBlockMetatiles:
.db $c1, $c0, $5f, $60 ;used by question blocks
;these two sets are functionally identical, but look different
.db $55, $56, $57, $58, $59 ;used by ground level types
.db $5a, $5b, $5c, $5d, $5e ;used by other level types
BlockBumpedChk:
ldyn ++$0d ;start at end of metatile data
BumpChkLoop: cmpy BrickQBlockMetatiles,y ;check to see if current metatile matches
beq MatchBump ;metatile found in block buffer, branch if so
dey ;otherwise move onto next metatile
bpl BumpChkLoop ;do this until all metatiles are checked
clc ;if none match, return with carry clear
if Z80
rts
MatchBump:
scf
ret
else
MatchBump: rts ;note carry is set if found match
endif
;--------------------------------
BrickShatter:
jsr CheckTopOfBlock ;check to see if there's a coin directly above this block
ldan ++Sfx_BrickShatter
stax Block_RepFlag,x ;set flag for block object to immediately replace metatile
sta NoiseSoundQueue ;load brick shatter sound
jsr SpawnBrickChunks ;create brick chunk objects
ldan ++$fe
sta Player_Y_Speed ;set vertical speed for player
ldan ++$05
sta DigitModifier+5 ;set digit modifier to give player 50 points
jsr AddToScore ;do sub to update the score
ldx SprDataOffset_Ctrl ;load control bit and leave
rts
;--------------------------------
CheckTopOfBlock:
ldx SprDataOffset_Ctrl ;load control bit
ldy SCRATCHPAD+$02 ;get vertical high nybble offset used in block buffer
checky
beq TopEx ;branch to leave if set to zero, because we're at the top
tya ;otherwise set to A
secsub
sbcn ++$10 ;subtract $10 to move up one row in the block buffer
sta SCRATCHPAD+$02 ;store as new vertical high nybble offset
tay
ldayindirect (SCRATCHPAD+$06),y ;get contents of block buffer in same column, one row up
cmpn ++$c2 ;is it a coin? (not underwater)
bne TopEx ;if not, branch to leave
ldan ++$00
stayindirect (SCRATCHPAD+$06),y ;otherwise put blank metatile where coin was
jsr RemoveCoin_Axe ;write blank metatile to vram buffer
ldx SprDataOffset_Ctrl ;get control bit
jsr SetupJumpCoin ;create jumping coin object and update coin variables
TopEx: rts ;leave!
;--------------------------------
SpawnBrickChunks:
ldax Block_X_Position,x ;set horizontal coordinate of block object
stax Block_Orig_XPos,x ;as original horizontal coordinate here
ldan ++$f0
stax Block_X_Speed,x ;set horizontal speed for brick chunk objects
stax Block_X_Speed+2,x
ldan ++$fa
stax Block_Y_Speed,x ;set vertical speed for one
ldan ++$fc
stax Block_Y_Speed+2,x ;set lower vertical speed for the other
ldan ++$00
stax Block_Y_MoveForce,x ;init fractional movement force for both
stax Block_Y_MoveForce+2,x
ldax Block_PageLoc,x
stax Block_PageLoc+2,x ;copy page location
ldax Block_X_Position,x
stax Block_X_Position+2,x ;copy horizontal coordinate
ldax Block_Y_Position,x
clc ;add 8 pixels to vertical coordinate
adcn ++$08 ;and save as vertical coordinate for one of them
stax Block_Y_Position+2,x
ldan ++$fa
stax Block_Y_Speed,x ;set vertical speed...again??? (redundant)
rts
;-------------------------------------------------------------------------------------
BlockObjectsCore:
ldax Block_State,x ;get state of block object
checka
beq UpdSte ;if not set, branch to leave
andn ++$0f ;mask out high nybble
pha ;push to stack
tay ;put in Y for now
txa
clc
adcn ++$09 ;add 9 bytes to offset (note two block objects are created
tax ;when using brick chunks, but only one offset for both) ;эрўшэр ё юс·хъЄр #9 шфєЄ misc objects (ўЄю ¤Єю???)
dey ;decrement Y to check for solid block state
beq BouncingBlockHandler ;branch if found, otherwise continue for brick chunks
jsr ImposeGravityBlock ;do sub to impose gravity on one block object object
jsr MoveObjectHorizontally ;do another sub to move horizontally
txa
clc ;move onto next block object
adcn ++$02
tax
jsr ImposeGravityBlock ;do sub to impose gravity on other block object
jsr MoveObjectHorizontally ;do another sub to move horizontally
ldx ObjectOffset ;get block object offset used for both
jsr RelativeBlockPosition ;get relative coordinates
jsr GetBlockOffscreenBits ;get offscreen information
jsr DrawBrickChunks ;draw the brick chunks
pla ;get lower nybble of saved state
ldyx Block_Y_HighPos,x ;check vertical high byte of block object
checky
beq UpdSte ;if above the screen, branch to kill it
pha ;otherwise save state back into stack
ldan ++$f0
cmpx Block_Y_Position+2,x ;check to see if bottom block object went
cmpcy
bcs ChkTop ;to the bottom of the screen, and branch if not
stax Block_Y_Position+2,x ;otherwise set offscreen coordinate
ChkTop: ldax Block_Y_Position,x ;get top block object's vertical coordinate
cmpn ++$f0 ;see if it went to the bottom of the screen
plakeepcy ;pull block object state from stack
cmpcy
bcc UpdSte ;if not, branch to save state
bcs KillBlock ;otherwise do unconditional branch to kill it
BouncingBlockHandler:
jsr ImposeGravityBlock ;do sub to impose gravity on block object
ldx ObjectOffset ;get block object offset
jsr RelativeBlockPosition ;get relative coordinates
jsr GetBlockOffscreenBits ;get offscreen information
jsr DrawBlock ;draw the block
ldax Block_Y_Position,x ;get vertical coordinate
andn ++$0f ;mask out high nybble
cmpn ++$05 ;check to see if low nybble wrapped around
cmpcy
plakeepcy ;pull state from stack
bcs UpdSte ;if still above amount, not time to kill block yet, thus branch
ldan ++$01
stax Block_RepFlag,x ;otherwise set flag to replace metatile
KillBlock: ldan ++$00 ;if branched here, nullify object state
UpdSte: stax Block_State,x ;store contents of A in block object state
rts
;-------------------------------------------------------------------------------------
;$02 - used to store offset to block buffer
;$06-$07 - used to store block buffer address
BlockObjMT_Updater:
ldxn ++$01 ;set offset to start with second block object
UpdateLoop: stx ObjectOffset ;set offset here
lda VRAM_Buffer1 ;if vram buffer already being used here,
checka
bne NextBUpd ;branch to move onto next block object
ldax Block_RepFlag,x ;if flag for block object already clear,
checka
beq NextBUpd ;branch to move onto next block object
ldax Block_BBuf_Low,x ;get low byte of block buffer
sta SCRATCHPAD+$06 ;store into block buffer address
ldan ++$05
sta SCRATCHPAD+$07 ;set high byte of block buffer address
ldax Block_Orig_YPos,x ;get original vertical coordinate of block object
sta SCRATCHPAD+$02 ;store here and use as offset to block buffer
tay
ldax Block_Metatile,x ;get metatile to be written
stayindirect (SCRATCHPAD+$06),y ;write it to the block buffer
jsr ReplaceBlockMetatile ;do sub to replace metatile where block object is
ldan ++$00
stax Block_RepFlag,x ;clear block object flag
NextBUpd: dex ;decrement block object offset
bpl UpdateLoop ;do this until both block objects are dealt with
rts ;then leave
;-------------------------------------------------------------------------------------
;$00 - used to store high nybble of horizontal speed as adder
;$01 - used to store low nybble of horizontal speed
;$02 - used to store adder to page location
MoveEnemyHorizontally:
inx ;increment offset for enemy offset
jsr MoveObjectHorizontally ;position object horizontally according to
ldx ObjectOffset ;counters, return with saved value in A,
rts ;put enemy offset back in X and leave
MovePlayerHorizontally:
lda JumpspringAnimCtrl ;if jumpspring currently animating,
if Z80OPT
or a
ret nz ;branch to leave
else
checka
bne ExXMove ;branch to leave
endif
tax ;otherwise set zero for offset to use player's stuff
MoveObjectHorizontally:
;PageLoc_XPosition_XMoveforce += 16*Xspeed (signed)
;out: a=ёьх∙хэшх X-ъююЁфшэрЄ√ юЄэюёшЄхы№эю яЁю°ыющ яючшЎшш (шёяюы№чєхЄё Єюы№ъю фы яырЄЇюЁь? эхяюэ Єэю - чрўхь?)
;CY эх трцхэ
;ьюцэю яюЁЄшЄ№ y
;ьюцэю эх яшёрЄ№ т SCRATCHPAD+0,1,2
if Z80OPT
ld ix,SprObject_X_Speed
add ix,bc
ld a,(ix)
add a,a
ld l,a
sbc a,a
ld h,a ;Ёрё°шЁхэшх чэрър
ld e,a ;ёюїЁрэ хь чэръ
add hl,hl
add hl,hl
add hl,hl
ld a,l
ld d,h
ld hl,SprObject_X_MoveForce
add hl,bc
add a,(hl) ;get whatever number's here (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√) ;add low nybble moved to high
ld (hl),a ;store result here (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
ld a,(ix+SprObject_X_Position-SprObject_X_Speed)
ld l,a
adc a,d ;add carry plus saved value (high nybble moved to low
ld d,b;0
ld h,a ;plus $f0 if necessary) to object's horizontal position
ld a,(ix+SprObject_PageLoc-SprObject_X_Speed)
adc a,e ;add carry plus other saved value to the
ld (ix+SprObject_PageLoc-SprObject_X_Speed),a ;object's page location and save
ld a,h
sub l;(ix+SprObject_X_Position-SprObject_X_Speed) ;(яюыєўрхЄё ёьх∙хэшх X-ъююЁфшэрЄ√ юЄэюёшЄхы№эю яЁю°ыющ яючшЎшш)
ld (ix+SprObject_X_Position-SprObject_X_Speed),h
ret
else ;~Z80
ldax SprObject_X_Speed,x ;get currently saved value (horizontal
asl ;speed, secondary counter, whatever)
asl ;and move low nybble to high
asl
asl
sta SCRATCHPAD+$01 ;store result here
ldax SprObject_X_Speed,x ;get saved value again
lsr ;move high nybble to low
lsr
lsr
lsr
cmpn ++$08 ;if < 8, branch, do not change
cmpcy
bcc SaveXSpd
oran ++%11110000 ;otherwise alter high nybble (Ёрё°шЁхэшх чэрър)
SaveXSpd: sta SCRATCHPAD+$00 ;save result here
ldyn ++$00 ;load default Y value here
cmpn ++$00 ;if result positive, leave Y alone
bpl UseAdder
dey ;otherwise decrement Y
UseAdder: sty SCRATCHPAD+$02 ;save Y here (яюыєўшыё чэръ ёъюЁюёЄш 0/-1)
ldax SprObject_X_MoveForce,x ;get whatever number's here (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
clc
adci SCRATCHPAD+$01 ;add low nybble moved to high
push af
stax SprObject_X_MoveForce,x ;store result here (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
pop af
ldan ++$00 ;init A
rol ;rotate carry into d0
pha ;push onto stack (ёюїЁрэ хь Їыру яхЁхэюёр яюёых ёыюцхэш ьырф°хщ ўрёЄш X-ъююЁфшэрЄ√)
ror ;rotate d0 back onto carry
ldaxkeepcy SprObject_X_Position,x
adci SCRATCHPAD+$00 ;add carry plus saved value (high nybble moved to low
push af
stax SprObject_X_Position,x ;plus $f0 if necessary) to object's horizontal position
ldax SprObject_PageLoc,x
ld h,a
pop af
ld a,h
adci SCRATCHPAD+$02 ;add carry plus other saved value to the
stax SprObject_PageLoc,x ;object's page location and save
pla
clc ;pull old carry from stack (Їыру яхЁхэюёр яюёых ёыюцхэш ьырф°хщ ўрёЄш X-ъююЁфшэрЄ√) and add
adci SCRATCHPAD+$00 ;to high nybble moved to low (яюыєўрхЄё ёьх∙хэшх X-ъююЁфшэрЄ√ юЄэюёшЄхы№эю яЁю°ыющ яючшЎшш)
ExXMove: rts ;and leave
endif
;-------------------------------------------------------------------------------------
;$00 - used for downward force
;$01 - used for upward force
;$02 - used for maximum vertical speed
MovePlayerVertically:
ldxn ++$00 ;set X for player offset
lda TimerControl
checka
bne NoJSChk ;if master timer control set, branch ahead
lda JumpspringAnimCtrl ;otherwise check to see if jumpspring is animating
if Z80OPT
or a
ret nz ;branch to leave if so
else
checka
bne ExXMove ;branch to leave if so
endif
NoJSChk: lda VerticalForce ;dump vertical force
sta SCRATCHPAD+$00
ldan ++$04 ;set maximum vertical speed here
jmp ImposeGravitySprObj ;then jump to move player vertically
;--------------------------------
MoveD_EnemyVertically:
ldyn ++$3d ;set quick movement amount downwards
ldax Enemy_State,x ;then check enemy state
cmpn ++$05 ;if not set to unique state for spiny's egg, go ahead
bne ContVMove ;and use, otherwise set different movement amount, continue on
MoveFallingPlatform:
ldyn ++$20 ;set movement amount
ContVMove: jmp SetHiMax ;jump to skip the rest of this
;--------------------------------
MoveRedPTroopaDown:
ldyn ++$00 ;set Y to move downwards
jmp MoveRedPTroopa ;skip to movement routine
MoveRedPTroopaUp:
ldyn ++$01 ;set Y to move upwards
MoveRedPTroopa:
inx ;increment X for enemy offset
ldan ++$03
sta SCRATCHPAD+$00 ;set downward movement amount here
ldan ++$06
sta SCRATCHPAD+$01 ;set upward movement amount here
ldan ++$02
sta SCRATCHPAD+$02 ;set maximum speed here
tya ;set movement direction in A, and
jmp RedPTroopaGrav ;jump to move this thing
;--------------------------------
MoveDropPlatform:
ldyn ++$7f ;set movement amount for drop platform
checky
bne SetMdMax ;skip ahead of other value set here
MoveEnemySlowVert:
ldyn ++$0f ;set movement amount for bowser/other objects
SetMdMax: ldan ++$02 ;set maximum speed in A
checka
bne SetXMoveAmt ;unconditional branch
;--------------------------------
MoveJ_EnemyVertically:
ldyn ++$1c ;set movement amount for podoboo/other objects
SetHiMax: ldan ++$03 ;set maximum speed in A
SetXMoveAmt: sty SCRATCHPAD+$00 ;set movement amount here
inx ;increment X for enemy offset
jsr ImposeGravitySprObj ;do a sub to move enemy object downwards
ldx ObjectOffset ;get enemy object buffer offset and leave
rts
;--------------------------------
MaxSpdBlockData:
.db $06, $08
ResidualGravityCode:
ldyn ++$00 ;this part appears to be residual,
jr ImposeGravityBlock_go;.db $2c ;no code branches or jumps to it...
ImposeGravityBlock:
ldyn ++$01 ;set offset for maximum speed
ImposeGravityBlock_go
ldan ++$50 ;set movement amount here
sta SCRATCHPAD+$00
lday MaxSpdBlockData,y ;get maximum speed
ImposeGravitySprObj:
sta SCRATCHPAD+$02 ;set maximum speed here
ldan ++$00 ;set value to move downwards
jmp ImposeGravity ;jump to the code that actually moves it
;--------------------------------
MovePlatformDown:
ldan ++$00 ;save value to stack (if branching here, execute next
jr MovePlatform_go;.db $2c ;part as BIT instruction)
MovePlatformUp:
ldan ++$01 ;save value to stack
MovePlatform_go
pha
ldyx Enemy_ID,x ;get enemy object identifier
inx ;increment offset for enemy object
ldan ++$05 ;load default value here
cpyn ++$29 ;residual comparison, object ++29 never executes
bne SetDplSpd ;this code, thus unconditional branch here
ldan ++$09 ;residual code
SetDplSpd: sta SCRATCHPAD+$00 ;save downward movement amount here
ldan ++$0a ;save upward movement amount here
sta SCRATCHPAD+$01
ldan ++$03 ;save maximum vertical speed here
sta SCRATCHPAD+$02
pla ;get value from stack
tay ;use as Y, then move onto code shared by red koopa
RedPTroopaGrav:
jsr ImposeGravity ;do a sub to move object gradually
ldx ObjectOffset ;get enemy object offset and leave
rts
;-------------------------------------------------------------------------------------
;$00 - used for downward force
;$01 - used for upward force
;$07 - used as adder for vertical position
ImposeGravity:
pha ;push value to stack
ldax SprObject_YMF_Dummy,x
clc ;add value in movement force to contents of dummy variable
adcx SprObject_Y_MoveForce,x
push af
stax SprObject_YMF_Dummy,x
ldyn ++$00 ;set Y to zero by default
ldax SprObject_Y_Speed,x ;get current vertical speed
ld h,a
pop af
ld a,h
checka
bpl AlterYP ;if currently moving downwards, do not decrement Y
dey ;otherwise decrement Y
AlterYP: sty SCRATCHPAD+$07 ;store Y here
adcx SprObject_Y_Position,x ;add vertical position to vertical speed plus carry
push af
stax SprObject_Y_Position,x ;store as new vertical position
ldax SprObject_Y_HighPos,x
ld h,a
pop af
ld a,h
adci SCRATCHPAD+$07 ;add carry plus contents of $07 to vertical high byte
stax SprObject_Y_HighPos,x ;store as new vertical high byte
ldax SprObject_Y_MoveForce,x
clc
adci SCRATCHPAD+$00 ;add downward movement amount to contents of $0433
push af
stax SprObject_Y_MoveForce,x
ldax SprObject_Y_Speed,x ;add carry to vertical speed and store
ld h,a
pop af
ld a,h
adcn ++$00
stax SprObject_Y_Speed,x
cmpi SCRATCHPAD+$02 ;compare to maximum speed
bmi ChkUpM ;if less than preset value, skip this part
ldax SprObject_Y_MoveForce,x
cmpn ++$80 ;if less positively than preset maximum, skip this part
cmpcy
bcc ChkUpM
lda SCRATCHPAD+$02
stax SprObject_Y_Speed,x ;keep vertical speed within maximum value
ldan ++$00
stax SprObject_Y_MoveForce,x ;clear fractional
ChkUpM: pla ;get value from stack
checka
beq ExVMove ;if set to zero, branch to leave
lda SCRATCHPAD+$02
eorn ++%11111111 ;otherwise get two's compliment of maximum speed
tay
iny
sty SCRATCHPAD+$07 ;store two's compliment here
ldax SprObject_Y_MoveForce,x
secsub ;subtract upward movement amount from contents
sbci SCRATCHPAD+$01 ;of movement force, note that $01 is twice as large as $00,
push af
stax SprObject_Y_MoveForce,x ;thus it effectively undoes add we did earlier
ldax SprObject_Y_Speed,x
ld h,a
pop af
ld a,h
sbcn ++$00 ;subtract borrow from vertical speed and store
stax SprObject_Y_Speed,x
cmpi SCRATCHPAD+$07 ;compare vertical speed to two's compliment
bpl ExVMove ;if less negatively than preset maximum, skip this part
ldax SprObject_Y_MoveForce,x
cmpn ++$80 ;check if fractional part is above certain amount,
cmpcy
bcs ExVMove ;and if so, branch to leave
lda SCRATCHPAD+$07
stax SprObject_Y_Speed,x ;keep vertical speed within maximum value
ldan ++$ff
stax SprObject_Y_MoveForce,x ;clear fractional
ExVMove: rts ;leave!
;-------------------------------------------------------------------------------------
EnemiesAndLoopsCore:
ldax Enemy_Flag,x ;check data here for MSB set
pha ;save in stack
asl
bcs ChkBowserF ;if MSB set in enemy flag, branch ahead of jumps
pla ;get from stack
checka
beq ChkAreaTsk ;if data zero, branch
jmp RunEnemyObjectsCore ;otherwise, jump to run enemy subroutines
ChkAreaTsk: lda AreaParserTaskNum ;check number of tasks to perform
andn ++$07
cmpn ++$07 ;if at a specific task, jump and leave
beq ExitELCore
jmp ProcLoopCommand ;otherwise, jump to process loop command/load enemies
ChkBowserF: pla ;get data from stack
andn ++%00001111 ;mask out high nybble
tay
lday Enemy_Flag,y ;use as pointer and load same place with different offset
checka
bne ExitELCore
stax Enemy_Flag,x ;if second enemy flag not set, also clear first one
ExitELCore: rts
;--------------------------------
;loop command data
LoopCmdWorldNumber:
.db $03, $03, $06, $06, $06, $06, $06, $06, $07, $07, $07
LoopCmdPageNumber:
.db $05, $09, $04, $05, $06, $08, $09, $0a, $06, $0b, $10
LoopCmdYPosition:
.db $40, $b0, $b0, $80, $40, $40, $80, $40, $f0, $f0, $f0
ExecGameLoopback:
lda Player_PageLoc ;send player back four pages
secsub
sbcn ++$04
sta Player_PageLoc
lda CurrentPageLoc ;send current page back four pages
secsub
sbcn ++$04
sta CurrentPageLoc
lda ScreenLeft_PageLoc ;subtract four from page location
secsub ;of screen's left border
sbcn ++$04
sta ScreenLeft_PageLoc
lda ScreenRight_PageLoc ;do the same for the page location
secsub ;of screen's right border
sbcn ++$04
sta ScreenRight_PageLoc
lda AreaObjectPageLoc ;subtract four from page control
secsub ;for area objects
sbcn ++$04
sta AreaObjectPageLoc
ldan ++$00 ;initialize page select for both
sta EnemyObjectPageSel ;area and enemy objects
sta AreaObjectPageSel
sta EnemyDataOffset ;initialize enemy object data offset
sta EnemyObjectPageLoc ;and enemy object page control
lday AreaDataOfsLoopback,y ;adjust area object offset based on
sta AreaDataOffset ;which loop command we encountered
rts
ProcLoopCommand:
lda LoopCommand ;check if loop command was found
checka
beq ChkEnemyFrenzy
lda CurrentColumnPos ;check to see if we're still on the first page
checka
bne ChkEnemyFrenzy ;if not, do not loop yet
ldyn ++$0b ;start at the end of each set of loop data
FindLoop: dey
bmi ChkEnemyFrenzy ;if all data is checked and not match, do not loop
lda WorldNumber ;check to see if one of the world numbers
cmpy LoopCmdWorldNumber,y ;matches our current world number
bne FindLoop
lda CurrentPageLoc ;check to see if one of the page numbers
cmpy LoopCmdPageNumber,y ;matches the page we're currently on
bne FindLoop
lda Player_Y_Position ;check to see if the player is at the correct position
cmpy LoopCmdYPosition,y ;if not, branch to check for world 7
bne WrongChk
lda Player_State ;check to see if the player is
cmpn ++$00 ;on solid ground (i.e. not jumping or falling)
bne WrongChk ;if not, player fails to pass loop, and loopback
lda WorldNumber ;are we in world 7? (check performed on correct
cmpn ++World7 ;vertical position and on solid ground)
bne InitMLp ;if not, initialize flags used there, otherwise
inci MultiLoopCorrectCntr ;increment counter for correct progression
IncMLoop: inci MultiLoopPassCntr ;increment master multi-part counter
lda MultiLoopPassCntr ;have we done all three parts?
cmpn ++$03
bne InitLCmd ;if not, skip this part
lda MultiLoopCorrectCntr ;if so, have we done them all correctly?
cmpn ++$03
beq InitMLp ;if so, branch past unnecessary check here
bne DoLpBack ;unconditional branch if previous branch fails
WrongChk: lda WorldNumber ;are we in world 7? (check performed on
cmpn ++World7 ;incorrect vertical position or not on solid ground)
beq IncMLoop
DoLpBack: jsr ExecGameLoopback ;if player is not in right place, loop back
jsr KillAllEnemies
InitMLp: ldan ++$00 ;initialize counters used for multi-part loop commands
sta MultiLoopPassCntr
sta MultiLoopCorrectCntr
InitLCmd: ldan ++$00 ;initialize loop command flag
sta LoopCommand
;--------------------------------
ChkEnemyFrenzy:
lda EnemyFrenzyQueue ;check for enemy object in frenzy queue
checka
beq ProcessEnemyData ;if not, skip this part
stax Enemy_ID,x ;store as enemy object identifier here
ldan ++$01
stax Enemy_Flag,x ;activate enemy object flag
ldan ++$00
stax Enemy_State,x ;initialize state and frenzy queue
sta EnemyFrenzyQueue
jmp InitEnemyObject ;and then jump to deal with this enemy
;--------------------------------
;$06 - used to hold page location of extended right boundary
;$07 - used to hold high nybble of position of extended right boundary
ProcessEnemyData:
ldy EnemyDataOffset ;get offset of enemy object data
ldayindirect (EnemyData),y ;load first byte
cmpn ++$ff ;check for EOD terminator
bne CheckEndofBuffer
jmp CheckFrenzyBuffer ;if found, jump to check frenzy buffer, otherwise
CheckEndofBuffer:
andn ++%00001111 ;check for special row $0e
cmpn ++$0e
beq CheckRightBounds ;if found, branch, otherwise
cpxn ++$05 ;check for end of buffer
cmpcy
bcc CheckRightBounds ;if not at end of buffer, branch
iny
ldayindirect (EnemyData),y ;check for specific value here
andn ++%00111111 ;not sure what this was intended for, exactly
cmpn ++$2e ;this part is quite possibly residual code
beq CheckRightBounds ;but it has the effect of keeping enemies out of
rts ;the sixth slot
CheckRightBounds:
lda ScreenRight_X_Pos ;add 48 to pixel coordinate of right boundary
clc
adcn ++$30
push af
andn ++%11110000 ;store high nybble
sta SCRATCHPAD+$07
lda ScreenRight_PageLoc ;add carry to page location of right boundary
ld h,a
pop af
ld a,h
adcn ++$00
sta SCRATCHPAD+$06 ;store page location + carry
ldy EnemyDataOffset
iny
ldayindirect (EnemyData),y ;if MSB of enemy object is clear, branch to check for row $0f
asl
bcc CheckPageCtrlRow
lda EnemyObjectPageSel ;if page select already set, do not set again
checka
bne CheckPageCtrlRow
inci EnemyObjectPageSel ;otherwise, if MSB is set, set page select
inci EnemyObjectPageLoc ;and increment page control
CheckPageCtrlRow:
dey
ldayindirect (EnemyData),y ;reread first byte
andn ++$0f
cmpn ++$0f ;check for special row $0f
bne PositionEnemyObj ;if not found, branch to position enemy object
lda EnemyObjectPageSel ;if page select set,
checka
bne PositionEnemyObj ;branch without reading second byte
iny
ldayindirect (EnemyData),y ;otherwise, get second byte, mask out 2 MSB
andn ++%00111111
sta EnemyObjectPageLoc ;store as page control for enemy object data
inci EnemyDataOffset ;increment enemy object data offset 2 bytes
inci EnemyDataOffset
inci EnemyObjectPageSel ;set page select for enemy object data and
jmp ProcLoopCommand ;jump back to process loop commands again
PositionEnemyObj:
lda EnemyObjectPageLoc ;store page control as page location
stax Enemy_PageLoc,x ;for enemy object
ldayindirect (EnemyData),y ;get first byte of enemy object
andn ++%11110000
stax Enemy_X_Position,x ;store column position
cmpi ScreenRight_X_Pos ;check column position against right boundary
ldaxkeepcy Enemy_PageLoc,x ;without subtracting, then subtract borrow
sbci ScreenRight_PageLoc ;from page location
cmpcy
bcs CheckRightExtBounds ;if enemy object beyond or at boundary, branch
ldayindirect (EnemyData),y
andn ++%00001111 ;check for special row $0e
cmpn ++$0e ;if found, jump elsewhere
beq ParseRow0e
jmp CheckThreeBytes ;if not found, unconditional jump
CheckRightExtBounds:
lda SCRATCHPAD+$07 ;check right boundary + 48 against
cmpx Enemy_X_Position,x ;column position without subtracting,
lda SCRATCHPAD+$06 ;then subtract borrow from page control temp
sbcx Enemy_PageLoc,x ;plus carry
cmpcy
bcc CheckFrenzyBuffer ;if enemy object beyond extended boundary, branch
ldan ++$01 ;store value in vertical high byte
stax Enemy_Y_HighPos,x
ldayindirect (EnemyData),y ;get first byte again
asl ;multiply by four to get the vertical
asl ;coordinate
asl
asl
stax Enemy_Y_Position,x
cmpn ++$e0 ;do one last check for special row $0e
beq ParseRow0e ;(necessary if branched to $c1cb)
iny
ldayindirect (EnemyData),y ;get second byte of object
andn ++%01000000 ;check to see if hard mode bit is set
beq CheckForEnemyGroup ;if not, branch to check for group enemy objects
lda SecondaryHardMode ;if set, check to see if secondary hard mode flag
checka
beq Inc2B ;is on, and if not, branch to skip this object completely
CheckForEnemyGroup:
ldayindirect (EnemyData),y ;get second byte and mask out 2 MSB
andn ++%00111111
cmpn ++$37 ;check for value below $37
cmpcy
bcc BuzzyBeetleMutate
cmpn ++$3f ;if $37 or greater, check for value
cmpcy
bcc DoGroup ;below $3f, branch if below $3f
BuzzyBeetleMutate:
cmpn ++Goomba ;if below $37, check for goomba
bne StrID ;value ($3f or more always fails)
ldy PrimaryHardMode ;check if primary hard mode flag is set
checky
beq StrID ;and if so, change goomba to buzzy beetle
ldan ++BuzzyBeetle
StrID: stax Enemy_ID,x ;store enemy object number into buffer
ldan ++$01
stax Enemy_Flag,x ;set flag for enemy in buffer
jsr InitEnemyObject
ldax Enemy_Flag,x ;check to see if flag is set
checka
bne Inc2B ;if not, leave, otherwise branch
rts
CheckFrenzyBuffer:
lda EnemyFrenzyBuffer ;if enemy object stored in frenzy buffer
checka
bne StrFre ;then branch ahead to store in enemy object buffer
lda VineFlagOffset ;otherwise check vine flag offset
cmpn ++$01
bne ExEPar ;if other value LOW HIGH 1, leave
ldan ++VineObject ;otherwise put vine in enemy identifier
StrFre: stax Enemy_ID,x ;store contents of frenzy buffer into enemy identifier value
InitEnemyObject:
ldan ++$00 ;initialize enemy state
stax Enemy_State,x
jsr CheckpointEnemyID ;jump ahead to run jump engine and subroutines
ExEPar: rts ;then leave
DoGroup:
jmp HandleGroupEnemies ;handle enemy group objects
ParseRow0e:
iny ;increment Y to load third byte of object
iny
ldayindirect (EnemyData),y
lsr ;move 3 MSB to the bottom, effectively
lsr ;making %xxx00000 into %00000xxx
lsr
lsr
lsr
cmpi WorldNumber ;is it the same world number as we're on?
bne NotUse ;if not, do not use (this allows multiple uses
dey ;of the same area, like the underground bonus areas)
ldayindirect (EnemyData),y ;otherwise, get second byte and use as offset
sta AreaPointer ;to addresses for level and enemy object data
iny
ldayindirect (EnemyData),y ;get third byte again, and this time mask out
andn ++%00011111 ;the 3 MSB from before, save as page number to be
sta EntrancePage ;used upon entry to area, if area is entered
NotUse: jmp Inc3B
CheckThreeBytes:
ldy EnemyDataOffset ;load current offset for enemy object data
ldayindirect (EnemyData),y ;get first byte
andn ++%00001111 ;check for special row $0e
cmpn ++$0e
bne Inc2B
Inc3B: inci EnemyDataOffset ;if row = $0e, increment three bytes
Inc2B: inci EnemyDataOffset ;otherwise increment two bytes
inci EnemyDataOffset
ldan ++$00 ;init page select for enemy objects
sta EnemyObjectPageSel
ldx ObjectOffset ;reload current offset in enemy buffers
rts ;and leave
CheckpointEnemyID:
ldax Enemy_ID,x
cmpn ++$15 ;check enemy object identifier for $15 or greater
cmpcy
bcs InitEnemyRoutines ;and branch straight to the jump engine if found
tay ;save identifier in Y register for now
ldax Enemy_Y_Position,x
or a
adcn ++$08 ;add eight pixels to what will eventually be the
stax Enemy_Y_Position,x ;enemy object's vertical coordinate ($00-$14 only)
ldan ++$01
stax EnemyOffscrBitsMasked,x ;set offscreen masked bit
tya ;get identifier back and use as offset for jump engine
InitEnemyRoutines:
jsr JumpEngine
;jump engine table for newly loaded enemy objects
.dw InitNormalEnemy ;for objects $00-$0f
.dw InitNormalEnemy
.dw InitNormalEnemy
.dw InitRedKoopa
.dw NoInitCode
.dw InitHammerBro
.dw InitGoomba
.dw InitBloober
.dw InitBulletBill
.dw NoInitCode
.dw InitCheepCheep
.dw InitCheepCheep
.dw InitPodoboo
.dw InitPiranhaPlant
.dw InitJumpGPTroopa
.dw InitRedPTroopa
.dw InitHorizFlySwimEnemy ;for objects $10-$1f
.dw InitLakitu
.dw InitEnemyFrenzy
.dw NoInitCode
.dw InitEnemyFrenzy
.dw InitEnemyFrenzy
.dw InitEnemyFrenzy
.dw InitEnemyFrenzy
.dw EndFrenzy
.dw NoInitCode
.dw NoInitCode
.dw InitShortFirebar
.dw InitShortFirebar
.dw InitShortFirebar
.dw InitShortFirebar
.dw InitLongFirebar
.dw NoInitCode ;for objects $20-$2f
.dw NoInitCode
.dw NoInitCode
.dw NoInitCode
.dw InitBalPlatform
.dw InitVertPlatform
.dw LargeLiftUp
.dw LargeLiftDown
.dw InitHoriPlatform
.dw InitDropPlatform
.dw InitHoriPlatform
.dw PlatLiftUp
.dw PlatLiftDown
.dw InitBowser
.dw PwrUpJmp ;possibly dummy value
.dw Setup_Vine
.dw NoInitCode ;for objects $30-$36
.dw NoInitCode
.dw NoInitCode
.dw NoInitCode
.dw NoInitCode
.dw InitRetainerObj
.dw EndOfEnemyInitCode
;-------------------------------------------------------------------------------------
NoInitCode:
rts ;this executed when enemy object has no init code
;--------------------------------
InitGoomba:
jsr InitNormalEnemy ;set appropriate horizontal speed
jmp SmallBBox ;set $09 as bounding box control, set other values
;--------------------------------
InitPodoboo:
ldan ++$02 ;set enemy position to below
stax Enemy_Y_HighPos,x ;the bottom of the screen
stax Enemy_Y_Position,x
lsr
stax EnemyIntervalTimer,x ;set timer for enemy
lsr
stax Enemy_State,x ;initialize enemy state, then jump to use
jmp SmallBBox ;$09 as bounding box size and set other things
;--------------------------------
InitRetainerObj:
ldan ++$b8 ;set fixed vertical position for
stax Enemy_Y_Position,x ;princess/mushroom retainer object
rts
;--------------------------------
NormalXSpdData:
.db $f8, $f4
InitNormalEnemy:
ldyn ++$01 ;load offset of 1 by default
lda PrimaryHardMode ;check for primary hard mode flag set
checka
bne GetESpd
dey ;if not set, decrement offset
GetESpd: lday NormalXSpdData,y ;get appropriate horizontal speed
SetESpd: stax Enemy_X_Speed,x ;store as speed for enemy object
jmp TallBBox ;branch to set bounding box control and other data
;--------------------------------
InitRedKoopa:
jsr InitNormalEnemy ;load appropriate horizontal speed
ldan ++$01 ;set enemy state for red koopa troopa $03
stax Enemy_State,x
rts
;--------------------------------
HBroWalkingTimerData:
.db $80, $50
InitHammerBro:
ldan ++$00 ;init horizontal speed and timer used by hammer bro
stax HammerThrowingTimer,x ;apparently to time hammer throwing
stax Enemy_X_Speed,x
ldy SecondaryHardMode ;get secondary hard mode flag
lday HBroWalkingTimerData,y
stax EnemyIntervalTimer,x ;set value as delay for hammer bro to walk left
ldan ++$0b ;set specific value for bounding box size control
jmp SetBBox
;--------------------------------
InitHorizFlySwimEnemy:
ldan ++$00 ;initialize horizontal speed
jmp SetESpd
;--------------------------------
InitBloober:
ldan ++$00 ;initialize horizontal speed
stax BlooperMoveSpeed,x
SmallBBox: ldan ++$09 ;set specific bounding box size control
checka
bne SetBBox ;unconditional branch
;--------------------------------
InitRedPTroopa:
;CY = 0 after JumpEngine ???
or a ;???
push af
ldyn ++$30 ;load central position adder for 48 pixels down
ldax Enemy_Y_Position,x ;set vertical coordinate into location to
stax RedPTroopaOrigXPos,x ;be used as original vertical coordinate
ld h,a
pop af
ld a,h
checka
bpl GetCent ;if vertical coordinate LOW $80
ldyn ++$e0 ;if =HIGH $80, load position adder for 32 pixels up
GetCent: tya ;send central position adder to A
adcx Enemy_Y_Position,x ;add to current vertical coordinate
stax RedPTroopaCenterYPos,x ;store as central vertical coordinate
TallBBox: ldan ++$03 ;set specific bounding box size control
SetBBox: stax Enemy_BoundBoxCtrl,x ;set bounding box control here
ldan ++$02 ;set moving direction for left
stax Enemy_MovingDir,x
InitVStf: ldan ++$00 ;initialize vertical speed
stax Enemy_Y_Speed,x ;and movement force
stax Enemy_Y_MoveForce,x
rts
;--------------------------------
InitBulletBill:
ldan ++$02 ;set moving direction for left
stax Enemy_MovingDir,x
ldan ++$09 ;set bounding box control for $09
stax Enemy_BoundBoxCtrl,x
rts
;--------------------------------
InitCheepCheep:
jsr SmallBBox ;set vertical bounding box, speed, init others
ldax PseudoRandomBitReg,x ;check one portion of LSFR
andn ++%00010000 ;get d4 from it
stax CheepCheepMoveMFlag,x ;save as movement flag of some sort
ldax Enemy_Y_Position,x
stax CheepCheepOrigYPos,x ;save original vertical coordinate here
rts
;--------------------------------
InitLakitu:
lda EnemyFrenzyBuffer ;check to see if an enemy is already in
checka
bne KillLakitu ;the frenzy buffer, and branch to kill lakitu if so
SetupLakitu:
ldan ++$00 ;erase counter for lakitu's reappearance
sta LakituReappearTimer
jsr InitHorizFlySwimEnemy ;set $03 as bounding box, set other attributes
jmp TallBBox2 ;set $03 as bounding box again (not necessary) and leave
KillLakitu:
jmp EraseEnemyObject
;--------------------------------
;$01-$03 - used to hold pseudorandom difference adjusters
PRDiffAdjustData:
.db $26, $2c, $32, $38
.db $20, $22, $24, $26
.db $13, $14, $15, $16
LakituAndSpinyHandler:
lda FrenzyEnemyTimer ;if timer here not expired, leave
checka
bne ExLSHand
cpxn ++$05 ;if we are on the special use slot, leave
cmpcy
bcs ExLSHand
ldan ++$80 ;set timer
sta FrenzyEnemyTimer
ldyn ++$04 ;start with the last enemy slot
ChkLak: lday Enemy_ID,y ;check all enemy slots to see
cmpn ++Lakitu ;if lakitu is on one of them
beq CreateSpiny ;if so, branch out of this loop
dey ;otherwise check another slot
bpl ChkLak ;loop until all slots are checked
inci LakituReappearTimer ;increment reappearance timer
lda LakituReappearTimer
cmpn ++$07 ;check to see if we're up to a certain value yet
cmpcy
bcc ExLSHand ;if not, leave
ldxn ++$04 ;start with the last enemy slot again
ChkNoEn: ldax Enemy_Flag,x ;check enemy buffer flag for non-active enemy slot
checka
beq CreateL ;branch out of loop if found
dex ;otherwise check next slot
bpl ChkNoEn ;branch until all slots are checked
bmi RetEOfs ;if no empty slots were found, branch to leave
CreateL: ldan ++$00 ;initialize enemy state
stax Enemy_State,x
ldan ++Lakitu ;create lakitu enemy object
stax Enemy_ID,x
jsr SetupLakitu ;do a sub to set up lakitu
ldan ++$20
jsr PutAtRightExtent ;finish setting up lakitu
RetEOfs: ldx ObjectOffset ;get enemy object buffer offset again and leave
ExLSHand: rts
;--------------------------------
CreateSpiny:
lda Player_Y_Position ;if player above a certain point, branch to leave
cmpn ++$2c
cmpcy
bcc ExLSHand
lday Enemy_State,y ;if lakitu is not in normal state, branch to leave
checka
bne ExLSHand
lday Enemy_PageLoc,y ;store horizontal coordinates (high and low) of lakitu
stax Enemy_PageLoc,x ;into the coordinates of the spiny we're going to create
lday Enemy_X_Position,y
stax Enemy_X_Position,x
ldan ++$01 ;put spiny within vertical screen unit
stax Enemy_Y_HighPos,x
lday Enemy_Y_Position,y ;put spiny eight pixels above where lakitu is
secsub
sbcn ++$08
stax Enemy_Y_Position,x
ldax PseudoRandomBitReg,x ;get 2 LSB of LSFR and save to Y
andn ++%00000011
tay
ldxn ++$02
DifLoop: lday PRDiffAdjustData,y ;get three values and save them
stax SCRATCHPAD+$01,x ;to $01-$03
iny
iny ;increment Y four bytes for each value
iny
iny
dex ;decrement X for each one
bpl DifLoop ;loop until all three are written
ldx ObjectOffset ;get enemy object buffer offset
jsr PlayerLakituDiff ;move enemy, change direction, get value - difference
ldy Player_X_Speed ;check player's horizontal speed
cpyn ++$08
cmpcy
bcs SetSpSpd ;if moving faster than a certain amount, branch elsewhere
tay ;otherwise save value in A to Y for now
ldax PseudoRandomBitReg+1,x
andn ++%00000011 ;get one of the LSFR parts and save the 2 LSB
beq UsePosv ;branch if neither bits are set
tya
eorn ++%11111111 ;otherwise get two's compliment of Y
tay
iny
UsePosv: tya ;put value from A in Y back to A (they will be lost anyway)
SetSpSpd: jsr SmallBBox ;set bounding box control, init attributes, lose contents of A
ldyn ++$02
stax Enemy_X_Speed,x ;set horizontal speed to zero because previous contents
cmpn ++$00 ;of A were lost...branch here will never be taken for
bmi SpinyRte ;the same reason
dey
SpinyRte: styx Enemy_MovingDir,x ;set moving direction to the right
ldan ++$fd
stax Enemy_Y_Speed,x ;set vertical speed to move upwards
ldan ++$01
stax Enemy_Flag,x ;enable enemy object by setting flag
ldan ++$05
stax Enemy_State,x ;put spiny in egg state and leave
ChpChpEx: rts
;--------------------------------
FirebarSpinSpdData:
.db $28, $38, $28, $38, $28
FirebarSpinDirData:
.db $00, $00, $10, $10, $00
InitLongFirebar:
jsr DuplicateEnemyObj ;create enemy object for long firebar
InitShortFirebar:
ldan ++$00 ;initialize low byte of spin state
stax FirebarSpinState_Low,x
ldax Enemy_ID,x ;subtract $1b from enemy identifier
secsub ;to get proper offset for firebar data
sbcn ++$1b
tay
lday FirebarSpinSpdData,y ;get spinning speed of firebar
stax FirebarSpinSpeed,x
lday FirebarSpinDirData,y ;get spinning direction of firebar
stax FirebarSpinDirection,x
ldax Enemy_Y_Position,x
clc ;add four pixels to vertical coordinate
adcn ++$04
stax Enemy_Y_Position,x
ldax Enemy_X_Position,x
clc ;add four pixels to horizontal coordinate
adcn ++$04
push af
stax Enemy_X_Position,x
ldax Enemy_PageLoc,x
ld h,a
pop af
ld a,h
adcn ++$00 ;add carry to page location
stax Enemy_PageLoc,x
jmp TallBBox2 ;set bounding box control (not used) and leave
;--------------------------------
;$00-$01 - used to hold pseudorandom bits
FlyCCXPositionData:
.db $80, $30, $40, $80
.db $30, $50, $50, $70
.db $20, $40, $80, $a0
.db $70, $40, $90, $68
FlyCCXSpeedData:
.db $0e, $05, $06, $0e
.db $1c, $20, $10, $0c
.db $1e, $22, $18, $14
FlyCCTimerData:
.db $10, $60, $20, $48
InitFlyingCheepCheep:
lda FrenzyEnemyTimer ;if timer here not expired yet, branch to leave
checka
bne ChpChpEx
jsr SmallBBox ;jump to set bounding box size $09 and init other values
ldax PseudoRandomBitReg+1,x
andn ++%00000011 ;set pseudorandom offset here
tay
lday FlyCCTimerData,y ;load timer with pseudorandom offset
sta FrenzyEnemyTimer
ldyn ++$03 ;load Y with default value
lda SecondaryHardMode
checka
beq MaxCC ;if secondary hard mode flag not set, do not increment Y
iny ;otherwise, increment Y to allow as many as four onscreen
MaxCC: sty SCRATCHPAD+$00 ;store whatever pseudorandom bits are in Y
cpxi SCRATCHPAD+$00 ;compare enemy object buffer offset with Y
cmpcy
bcs ChpChpEx ;if X =HIGH Y, branch to leave
ldax PseudoRandomBitReg,x
andn ++%00000011 ;get last two bits of LSFR, first part
sta SCRATCHPAD+$00 ;and store in two places
sta SCRATCHPAD+$01
ldan ++$fb ;set vertical speed for cheep-cheep
stax Enemy_Y_Speed,x
ldan ++$00 ;load default value
ldy Player_X_Speed ;check player's horizontal speed
checky
beq GSeed ;if player not moving left or right, skip this part
ldan ++$04
cpyn ++$19 ;if moving to the right but not very quickly,
cmpcy
bcc GSeed ;do not change A
asl ;otherwise, multiply A by 2
GSeed: pha ;save to stack
clc
adci SCRATCHPAD+$00 ;add to last two bits of LSFR we saved earlier
sta SCRATCHPAD+$00 ;save it there
ldax PseudoRandomBitReg+1,x
andn ++%00000011 ;if neither of the last two bits of second LSFR set,
beq RSeed ;skip this part and save contents of $00
ldax PseudoRandomBitReg+2,x
andn ++%00001111 ;otherwise overwrite with lower nybble of
sta SCRATCHPAD+$00 ;third LSFR part
RSeed: pla ;get value from stack we saved earlier
clc
adci SCRATCHPAD+$01 ;add to last two bits of LSFR we saved in other place
tay ;use as pseudorandom offset here
lday FlyCCXSpeedData,y ;get horizontal speed using pseudorandom offset
stax Enemy_X_Speed,x
ldan ++$01 ;set to move towards the right
stax Enemy_MovingDir,x
lda Player_X_Speed ;if player moving left or right, branch ahead of this part
checka
bne D2XPos1
ldy SCRATCHPAD+$00 ;get first LSFR or third LSFR lower nybble
tya ;and check for d1 set
andn ++%00000010
beq D2XPos1 ;if d1 not set, branch
ldax Enemy_X_Speed,x
eorn ++$ff ;if d1 set, change horizontal speed
clc ;into two's compliment, thus moving in the opposite
adcn ++$01 ;direction
stax Enemy_X_Speed,x
incx Enemy_MovingDir,x ;increment to move towards the left
D2XPos1: tya ;get first LSFR or third LSFR lower nybble again
andn ++%00000010
beq D2XPos2 ;check for d1 set again, branch again if not set
lda Player_X_Position ;get player's horizontal position
clc
adcy FlyCCXPositionData,y ;if d1 set, add value obtained from pseudorandom offset
push af
stax Enemy_X_Position,x ;and save as enemy's horizontal position
pop af
lda Player_PageLoc ;get player's page location
adcn ++$00 ;add carry and jump past this part
jmp FinCCSt
D2XPos2: lda Player_X_Position ;get player's horizontal position
secsub
sbcy FlyCCXPositionData,y ;if d1 not set, subtract value obtained from pseudorandom
push af
stax Enemy_X_Position,x ;offset and save as enemy's horizontal position
pop af
lda Player_PageLoc ;get player's page location
sbcn ++$00 ;subtract borrow
FinCCSt: stax Enemy_PageLoc,x ;save as enemy's page location
ldan ++$01
stax Enemy_Flag,x ;set enemy's buffer flag
stax Enemy_Y_HighPos,x ;set enemy's high vertical byte
ldan ++$f8
stax Enemy_Y_Position,x ;put enemy below the screen, and we are done
rts
;--------------------------------
InitBowser:
jsr DuplicateEnemyObj ;jump to create another bowser object
stx BowserFront_Offset ;save offset of first here
ldan ++$00
sta BowserBodyControls ;initialize bowser's body controls
sta BridgeCollapseOffset ;and bridge collapse offset
ldax Enemy_X_Position,x
sta BowserOrigXPos ;store original horizontal position here
ldan ++$df
sta BowserFireBreathTimer ;store something here
stax Enemy_MovingDir,x ;and in moving direction
ldan ++$20
sta BowserFeetCounter ;set bowser's feet timer and in enemy timer
stax EnemyFrameTimer,x
ldan ++$05
sta BowserHitPoints ;give bowser 5 hit points
lsr
sta BowserMovementSpeed ;set default movement speed here
rts
;--------------------------------
DuplicateEnemyObj:
ldyn ++$ff ;start at beginning of enemy slots
FSLoop: iny ;increment one slot
lday Enemy_Flag,y ;check enemy buffer flag for empty slot
checka
bne FSLoop ;if set, branch and keep checking
sty DuplicateObj_Offset ;otherwise set offset here
txa ;transfer original enemy buffer offset
oran ++%10000000 ;store with d7 set as flag in new enemy
stay Enemy_Flag,y ;slot as well as enemy offset
ldax Enemy_PageLoc,x
stay Enemy_PageLoc,y ;copy page location and horizontal coordinates
ldax Enemy_X_Position,x ;from original enemy to new enemy
stay Enemy_X_Position,y
ldan ++$01
stax Enemy_Flag,x ;set flag as normal for original enemy
stay Enemy_Y_HighPos,y ;set high vertical byte for new enemy
ldax Enemy_Y_Position,x
stay Enemy_Y_Position,y ;copy vertical coordinate from original to new
FlmEx: rts ;and then leave
;--------------------------------
FlameYPosData:
.db $90, $80, $70, $90
FlameYMFAdderData:
.db $ff, $01
InitBowserFlame:
lda FrenzyEnemyTimer ;if timer not expired yet, branch to leave
checka
bne FlmEx
stax Enemy_Y_MoveForce,x ;reset something here
lda NoiseSoundQueue
oran ++Sfx_BowserFlame ;load bowser's flame sound into queue
sta NoiseSoundQueue
ldy BowserFront_Offset ;get bowser's buffer offset
lday Enemy_ID,y ;check for bowser
cmpn ++Bowser
beq SpawnFromMouth ;branch if found
jsr SetFlameTimer ;get timer data based on flame counter
clc
adcn ++$20 ;add 32 frames by default
ldy SecondaryHardMode
checky
beq SetFrT ;if secondary mode flag not set, use as timer setting
secsub
sbcn ++$10 ;otherwise subtract 16 frames for secondary hard mode
SetFrT: sta FrenzyEnemyTimer ;set timer accordingly
ldax PseudoRandomBitReg,x
andn ++%00000011 ;get 2 LSB from first part of LSFR
stax BowserFlamePRandomOfs,x ;set here
tay ;use as offset
lday FlameYPosData,y ;load vertical position based on pseudorandom offset
PutAtRightExtent:
stax Enemy_Y_Position,x ;set vertical position
lda ScreenRight_X_Pos
clc
adcn ++$20 ;place enemy 32 pixels beyond right side of screen
push af
stax Enemy_X_Position,x
pop af
lda ScreenRight_PageLoc
adcn ++$00 ;add carry
stax Enemy_PageLoc,x
jmp FinishFlame ;skip this part to finish setting values
SpawnFromMouth:
lday Enemy_X_Position,y ;get bowser's horizontal position
secsub
sbcn ++$0e ;subtract 14 pixels
stax Enemy_X_Position,x ;save as flame's horizontal position
lday Enemy_PageLoc,y
stax Enemy_PageLoc,x ;copy page location from bowser to flame
lday Enemy_Y_Position,y
clc ;add 8 pixels to bowser's vertical position
adcn ++$08
stax Enemy_Y_Position,x ;save as flame's vertical position
ldax PseudoRandomBitReg,x
andn ++%00000011 ;get 2 LSB from first part of LSFR
stax Enemy_YMF_Dummy,x ;save here
tay ;use as offset
lday FlameYPosData,y ;get value here using bits as offset
ldyn ++$00 ;load default offset
cmpx Enemy_Y_Position,x ;compare value to flame's current vertical position
cmpcy
bcc SetMF ;if less, do not increment offset
iny ;otherwise increment now
SetMF: lday FlameYMFAdderData,y ;get value here and save
stax Enemy_Y_MoveForce,x ;to vertical movement force
ldan ++$00
sta EnemyFrenzyBuffer ;clear enemy frenzy buffer
FinishFlame:
ldan ++$08 ;set $08 for bounding box control
stax Enemy_BoundBoxCtrl,x
ldan ++$01 ;set high byte of vertical and
stax Enemy_Y_HighPos,x ;enemy buffer flag
stax Enemy_Flag,x
lsr
stax Enemy_X_MoveForce,x ;initialize horizontal movement force (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√), and
stax Enemy_State,x ;enemy state
rts
;--------------------------------
FireworksXPosData:
.db $00, $30, $60, $60, $00, $20
FireworksYPosData:
.db $60, $40, $70, $40, $60, $30
InitFireworks:
lda FrenzyEnemyTimer ;if timer not expired yet, branch to leave
checka
bne ExitFWk
ldan ++$20 ;otherwise reset timer
sta FrenzyEnemyTimer
deci FireworksCounter ;decrement for each explosion
ldyn ++$06 ;start at last slot
StarFChk: dey
lday Enemy_ID,y ;check for presence of star flag object
cmpn ++StarFlagObject ;if there isn't a star flag object,
bne StarFChk ;routine goes into infinite loop = crash
lday Enemy_X_Position,y
secsub ;get horizontal coordinate of star flag object, then
sbcn ++$30 ;subtract 48 pixels from it and save to
pha ;the stack
ldaykeepcy Enemy_PageLoc,y
sbcn ++$00 ;subtract the carry from the page location
sta SCRATCHPAD+$00 ;of the star flag object
lda FireworksCounter ;get fireworks counter
clc
adcy Enemy_State,y ;add state of star flag object (possibly not necessary)
tay ;use as offset
pla ;get saved horizontal coordinate of star flag - 48 pixels
clc
adcy FireworksXPosData,y ;add number based on offset of fireworks counter
push af
stax Enemy_X_Position,x ;store as the fireworks object horizontal coordinate
pop af
lda SCRATCHPAD+$00
adcn ++$00 ;add carry and store as page location for
stax Enemy_PageLoc,x ;the fireworks object
lday FireworksYPosData,y ;get vertical position using same offset
stax Enemy_Y_Position,x ;and store as vertical coordinate for fireworks object
ldan ++$01
stax Enemy_Y_HighPos,x ;store in vertical high byte
stax Enemy_Flag,x ;and activate enemy buffer flag
lsr
stax ExplosionGfxCounter,x ;initialize explosion counter
ldan ++$08
stax ExplosionTimerCounter,x ;set explosion timing counter
ExitFWk: rts
;--------------------------------
Bitmasks:
.db %00000001, %00000010, %00000100, %00001000, %00010000, %00100000, %01000000, %10000000
Enemy17YPosData:
.db $40, $30, $90, $50, $20, $60, $a0, $70
SwimCC_IDData:
.db $0a, $0b
BulletBillCheepCheep:
lda FrenzyEnemyTimer ;if timer not expired yet, branch to leave
checka
bne ExF17
lda AreaType ;are we in a water-type level?
checka
bne DoBulletBills ;if not, branch elsewhere
cpxn ++$03 ;are we past third enemy slot?
cmpcy
bcs ExF17 ;if so, branch to leave
ldyn ++$00 ;load default offset
ldax PseudoRandomBitReg,x
cmpn ++$aa ;check first part of LSFR against preset value
cmpcy
bcc ChkW2 ;if less than preset, do not increment offset
iny ;otherwise increment
ChkW2: lda WorldNumber ;check world number
cmpn ++World2
beq Get17ID ;if we're on world 2, do not increment offset
iny ;otherwise increment
Get17ID: tya
andn ++%00000001 ;mask out all but last bit of offset
tay
lday SwimCC_IDData,y ;load identifier for cheep-cheeps
Set17ID: stax Enemy_ID,x ;store whatever's in A as enemy identifier
lda BitMFilter
cmpn ++$ff ;if not all bits set, skip init part and compare bits
bne GetRBit
ldan ++$00 ;initialize vertical position filter
sta BitMFilter
GetRBit: ldax PseudoRandomBitReg,x ;get first part of LSFR
andn ++%00000111 ;mask out all but 3 LSB
ChkRBit: tay ;use as offset
lday Bitmasks,y ;load bitmask
biti BitMFilter ;perform AND on filter without changing it
beq AddFBit
iny ;increment offset
tya
andn ++%00000111 ;mask out all but 3 LSB thus keeping it 0-7
jmp ChkRBit ;do another check
AddFBit: orai BitMFilter ;add bit to already set bits in filter
sta BitMFilter ;and store
lday Enemy17YPosData,y ;load vertical position using offset
jsr PutAtRightExtent ;set vertical position and other values
stax Enemy_YMF_Dummy,x ;initialize dummy variable
ldan ++$20 ;set timer
sta FrenzyEnemyTimer
jmp CheckpointEnemyID ;process our new enemy object
DoBulletBills:
ldyn ++$ff ;start at beginning of enemy slots
BB_SLoop: iny ;move onto the next slot
cpyn ++$05 ;branch to play sound if we've done all slots
cmpcy
bcs FireBulletBill
lday Enemy_Flag,y ;if enemy buffer flag not set,
checka
beq BB_SLoop ;loop back and check another slot
lday Enemy_ID,y
cmpn ++BulletBill_FrenzyVar ;check enemy identifier for
bne BB_SLoop ;bullet bill object (frenzy variant)
ExF17: rts ;if found, leave
FireBulletBill:
lda Square2SoundQueue
oran ++Sfx_Blast ;play fireworks/gunfire sound
sta Square2SoundQueue
ldan ++BulletBill_FrenzyVar ;load identifier for bullet bill object
checka
bne Set17ID ;unconditional branch
;--------------------------------
;$00 - used to store Y position of group enemies
;$01 - used to store enemy ID
;$02 - used to store page location of right side of screen
;$03 - used to store X position of right side of screen
HandleGroupEnemies:
ldyn ++$00 ;load value for green koopa troopa
secsub
sbcn ++$37 ;subtract $37 from second byte read
pha ;save result in stack for now
cmpn ++$04 ;was byte in $3b-$3e range?
cmpcy
bcs SnglID ;if so, branch
pha ;save another copy to stack
ldyn ++Goomba ;load value for goomba enemy
lda PrimaryHardMode ;if primary hard mode flag not set,
checka
beq PullID ;branch, otherwise change to value
ldyn ++BuzzyBeetle ;for buzzy beetle
PullID: pla ;get second copy from stack
SnglID: sty SCRATCHPAD+$01 ;save enemy id here
ldyn ++$b0 ;load default y coordinate
andn ++$02 ;check to see if d1 was set
beq SetYGp ;if so, move y coordinate up,
ldyn ++$70 ;otherwise branch and use default
SetYGp: sty SCRATCHPAD+$00 ;save y coordinate here
lda ScreenRight_PageLoc ;get page number of right edge of screen
sta SCRATCHPAD+$02 ;save here
lda ScreenRight_X_Pos ;get pixel coordinate of right edge
sta SCRATCHPAD+$03 ;save here
ldyn ++$02 ;load two enemies by default
pla ;get first copy from stack
lsr ;check to see if d0 was set
bcc CntGrp ;if not, use default value
iny ;otherwise increment to three enemies
CntGrp: sty NumberofGroupEnemies ;save number of enemies here
GrLoop: ldxn ++$ff ;start at beginning of enemy buffers
GSltLp: inx ;increment and branch if past
cpxn ++$05 ;end of buffers
cmpcy
bcs NextED
ldax Enemy_Flag,x ;check to see if enemy is already
checka
bne GSltLp ;stored in buffer, and branch if so
lda SCRATCHPAD+$01
stax Enemy_ID,x ;store enemy object identifier
lda SCRATCHPAD+$02
stax Enemy_PageLoc,x ;store page location for enemy object
lda SCRATCHPAD+$03
stax Enemy_X_Position,x ;store x coordinate for enemy object
clc
adcn ++$18 ;add 24 pixels for next enemy
sta SCRATCHPAD+$03
lda SCRATCHPAD+$02 ;add carry to page location for
adcn ++$00 ;next enemy
sta SCRATCHPAD+$02
lda SCRATCHPAD+$00 ;store y coordinate for enemy object
stax Enemy_Y_Position,x
ldan ++$01 ;activate flag for buffer, and
stax Enemy_Y_HighPos,x ;put enemy within the screen vertically
stax Enemy_Flag,x
jsr CheckpointEnemyID ;process each enemy object separately
deci NumberofGroupEnemies ;do this until we run out of enemy objects
bne GrLoop
NextED: jmp Inc2B ;jump to increment data offset and leave
;--------------------------------
InitPiranhaPlant:
ldan ++$01 ;set initial speed
stax PiranhaPlant_Y_Speed,x
lsr
stax Enemy_State,x ;initialize enemy state and what would normally
stax PiranhaPlant_MoveFlag,x ;be used as vertical speed, but not in this case
ldax Enemy_Y_Position,x
stax PiranhaPlantDownYPos,x ;save original vertical coordinate here
secsub
sbcn ++$18
stax PiranhaPlantUpYPos,x ;save original vertical coordinate - 24 pixels here
ldan ++$09
jmp SetBBox2 ;set specific value for bounding box control ;эрўшэр ё юс·хъЄр #9 шфєЄ misc objects (ўЄю ¤Єю???)
;--------------------------------
InitEnemyFrenzy:
ldax Enemy_ID,x ;load enemy identifier
sta EnemyFrenzyBuffer ;save in enemy frenzy buffer
secsub
sbcn ++$12 ;subtract 12 and use as offset for jump engine
jsr JumpEngine
;frenzy object jump table
.dw LakituAndSpinyHandler
.dw NoFrenzyCode
.dw InitFlyingCheepCheep
.dw InitBowserFlame
.dw InitFireworks
.dw BulletBillCheepCheep
;--------------------------------
NoFrenzyCode:
rts
;--------------------------------
EndFrenzy:
ldyn ++$05 ;start at last slot
LakituChk: lday Enemy_ID,y ;check enemy identifiers
cmpn ++Lakitu ;for lakitu
bne NextFSlot
ldan ++$01 ;if found, set state
stay Enemy_State,y
NextFSlot: dey ;move onto the next slot
bpl LakituChk ;do this until all slots are checked
ldan ++$00
sta EnemyFrenzyBuffer ;empty enemy frenzy buffer
stax Enemy_Flag,x ;disable enemy buffer flag for this object
rts
;--------------------------------
InitJumpGPTroopa:
ldan ++$02 ;set for movement to the left
stax Enemy_MovingDir,x
ldan ++$f8 ;set horizontal speed
stax Enemy_X_Speed,x
TallBBox2: ldan ++$03 ;set specific value for bounding box control
SetBBox2: stax Enemy_BoundBoxCtrl,x ;set bounding box control then leave
rts
;--------------------------------
InitBalPlatform:
decx Enemy_Y_Position,x ;raise vertical position by two pixels
decx Enemy_Y_Position,x
ldy SecondaryHardMode ;if secondary hard mode flag not set,
checky
bne AlignP ;branch ahead
ldyn ++$02 ;otherwise set value here
jsr PosPlatform ;do a sub to add or subtract pixels
AlignP: ldyn ++$ff ;set default value here for now
lda BalPlatformAlignment ;get current balance platform alignment
stax Enemy_State,x ;set platform alignment to object state here
checka
bpl SetBPA ;if old alignment $ff, put $ff as alignment for negative
txa ;if old contents already $ff, put
tay ;object offset as alignment to make next positive
SetBPA: sty BalPlatformAlignment ;store whatever value's in Y here
ldan ++$00
stax Enemy_MovingDir,x ;init moving direction
tay ;init Y
jsr PosPlatform ;do a sub to add 8 pixels, then run shared code here
;--------------------------------
InitDropPlatform:
ldan ++$ff
stax PlatformCollisionFlag,x ;set some value here
jmp CommonPlatCode ;then jump ahead to execute more code
;--------------------------------
InitHoriPlatform:
ldan ++$00
stax XMoveSecondaryCounter,x ;init one of the moving counters
jmp CommonPlatCode ;jump ahead to execute more code
;--------------------------------
InitVertPlatform:
ldyn ++$40 ;set default value here
ldax Enemy_Y_Position,x ;check vertical position
checka
bpl SetYO ;if above a certain point, skip this part
eorn ++$ff
clc ;otherwise get two's compliment
adcn ++$01
ldyn ++$c0 ;get alternate value to add to vertical position
SetYO: stax YPlatformTopYPos,x ;save as top vertical position
tya
clc ;load value from earlier, add number of pixels
adcx Enemy_Y_Position,x ;to vertical position
stax YPlatformCenterYPos,x ;save result as central vertical position
;--------------------------------
CommonPlatCode:
jsr InitVStf ;do a sub to init certain other values
SPBBox: ldan ++$05 ;set default bounding box size control
ldy AreaType
cpyn ++$03 ;check for castle-type level
beq CasPBB ;use default value if found
ldy SecondaryHardMode ;otherwise check for secondary hard mode flag
checky
bne CasPBB ;if set, use default value
ldan ++$06 ;use alternate value if not castle or secondary not set
CasPBB: stax Enemy_BoundBoxCtrl,x ;set bounding box size control here and leave
rts
;--------------------------------
LargeLiftUp:
jsr PlatLiftUp ;execute code for platforms going up
jmp LargeLiftBBox ;overwrite bounding box for large platforms
LargeLiftDown:
jsr PlatLiftDown ;execute code for platforms going down
LargeLiftBBox:
jmp SPBBox ;jump to overwrite bounding box size control
;--------------------------------
PlatLiftUp:
ldan ++$10 ;set movement amount here
stax Enemy_Y_MoveForce,x
ldan ++$ff ;set moving speed for platforms going up
stax Enemy_Y_Speed,x
jmp CommonSmallLift ;skip ahead to part we should be executing
;--------------------------------
PlatLiftDown:
ldan ++$f0 ;set movement amount here
stax Enemy_Y_MoveForce,x
ldan ++$00 ;set moving speed for platforms going down
stax Enemy_Y_Speed,x
;--------------------------------
CommonSmallLift:
ldyn ++$01
jsr PosPlatform ;do a sub to add 12 pixels due to preset value
ldan ++$04
stax Enemy_BoundBoxCtrl,x ;set bounding box control for small platforms
rts
;--------------------------------
PlatPosDataLow:
.db $08,$0c,$f8
PlatPosDataHigh:
.db $00,$00,$ff
PosPlatform:
ldax Enemy_X_Position,x ;get horizontal coordinate
clc
adcy PlatPosDataLow,y ;add or subtract pixels depending on offset
push af
stax Enemy_X_Position,x ;store as new horizontal coordinate
ldax Enemy_PageLoc,x
ld h,a
pop af
ld a,h
adcy PlatPosDataHigh,y ;add or subtract page location depending on offset
stax Enemy_PageLoc,x ;store as new page location
rts ;and go back
;--------------------------------
EndOfEnemyInitCode:
rts
;-------------------------------------------------------------------------------------
RunEnemyObjectsCore:
;jr $
ldx ObjectOffset ;get offset for enemy object buffer
ldan ++$00 ;load value 0 for jump engine by default
ldyx Enemy_ID,x
cpyn ++$15 ;if enemy object LOW $15, use default value
cmpcy
bcc JmpEO
tya ;otherwise subtract $14 from the value and use
cmpcy
sbcn ++$14 ;as value for jump engine
JmpEO: jsr JumpEngine
.dw RunNormalEnemies ;for objects $00-$14
.dw RunBowserFlame ;for objects $15-$1f
.dw RunFireworks
.dw NoRunCode
.dw NoRunCode
.dw NoRunCode
.dw NoRunCode
.dw RunFirebarObj
.dw RunFirebarObj
.dw RunFirebarObj
.dw RunFirebarObj
.dw RunFirebarObj
.dw RunFirebarObj ;for objects $20-$2f
.dw RunFirebarObj
.dw RunFirebarObj
.dw NoRunCode
.dw RunLargePlatform
.dw RunLargePlatform
.dw RunLargePlatform
.dw RunLargePlatform
.dw RunLargePlatform
.dw RunLargePlatform
.dw RunLargePlatform
.dw RunSmallPlatform
.dw RunSmallPlatform
.dw RunBowser
.dw PowerUpObjHandler
.dw VineObjectHandler
.dw NoRunCode ;for objects $30-$35
.dw RunStarFlagObj
.dw JumpspringHandler
.dw NoRunCode
.dw WarpZoneObject
.dw RunRetainerObj
;--------------------------------
NoRunCode:
rts
;--------------------------------
RunRetainerObj:
jsr GetEnemyOffscreenBits
jsr RelativeEnemyPosition
jmp EnemyGfxHandler
;--------------------------------
RunNormalEnemies:
;TODO ёє∙хёЄтхээю єёъюЁшЄ№ ¤Єю
ldan ++$00 ;init sprite attributes
stax Enemy_SprAttrib,x
;jr $
jsr GetEnemyOffscreenBits ;1589t (497 opt)
jsr RelativeEnemyPosition ;316t (274 opt)
if Z80
logicframe=$+1
ld l,0
dec l
call z,EnemyGfxHandler ;Єюы№ъю т яюёыхфэхь ърфЁх ыюушъш (эхяюёЁхфёЄтхээю яхЁхф юЄюсЁрцхэшхь)
else
jsr EnemyGfxHandler ;4939t (тэєЄЁш 3 т√чютр яю 768t) (3280 opt) <----- TODO эх т√ч√трЄ№, хёыш ¤Єю эх яюёыхфэшщ ърфЁ ыюушъш?
endif
jsr GetEnemyBoundBox ;631t (452 opt)
jsr EnemyToBGCollisionDet ;3500t (тэєЄЁш ChkUnderEnemy (cc46) = 1193t, BlockBufferChk_Enemy (cf18) = 1159t) (2058 opt) <-----
jsr EnemiesCollision ;45/230t (ьюцхЄ с√Є№ ъєфр сюы№°х) (209 opt)
jsr PlayerEnemyCollision ;230t (58 opt)
ldy TimerControl ;if master timer control set, skip to last routine
checky
bne SkipMove
jsr EnemyMovementSubs ;1408t (833 opt)
SkipMove: jmp OffscreenBoundsCheck
EnemyMovementSubs:
ldax Enemy_ID,x
jsr JumpEngine
.dw MoveNormalEnemy ;only objects $00-$14 use this table
.dw MoveNormalEnemy
.dw MoveNormalEnemy
.dw MoveNormalEnemy
.dw MoveNormalEnemy
.dw ProcHammerBro
.dw MoveNormalEnemy
.dw MoveBloober
.dw MoveBulletBill
.dw NoMoveCode
.dw MoveSwimmingCheepCheep
.dw MoveSwimmingCheepCheep
.dw MovePodoboo
.dw MovePiranhaPlant
.dw MoveJumpingEnemy
.dw ProcMoveRedPTroopa
.dw MoveFlyGreenPTroopa
.dw MoveLakitu
.dw MoveNormalEnemy
.dw NoMoveCode ;dummy
.dw MoveFlyingCheepCheep
;--------------------------------
NoMoveCode:
rts
;--------------------------------
RunBowserFlame:
jsr ProcBowserFlame
jsr GetEnemyOffscreenBits
jsr RelativeEnemyPosition
jsr GetEnemyBoundBox
jsr PlayerEnemyCollision
jmp OffscreenBoundsCheck
;--------------------------------
RunFirebarObj:
jsr ProcFirebar
jmp OffscreenBoundsCheck
;--------------------------------
RunSmallPlatform:
jsr GetEnemyOffscreenBits
jsr RelativeEnemyPosition
jsr SmallPlatformBoundBox
jsr SmallPlatformCollision
jsr RelativeEnemyPosition
jsr DrawSmallPlatform
jsr MoveSmallPlatform
jmp OffscreenBoundsCheck
;--------------------------------
RunLargePlatform:
jsr GetEnemyOffscreenBits
jsr RelativeEnemyPosition
jsr LargePlatformBoundBox
jsr LargePlatformCollision
lda TimerControl ;if master timer control set,
checka
bne SkipPT ;skip subroutine tree
jsr LargePlatformSubroutines
SkipPT: jsr RelativeEnemyPosition
jsr DrawLargePlatform
jmp OffscreenBoundsCheck
;--------------------------------
LargePlatformSubroutines:
ldax Enemy_ID,x ;subtract $24 to get proper offset for jump table
secsub
sbcn ++$24
jsr JumpEngine
.dw BalancePlatform ;table used by objects $24-$2a
.dw YMovingPlatform
.dw MoveLargeLiftPlat
.dw MoveLargeLiftPlat
.dw XMovingPlatform
.dw DropPlatform
.dw RightPlatform
;-------------------------------------------------------------------------------------
EraseEnemyObject:
;TODO ix
ldan ++$00 ;clear all enemy object variables
stax Enemy_Flag,x
stax Enemy_ID,x
stax Enemy_State,x
stax FloateyNum_Control,x
stax EnemyIntervalTimer,x
stax ShellChainCounter,x
stax Enemy_SprAttrib,x
stax EnemyFrameTimer,x
rts
;-------------------------------------------------------------------------------------
MovePodoboo:
ldax EnemyIntervalTimer,x ;check enemy timer
checka
bne PdbM ;branch to move enemy if not expired
jsr InitPodoboo ;otherwise set up podoboo again
ldax PseudoRandomBitReg+1,x ;get part of LSFR
oran ++%10000000 ;set d7
stax Enemy_Y_MoveForce,x ;store as movement force
andn ++%00001111 ;mask out high nybble
oran ++$06 ;set for at least six intervals
stax EnemyIntervalTimer,x ;store as new enemy timer
ldan ++$f9
stax Enemy_Y_Speed,x ;set vertical speed to move podoboo upwards
PdbM: jmp MoveJ_EnemyVertically ;branch to impose gravity on podoboo
;--------------------------------
;$00 - used in HammerBroJumpCode as bitmask
HammerThrowTmrData:
.db $30, $1c
XSpeedAdderData:
.db $00, $e8, $00, $18
RevivedXSpeed:
.db $08, $f8, $0c, $f4
ProcHammerBro:
ldax Enemy_State,x ;check hammer bro's enemy state for d5 set
andn ++%00100000
beq ChkJH ;if not set, go ahead with code
jmp MoveDefeatedEnemy ;otherwise jump to something else
ChkJH: ldax HammerBroJumpTimer,x ;check jump timer
checka
beq HammerBroJumpCode ;if expired, branch to jump
decx HammerBroJumpTimer,x ;otherwise decrement jump timer
lda Enemy_OffscreenBits
andn ++%00001100 ;check offscreen bits
bne MoveHammerBroXDir ;if hammer bro a little offscreen, skip to movement code
ldax HammerThrowingTimer,x ;check hammer throwing timer
checka
bne DecHT ;if not expired, skip ahead, do not throw hammer
ldy SecondaryHardMode ;otherwise get secondary hard mode flag
lday HammerThrowTmrData,y ;get timer data using flag as offset
stax HammerThrowingTimer,x ;set as new timer
jsr SpawnHammerObj ;do a sub here to spawn hammer object
bcc DecHT ;if carry clear, hammer not spawned, skip to decrement timer
ldax Enemy_State,x
oran ++%00001000 ;set d3 in enemy state for hammer throw
stax Enemy_State,x
jmp MoveHammerBroXDir ;jump to move hammer bro
DecHT: decx HammerThrowingTimer,x ;decrement timer
jmp MoveHammerBroXDir ;jump to move hammer bro
HammerBroJumpLData:
.db $20, $37
HammerBroJumpCode:
ldax Enemy_State,x ;get hammer bro's enemy state
andn ++%00000111 ;mask out all but 3 LSB
cmpn ++$01 ;check for d0 set (for jumping)
beq MoveHammerBroXDir ;if set, branch ahead to moving code
ldan ++$00 ;load default value here
sta SCRATCHPAD+$00 ;save into temp variable for now
ldyn ++$fa ;set default vertical speed
ldax Enemy_Y_Position,x ;check hammer bro's vertical coordinate
checka
bmi SetHJ ;if on the bottom half of the screen, use current speed
ldyn ++$fd ;otherwise set alternate vertical speed
cmpn ++$70 ;check to see if hammer bro is above the middle of screen
cmpcy
inci SCRATCHPAD+$00 ;increment preset value to $01
bcc SetHJ ;if above the middle of the screen, use current speed and $01
deci SCRATCHPAD+$00 ;otherwise return value to $00
ldax PseudoRandomBitReg+1,x ;get part of LSFR, mask out all but LSB
andn ++$01
bne SetHJ ;if d0 of LSFR set, branch and use current speed and $00
ldyn ++$fa ;otherwise reset to default vertical speed
SetHJ: styx Enemy_Y_Speed,x ;set vertical speed for jumping
ldax Enemy_State,x ;set d0 in enemy state for jumping
oran ++$01
stax Enemy_State,x
lda SCRATCHPAD+$00 ;load preset value here to use as bitmask
andx PseudoRandomBitReg+2,x ;and do bit-wise comparison with part of LSFR
tay ;then use as offset
lda SecondaryHardMode ;check secondary hard mode flag
checka
bne HJump
tay ;if secondary hard mode flag clear, set offset to 0
HJump: lday HammerBroJumpLData,y ;get jump length timer data using offset from before
stax EnemyFrameTimer,x ;save in enemy timer
ldax PseudoRandomBitReg+1,x
oran ++%11000000 ;get contents of part of LSFR, set d7 and d6, then
stax HammerBroJumpTimer,x ;store in jump timer
MoveHammerBroXDir:
ldyn ++$fc ;move hammer bro a little to the left
lda FrameCounter
andn ++%01000000 ;change hammer bro's direction every 64 frames
bne Shimmy
ldyn ++$04 ;if d6 set in counter, move him a little to the right
Shimmy: styx Enemy_X_Speed,x ;store horizontal speed
ldyn ++$01 ;set to face right by default
jsr PlayerEnemyDiff ;get horizontal difference between player and hammer bro
bmi SetShim ;if enemy to the left of player, skip this part
iny ;set to face left
ldax EnemyIntervalTimer,x ;check walking timer
checka
bne SetShim ;if not yet expired, skip to set moving direction
ldan ++$f8
stax Enemy_X_Speed,x ;otherwise, make the hammer bro walk left towards player
SetShim: styx Enemy_MovingDir,x ;set moving direction
MoveNormalEnemy:
ldyn ++$00 ;init Y to leave horizontal movement as-is
ldax Enemy_State,x
andn ++%01000000 ;check enemy state for d6 set, if set skip
bne FallE ;to move enemy vertically, then horizontally if necessary
ldax Enemy_State,x
asl ;check enemy state for d7 set
bcs SteadM ;if set, branch to move enemy horizontally
ldax Enemy_State,x
andn ++%00100000 ;check enemy state for d5 set
bne MoveDefeatedEnemy ;if set, branch to move defeated enemy object
ldax Enemy_State,x
andn ++%00000111 ;check d2-d0 of enemy state for any set bits
beq SteadM ;if enemy in normal state, branch to move enemy horizontally
cmpn ++$05
beq FallE ;if enemy in state used by spiny's egg, go ahead here
cmpn ++$03
cmpcy
bcs ReviveStunned ;if enemy in states $03 or $04, skip ahead to yet another part
FallE: jsr MoveD_EnemyVertically ;do a sub here to move enemy downwards
ldyn ++$00
ldax Enemy_State,x ;check for enemy state $02
cmpn ++$02
beq MEHor ;if found, branch to move enemy horizontally
andn ++%01000000 ;check for d6 set
beq SteadM ;if not set, branch to something else
ldax Enemy_ID,x
cmpn ++PowerUpObject ;check for power-up object
beq SteadM
bne SlowM ;if any other object where d6 set, jump to set Y
MEHor: jmp MoveEnemyHorizontally ;jump here to move enemy horizontally for LOW HIGH $2e and d6 set
SlowM: ldyn ++$01 ;if branched here, increment Y to slow horizontal movement
SteadM: ldax Enemy_X_Speed,x ;get current horizontal speed
pha ;save to stack
checka
bpl AddHS ;if not moving or moving right, skip, leave Y alone
iny
iny ;otherwise increment Y to next data
AddHS: clc
adcy XSpeedAdderData,y ;add value here to slow enemy down if necessary
stax Enemy_X_Speed,x ;save as horizontal speed temporarily
jsr MoveEnemyHorizontally ;then do a sub to move horizontally
pla
stax Enemy_X_Speed,x ;get old horizontal speed from stack and return to
rts ;original memory location, then leave
ReviveStunned:
ldax EnemyIntervalTimer,x ;if enemy timer not expired yet,
checka
bne ChkKillGoomba ;skip ahead to something else
stax Enemy_State,x ;otherwise initialize enemy state to normal
lda FrameCounter
andn ++$01 ;get d0 of frame counter
tay ;use as Y and increment for movement direction
iny
styx Enemy_MovingDir,x ;store as pseudorandom movement direction
dey ;decrement for use as pointer
lda PrimaryHardMode ;check primary hard mode flag
checka
beq SetRSpd ;if not set, use pointer as-is
iny
iny ;otherwise increment 2 bytes to next data
SetRSpd: lday RevivedXSpeed,y ;load and store new horizontal speed
stax Enemy_X_Speed,x ;and leave
rts
MoveDefeatedEnemy:
jsr MoveD_EnemyVertically ;execute sub to move defeated enemy downwards
jmp MoveEnemyHorizontally ;now move defeated enemy horizontally
ChkKillGoomba:
cmpn ++$0e ;check to see if enemy timer has reached
bne NKGmba ;a certain point, and branch to leave if not
ldax Enemy_ID,x
cmpn ++Goomba ;check for goomba object
bne NKGmba ;branch if not found
jsr EraseEnemyObject ;otherwise, kill this goomba object
NKGmba: rts ;leave!
;--------------------------------
MoveJumpingEnemy:
jsr MoveJ_EnemyVertically ;do a sub to impose gravity on green paratroopa
jmp MoveEnemyHorizontally ;jump to move enemy horizontally
;--------------------------------
ProcMoveRedPTroopa:
ldax Enemy_Y_Speed,x
orax Enemy_Y_MoveForce,x ;check for any vertical force or speed
bne MoveRedPTUpOrDown ;branch if any found
stax Enemy_YMF_Dummy,x ;initialize something here
ldax Enemy_Y_Position,x ;check current vs. original vertical coordinate
cmpx RedPTroopaOrigXPos,x
cmpcy
bcs MoveRedPTUpOrDown ;if current =HIGH original, skip ahead to more code
lda FrameCounter ;get frame counter
andn ++%00000111 ;mask out all but 3 LSB
bne NoIncPT ;if any bits set, branch to leave
incx Enemy_Y_Position,x ;otherwise increment red paratroopa's vertical position
NoIncPT: rts ;leave
MoveRedPTUpOrDown:
ldax Enemy_Y_Position,x ;check current vs. central vertical coordinate
cmpx RedPTroopaCenterYPos,x
cmpcy
bcc MovPTDwn ;if current LOW central, jump to move downwards
jmp MoveRedPTroopaUp ;otherwise jump to move upwards
MovPTDwn: jmp MoveRedPTroopaDown ;move downwards
;--------------------------------
;$00 - used to store adder for movement, also used as adder for platform
;$01 - used to store maximum value for secondary counter
MoveFlyGreenPTroopa:
jsr XMoveCntr_GreenPTroopa ;do sub to increment primary and secondary counters
jsr MoveWithXMCntrs ;do sub to move green paratroopa accordingly, and horizontally
ldyn ++$01 ;set Y to move green paratroopa down
lda FrameCounter
andn ++%00000011 ;check frame counter 2 LSB for any bits set
bne NoMGPT ;branch to leave if set to move up/down every fourth frame
lda FrameCounter
andn ++%01000000 ;check frame counter for d6 set
bne YSway ;branch to move green paratroopa down if set
ldyn ++$ff ;otherwise set Y to move green paratroopa up
YSway: sty SCRATCHPAD+$00 ;store adder here
ldax Enemy_Y_Position,x
clc ;add or subtract from vertical position
adci SCRATCHPAD+$00 ;to give green paratroopa a wavy flight
stax Enemy_Y_Position,x
NoMGPT: rts ;leave!
XMoveCntr_GreenPTroopa:
ldan ++$13 ;load preset maximum value for secondary counter
XMoveCntr_Platform:
sta SCRATCHPAD+$01 ;store value here
lda FrameCounter
andn ++%00000011 ;branch to leave if not on
bne NoIncXM ;every fourth frame
ldyx XMoveSecondaryCounter,x ;get secondary counter
ldax XMovePrimaryCounter,x ;get primary counter
lsr
bcs DecSeXM ;if d0 of primary counter set, branch elsewhere
cpyi SCRATCHPAD+$01 ;compare secondary counter to preset maximum value
beq IncPXM ;if equal, branch ahead of this part
incx XMoveSecondaryCounter,x ;increment secondary counter and leave
NoIncXM: rts
IncPXM: incx XMovePrimaryCounter,x ;increment primary counter and leave
rts
DecSeXM: tya ;put secondary counter in A
checka
beq IncPXM ;if secondary counter at zero, branch back
decx XMoveSecondaryCounter,x ;otherwise decrement secondary counter and leave
rts
MoveWithXMCntrs:
ldax XMoveSecondaryCounter,x ;save secondary counter to stack
pha
ldyn ++$01 ;set value here by default
ldax XMovePrimaryCounter,x
andn ++%00000010 ;if d1 of primary counter is
bne XMRight ;set, branch ahead of this part here
ldax XMoveSecondaryCounter,x
eorn ++$ff ;otherwise change secondary
clc ;counter to two's compliment
adcn ++$01
stax XMoveSecondaryCounter,x
ldyn ++$02 ;load alternate value here
XMRight: styx Enemy_MovingDir,x ;store as moving direction
jsr MoveEnemyHorizontally
sta SCRATCHPAD+$00 ;save value obtained from sub here (ёюїЁрэ хь ёьх∙хэшх X-ъююЁфшэрЄ√ яю ёЁртэхэш■ ё яЁю°ы√ь яюыюцхэшхь)
pla ;get secondary counter from stack
stax XMoveSecondaryCounter,x ;and return to original place
rts
;--------------------------------
BlooberBitmasks:
.db %00111111, %00000011
MoveBloober:
ldax Enemy_State,x
andn ++%00100000 ;check enemy state for d5 set
bne MoveDefeatedBloober ;branch if set to move defeated bloober
ldy SecondaryHardMode ;use secondary hard mode flag as offset
ldax PseudoRandomBitReg+1,x ;get LSFR
andy BlooberBitmasks,y ;mask out bits in LSFR using bitmask loaded with offset
bne BlooberSwim ;if any bits set, skip ahead to make swim
txa
lsr ;check to see if on second or fourth slot (1 or 3)
bcc FBLeft ;if not, branch to figure out moving direction
ldy Player_MovingDir ;otherwise, load player's moving direction and
bcs SBMDir ;do an unconditional branch to set
FBLeft: ldyn ++$02 ;set left moving direction by default
jsr PlayerEnemyDiff ;get horizontal difference between player and bloober
bpl SBMDir ;if enemy to the right of player, keep left
dey ;otherwise decrement to set right moving direction
SBMDir: styx Enemy_MovingDir,x ;set moving direction of bloober, then continue on here
BlooberSwim:
jsr ProcSwimmingB ;execute sub to make bloober swim characteristically
ldax Enemy_Y_Position,x ;get vertical coordinate
secsub
sbcx Enemy_Y_MoveForce,x ;subtract movement force
cmpn ++$20 ;check to see if position is above edge of status bar
cmpcy
bcc SwimX ;if so, don't do it
stax Enemy_Y_Position,x ;otherwise, set new vertical position, make bloober swim
SwimX: ldyx Enemy_MovingDir,x ;check moving direction
dey
bne LeftSwim ;if moving to the left, branch to second part
ldax Enemy_X_Position,x
clc ;add movement speed to horizontal coordinate
adcx BlooperMoveSpeed,x
push af
stax Enemy_X_Position,x ;store result as new horizontal coordinate
ldax Enemy_PageLoc,x
ld h,a
pop af
ld a,h
adcn ++$00 ;add carry to page location
stax Enemy_PageLoc,x ;store as new page location and leave
rts
LeftSwim:
ldax Enemy_X_Position,x
secsub ;subtract movement speed from horizontal coordinate
sbcx BlooperMoveSpeed,x
push af
stax Enemy_X_Position,x ;store result as new horizontal coordinate
ldax Enemy_PageLoc,x
ld h,a
pop af
ld a,h
sbcn ++$00 ;subtract borrow from page location
stax Enemy_PageLoc,x ;store as new page location and leave
rts
MoveDefeatedBloober:
jmp MoveEnemySlowVert ;jump to move defeated bloober downwards
ProcSwimmingB:
ldax BlooperMoveCounter,x ;get enemy's movement counter
andn ++%00000010 ;check for d1 set
bne ChkForFloatdown ;branch if set
lda FrameCounter
andn ++%00000111 ;get 3 LSB of frame counter
pha ;and save it to the stack
ldax BlooperMoveCounter,x ;get enemy's movement counter
lsr ;check for d0 set
bcs SlowSwim ;branch if set
pla ;pull 3 LSB of frame counter from the stack
checka
bne BSwimE ;branch to leave, execute code only every eighth frame
ldax Enemy_Y_MoveForce,x
clc ;add to movement force to speed up swim
adcn ++$01
stax Enemy_Y_MoveForce,x ;set movement force
stax BlooperMoveSpeed,x ;set as movement speed
cmpn ++$02
bne BSwimE ;if certain horizontal speed, branch to leave
incx BlooperMoveCounter,x ;otherwise increment movement counter
BSwimE: rts
SlowSwim:
pla ;pull 3 LSB of frame counter from the stack
checka
bne NoSSw ;branch to leave, execute code only every eighth frame
ldax Enemy_Y_MoveForce,x
secsub ;subtract from movement force to slow swim
sbcn ++$01
stax Enemy_Y_MoveForce,x ;set movement force
stax BlooperMoveSpeed,x ;set as movement speed
checka
bne NoSSw ;if any speed, branch to leave
incx BlooperMoveCounter,x ;otherwise increment movement counter
ldan ++$02
stax EnemyIntervalTimer,x ;set enemy's timer
NoSSw: rts ;leave
ChkForFloatdown:
ldax EnemyIntervalTimer,x ;get enemy timer
checka
beq ChkNearPlayer ;branch if expired
Floatdown:
lda FrameCounter ;get frame counter
lsr ;check for d0 set
bcs NoFD ;branch to leave on every other frame
incx Enemy_Y_Position,x ;otherwise increment vertical coordinate
NoFD: rts ;leave
ChkNearPlayer:
;CY=0??? after JumpEngine
ldax Enemy_Y_Position,x ;get vertical coordinate
or a ;???
adcn ++$10 ;add sixteen pixels
cmpi Player_Y_Position ;compare result with player's vertical coordinate
cmpcy
bcc Floatdown ;if modified vertical less than player's, branch
ldan ++$00
stax BlooperMoveCounter,x ;otherwise nullify movement counter
rts
;--------------------------------
MoveBulletBill:
ldax Enemy_State,x ;check bullet bill's enemy object state for d5 set
andn ++%00100000
beq NotDefB ;if not set, continue with movement code
jmp MoveJ_EnemyVertically ;otherwise jump to move defeated bullet bill downwards
NotDefB: ldan ++$e8 ;set bullet bill's horizontal speed
stax Enemy_X_Speed,x ;and move it accordingly (note: this bullet bill
jmp MoveEnemyHorizontally ;object occurs in frenzy object $17, not from cannons)
;--------------------------------
;$02 - used to hold preset values
;$03 - used to hold enemy state
SwimCCXMoveData:
.db $40, $80
.db $04, $04 ;residual data, not used
MoveSwimmingCheepCheep:
ldax Enemy_State,x ;check cheep-cheep's enemy object state
andn ++%00100000 ;for d5 set
beq CCSwim ;if not set, continue with movement code
jmp MoveEnemySlowVert ;otherwise jump to move defeated cheep-cheep downwards
CCSwim: sta SCRATCHPAD+$03 ;save enemy state in $03
ldax Enemy_ID,x ;get enemy identifier
secsub
sbcn ++$0a ;subtract ten for cheep-cheep identifiers
tay ;use as offset
lday SwimCCXMoveData,y ;load value here
sta SCRATCHPAD+$02
ldax Enemy_X_MoveForce,x ;load horizontal force (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
secsub
sbci SCRATCHPAD+$02 ;subtract preset value from horizontal force
push af
stax Enemy_X_MoveForce,x ;store as new horizontal force (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
ldax Enemy_X_Position,x ;get horizontal coordinate
ld h,a
pop af
ld a,h
sbcn ++$00 ;subtract borrow (thus moving it slowly)
push af
stax Enemy_X_Position,x ;and save as new horizontal coordinate
ldax Enemy_PageLoc,x
ld h,a
pop af
ld a,h
sbcn ++$00 ;subtract borrow again, this time from the
stax Enemy_PageLoc,x ;page location, then save
ldan ++$20
sta SCRATCHPAD+$02 ;save new value here
cpxn ++$02 ;check enemy object offset
cmpcy
bcc ExSwCC ;if in first or second slot, branch to leave
ldax CheepCheepMoveMFlag,x ;check movement flag
cmpn ++$10 ;if movement speed set to $00,
cmpcy
bcc CCSwimUpwards ;branch to move upwards
ldax Enemy_YMF_Dummy,x
clc
adci SCRATCHPAD+$02 ;add preset value to dummy variable to get carry
push af
stax Enemy_YMF_Dummy,x ;and save dummy
ldax Enemy_Y_Position,x ;get vertical coordinate
ld h,a
pop af
ld a,h
adci SCRATCHPAD+$03 ;add carry to it plus enemy state to slowly move it downwards
push af
stax Enemy_Y_Position,x ;save as new vertical coordinate
ldax Enemy_Y_HighPos,x
ld h,a
pop af
ld a,h
adcn ++$00 ;add carry to page location and
jmp ChkSwimYPos ;jump to end of movement code
CCSwimUpwards:
ldax Enemy_YMF_Dummy,x
secsub
sbci SCRATCHPAD+$02 ;subtract preset value to dummy variable to get borrow
push af
stax Enemy_YMF_Dummy,x ;and save dummy
ldax Enemy_Y_Position,x ;get vertical coordinate
ld h,a
pop af
ld a,h
sbci SCRATCHPAD+$03 ;subtract borrow to it plus enemy state to slowly move it upwards
push af
stax Enemy_Y_Position,x ;save as new vertical coordinate
ldax Enemy_Y_HighPos,x
ld h,a
pop af
ld a,h
sbcn ++$00 ;subtract borrow from page location
ChkSwimYPos:
stax Enemy_Y_HighPos,x ;save new page location here
ldyn ++$00 ;load movement speed to upwards by default
ldax Enemy_Y_Position,x ;get vertical coordinate
secsub
sbcx CheepCheepOrigYPos,x ;subtract original coordinate from current
bpl YPDiff ;if result positive, skip to next part
ldyn ++$10 ;otherwise load movement speed to downwards
eorn ++$ff
clc ;get two's compliment of result
adcn ++$01 ;to obtain total difference of original vs. current
YPDiff: cmpn ++$0f ;if difference between original vs. current vertical
cmpcy
bcc ExSwCC ;coordinates LOW 15 pixels, leave movement speed alone
tya
stax CheepCheepMoveMFlag,x ;otherwise change movement speed
ExSwCC: rts ;leave
;--------------------------------
;$00 - used as counter for firebar parts
;$01 - used for oscillated high byte of spin state or to hold horizontal adder
;$02 - used for oscillated high byte of spin state or to hold vertical adder
;$03 - used for mirror data
;$04 - used to store player's sprite 1 X coordinate
;$05 - used to evaluate mirror data
;$06 - used to store either screen X coordinate or sprite data offset
;$07 - used to store screen Y coordinate
;$ed - used to hold maximum length of firebar
;$ef - used to hold high byte of spinstate
;horizontal adder is at first byte + high byte of spinstate,
;vertical adder is same + 8 bytes, two's compliment
;if greater than $08 for proper oscillation
FirebarPosLookupTbl:
.db $00, $01, $03, $04, $05, $06, $07, $07, $08
.db $00, $03, $06, $09, $0b, $0d, $0e, $0f, $10
.db $00, $04, $09, $0d, $10, $13, $16, $17, $18
.db $00, $06, $0c, $12, $16, $1a, $1d, $1f, $20
.db $00, $07, $0f, $16, $1c, $21, $25, $27, $28
.db $00, $09, $12, $1b, $21, $27, $2c, $2f, $30
.db $00, $0b, $15, $1f, $27, $2e, $33, $37, $38
.db $00, $0c, $18, $24, $2d, $35, $3b, $3e, $40
.db $00, $0e, $1b, $28, $32, $3b, $42, $46, $48
.db $00, $0f, $1f, $2d, $38, $42, $4a, $4e, $50
.db $00, $11, $22, $31, $3e, $49, $51, $56, $58
FirebarMirrorData:
.db $01, $03, $02, $00
FirebarTblOffsets:
.db $00, $09, $12, $1b, $24, $2d
.db $36, $3f, $48, $51, $5a, $63
FirebarYPos:
.db $0c, $18
ProcFirebar:
jsr GetEnemyOffscreenBits ;get offscreen information
lda Enemy_OffscreenBits ;check for d3 set
andn ++%00001000 ;if so, branch to leave
bne SkipFBar
lda TimerControl ;if master timer control set, branch
checka
bne SusFbar ;ahead of this part
ldax FirebarSpinSpeed,x ;load spinning speed of firebar
jsr FirebarSpin ;modify current spinstate
andn ++%00011111 ;mask out all but 5 LSB
stax FirebarSpinState_High,x ;and store as new high byte of spinstate
SusFbar: ldax FirebarSpinState_High,x ;get high byte of spinstate
ldyx Enemy_ID,x ;check enemy identifier
cpyn ++$1f
cmpcy
bcc SetupGFB ;if LOW $1f (long firebar), branch
cmpn ++$08 ;check high byte of spinstate
beq SkpFSte ;if eight, branch to change
cmpn ++$18
bne SetupGFB ;if not at twenty-four branch to not change
SkpFSte: clc
adcn ++$01 ;add one to spinning thing to avoid horizontal state
stax FirebarSpinState_High,x
SetupGFB: sta SCRATCHPAD+$ef ;save high byte of spinning thing, modified or otherwise
jsr RelativeEnemyPosition ;get relative coordinates to screen
jsr GetFirebarPosition ;do a sub here (residual, too early to be used now)
ldyx Enemy_SprDataOffset,x ;get OAM data offset
lda Enemy_Rel_YPos ;get relative vertical coordinate
stay Sprite_Y_Position,y ;store as Y in OAM data
sta SCRATCHPAD+$07 ;also save here
lda Enemy_Rel_XPos ;get relative horizontal coordinate
stay Sprite_X_Position,y ;store as X in OAM data
sta SCRATCHPAD+$06 ;also save here
ldan ++$01
sta SCRATCHPAD+$00 ;set $01 value here (not necessary)
jsr FirebarCollision ;draw fireball part and do collision detection
ldyn ++$05 ;load value for short firebars by default
ldax Enemy_ID,x
cmpn ++$1f ;are we doing a long firebar?
cmpcy
bcc SetMFbar ;no, branch then
ldyn ++$0b ;otherwise load value for long firebars
SetMFbar: sty SCRATCHPAD+$ed ;store maximum value for length of firebars
ldan ++$00
sta SCRATCHPAD+$00 ;initialize counter here
DrawFbar: lda SCRATCHPAD+$ef ;load high byte of spinstate
jsr GetFirebarPosition ;get fireball position data depending on firebar part
jsr DrawFirebar_Collision ;position it properly, draw it and do collision detection
lda SCRATCHPAD+$00 ;check which firebar part
cmpn ++$04
bne NextFbar
ldy DuplicateObj_Offset ;if we arrive at fifth firebar part,
lday Enemy_SprDataOffset,y ;get offset from long firebar and load OAM data offset
sta SCRATCHPAD+$06 ;using long firebar offset, then store as new one here
NextFbar: inci SCRATCHPAD+$00 ;move onto the next firebar part
lda SCRATCHPAD+$00
cmpi SCRATCHPAD+$ed ;if we end up at the maximum part, go on and leave
cmpcy
bcc DrawFbar ;otherwise go back and do another
SkipFBar: rts
DrawFirebar_Collision:
lda SCRATCHPAD+$03 ;store mirror data elsewhere
sta SCRATCHPAD+$05
ldy SCRATCHPAD+$06 ;load OAM data offset for firebar
lda SCRATCHPAD+$01 ;load horizontal adder we got from position loader
lsri SCRATCHPAD+$05 ;shift LSB of mirror data
bcs AddHA ;if carry was set, skip this part
eorn ++$ff
adcn ++$01 ;otherwise get two's compliment of horizontal adder
AddHA: clc ;add horizontal coordinate relative to screen to
adci Enemy_Rel_XPos ;horizontal adder, modified or otherwise
stay Sprite_X_Position,y ;store as X coordinate here
sta SCRATCHPAD+$06 ;store here for now, note offset is saved in Y still
cmpi Enemy_Rel_XPos ;compare X coordinate of sprite to original X of firebar
cmpcy
bcs SubtR1 ;if sprite coordinate =HIGH original coordinate, branch
lda Enemy_Rel_XPos
secsub ;otherwise subtract sprite X from the
sbci SCRATCHPAD+$06 ;original one and skip this part
jmp ChkFOfs
SubtR1: secsub ;subtract original X from the
sbci Enemy_Rel_XPos ;current sprite X
ChkFOfs: cmpn ++$59 ;if difference of coordinates within a certain range,
cmpcy
bcc VAHandl ;continue by handling vertical adder
ldan ++$f8 ;otherwise, load offscreen Y coordinate
checka
bne SetVFbr ;and unconditionally branch to move sprite offscreen
VAHandl: lda Enemy_Rel_YPos ;if vertical relative coordinate offscreen,
cmpn ++$f8 ;skip ahead of this part and write into sprite Y coordinate
beq SetVFbr
lda SCRATCHPAD+$02 ;load vertical adder we got from position loader
lsri SCRATCHPAD+$05 ;shift LSB of mirror data one more time
bcs AddVA ;if carry was set, skip this part
eorn ++$ff
adcn ++$01 ;otherwise get two's compliment of second part
AddVA: clc ;add vertical coordinate relative to screen to
adci Enemy_Rel_YPos ;the second data, modified or otherwise
SetVFbr: stay Sprite_Y_Position,y ;store as Y coordinate here
sta SCRATCHPAD+$07 ;also store here for now
FirebarCollision:
jsr DrawFirebar ;run sub here to draw current tile of firebar
tya ;return OAM data offset and save
pha ;to the stack for now
lda StarInvincibleTimer ;if star mario invincibility timer
orai TimerControl ;or master timer controls set
bne NoColFB ;then skip all of this
sta SCRATCHPAD+$05 ;otherwise initialize counter
ldy Player_Y_HighPos
dey ;if player's vertical high byte offscreen,
bne NoColFB ;skip all of this
ldy Player_Y_Position ;get player's vertical position
lda PlayerSize ;get player's size
checka
bne AdjSm ;if player small, branch to alter variables
lda CrouchingFlag
checka
beq BigJp ;if player big and not crouching, jump ahead
AdjSm: inci SCRATCHPAD+$05 ;if small or big but crouching, execute this part
inci SCRATCHPAD+$05 ;first increment our counter twice (setting $02 as flag)
tya
clc ;then add 24 pixels to the player's
adcn ++$18 ;vertical coordinate
tay
BigJp: tya ;get vertical coordinate, altered or otherwise, from Y
FBCLoop: secsub ;subtract vertical position of firebar
sbci SCRATCHPAD+$07 ;from the vertical coordinate of the player
bpl ChkVFBD ;if player lower on the screen than firebar,
eorn ++$ff ;skip two's compliment part
clc ;otherwise get two's compliment
adcn ++$01
ChkVFBD: cmpn ++$08 ;if difference =HIGH 8 pixels, skip ahead of this part
cmpcy
bcs Chk2Ofs
lda SCRATCHPAD+$06 ;if firebar on far right on the screen, skip this,
cmpn ++$f0 ;because, really, what's the point?
cmpcy
bcs Chk2Ofs
lda Sprite_X_Position+4 ;get OAM X coordinate for sprite ++1
clc
adcn ++$04 ;add four pixels
sta SCRATCHPAD+$04 ;store here
secsub ;subtract horizontal coordinate of firebar
sbci SCRATCHPAD+$06 ;from the X coordinate of player's sprite 1
bpl ChkFBCl ;if modded X coordinate to the right of firebar
eorn ++$ff ;skip two's compliment part
clc ;otherwise get two's compliment
adcn ++$01
ChkFBCl: cmpn ++$08 ;if difference LOW 8 pixels, collision, thus branch
cmpcy
bcc ChgSDir ;to process
Chk2Ofs: lda SCRATCHPAD+$05 ;if value of $02 was set earlier for whatever reason,
cmpn ++$02 ;branch to increment OAM offset and leave, no collision
beq NoColFB
ldy SCRATCHPAD+$05 ;otherwise get temp here and use as offset
lda Player_Y_Position
clc
adcy FirebarYPos,y ;add value loaded with offset to player's vertical coordinate
inci SCRATCHPAD+$05 ;then increment temp and jump back
jmp FBCLoop
ChgSDir: ldxn ++$01 ;set movement direction by default
lda SCRATCHPAD+$04 ;if OAM X coordinate of player's sprite 1
cmpi SCRATCHPAD+$06 ;is greater than horizontal coordinate of firebar
cmpcy
bcs SetSDir ;then do not alter movement direction
inx ;otherwise increment it
SetSDir: stx Enemy_MovingDir ;store movement direction here
ldxn ++$00
lda SCRATCHPAD+$00 ;save value written to $00 to stack
pha
jsr InjurePlayer ;perform sub to hurt or kill player
pla
sta SCRATCHPAD+$00 ;get value of $00 from stack
NoColFB: pla ;get OAM data offset
clc ;add four to it and save
adcn ++$04
sta SCRATCHPAD+$06
ldx ObjectOffset ;get enemy object buffer offset and leave
rts
GetFirebarPosition:
pha ;save high byte of spinstate to the stack
andn ++%00001111 ;mask out low nybble
cmpn ++$09
cmpcy
bcc GetHAdder ;if lower than $09, branch ahead
eorn ++%00001111 ;otherwise get two's compliment to oscillate
clc
adcn ++$01
GetHAdder: sta SCRATCHPAD+$01 ;store result, modified or not, here
ldy SCRATCHPAD+$00 ;load number of firebar ball where we're at
lday FirebarTblOffsets,y ;load offset to firebar position data
clc
adci SCRATCHPAD+$01 ;add oscillated high byte of spinstate
tay ;to offset here and use as new offset
lday FirebarPosLookupTbl,y ;get data here and store as horizontal adder
sta SCRATCHPAD+$01
pla ;pull whatever was in A from the stack
pha ;save it again because we still need it
clc
adcn ++$08 ;add eight this time, to get vertical adder
andn ++%00001111 ;mask out high nybble
cmpn ++$09 ;if lower than $09, branch ahead
cmpcy
bcc GetVAdder
eorn ++%00001111 ;otherwise get two's compliment
clc
adcn ++$01
GetVAdder: sta SCRATCHPAD+$02 ;store result here
ldy SCRATCHPAD+$00
lday FirebarTblOffsets,y ;load offset to firebar position data again
clc
adci SCRATCHPAD+$02 ;this time add value in $02 to offset here and use as offset
tay
lday FirebarPosLookupTbl,y ;get data here and store as vertica adder
sta SCRATCHPAD+$02
pla ;pull out whatever was in A one last time
lsr ;divide by eight or shift three to the right
lsr
lsr
tay ;use as offset
lday FirebarMirrorData,y ;load mirroring data here
sta SCRATCHPAD+$03 ;store
rts
;--------------------------------
PRandomSubtracter:
.db $f8, $a0, $70, $bd, $00
FlyCCBPriority:
.db $20, $20, $20, $00, $00
MoveFlyingCheepCheep:
ldax Enemy_State,x ;check cheep-cheep's enemy state
andn ++%00100000 ;for d5 set
beq FlyCC ;branch to continue code if not set
ldan ++$00
stax Enemy_SprAttrib,x ;otherwise clear sprite attributes
jmp MoveJ_EnemyVertically ;and jump to move defeated cheep-cheep downwards
FlyCC: jsr MoveEnemyHorizontally ;move cheep-cheep horizontally based on speed and force
ldyn ++$0d ;set vertical movement amount
ldan ++$05 ;set maximum speed
jsr SetXMoveAmt ;branch to impose gravity on flying cheep-cheep
ldax Enemy_Y_MoveForce,x
lsr ;get vertical movement force and
lsr ;move high nybble to low
lsr
lsr
tay ;save as offset (note this tends to go into reach of code)
ldax Enemy_Y_Position,x ;get vertical position
secsub ;subtract pseudorandom value based on offset from position
sbcy PRandomSubtracter,y
bpl AddCCF ;if result within top half of screen, skip this part
eorn ++$ff
clc ;otherwise get two's compliment
adcn ++$01
AddCCF: cmpn ++$08 ;if result or two's compliment greater than eight,
cmpcy
bcs BPGet ;skip to the end without changing movement force
ldax Enemy_Y_MoveForce,x
clc
adcn ++$10 ;otherwise add to it
stax Enemy_Y_MoveForce,x
lsr ;move high nybble to low again
lsr
lsr
lsr
tay
BPGet: lday FlyCCBPriority,y ;load bg priority data and store (this is very likely
stax Enemy_SprAttrib,x ;broken or residual code, value is overwritten before
rts ;drawing it next frame), then leave
;--------------------------------
;$00 - used to hold horizontal difference
;$01-$03 - used to hold difference adjusters
LakituDiffAdj:
.db $15, $30, $40
MoveLakitu:
ldax Enemy_State,x ;check lakitu's enemy state
andn ++%00100000 ;for d5 set
beq ChkLS ;if not set, continue with code
jmp MoveD_EnemyVertically ;otherwise jump to move defeated lakitu downwards
ChkLS: ldax Enemy_State,x ;if lakitu's enemy state not set at all,
checka
beq Fr12S ;go ahead and continue with code
ldan ++$00
stax LakituMoveDirection,x ;otherwise initialize moving direction to move to left
sta EnemyFrenzyBuffer ;initialize frenzy buffer
ldan ++$10
checka
bne SetLSpd ;load horizontal speed and do unconditional branch
Fr12S: ldan ++Spiny
sta EnemyFrenzyBuffer ;set spiny identifier in frenzy buffer
ldyn ++$02
LdLDa: lday LakituDiffAdj,y ;load values
stay SCRATCHPAD+$0001,y ;store in zero page
dey
bpl LdLDa ;do this until all values are stired
jsr PlayerLakituDiff ;execute sub to set speed and create spinys
SetLSpd: stax LakituMoveSpeed,x ;set movement speed returned from sub
ldyn ++$01 ;set moving direction to right by default
ldax LakituMoveDirection,x
andn ++$01 ;get LSB of moving direction
bne SetLMov ;if set, branch to the end to use moving direction
ldax LakituMoveSpeed,x
eorn ++$ff ;get two's compliment of moving speed
clc
adcn ++$01
stax LakituMoveSpeed,x ;store as new moving speed
iny ;increment moving direction to left
SetLMov: styx Enemy_MovingDir,x ;store moving direction
jmp MoveEnemyHorizontally ;move lakitu horizontally
PlayerLakituDiff:
ldyn ++$00 ;set Y for default value
jsr PlayerEnemyDiff ;get horizontal difference between enemy and player
bpl ChkLakDif ;branch if enemy is to the right of the player
iny ;increment Y for left of player
lda SCRATCHPAD+$00
eorn ++$ff ;get two's compliment of low byte of horizontal difference
clc
adcn ++$01 ;store two's compliment as horizontal difference
sta SCRATCHPAD+$00
ChkLakDif: lda SCRATCHPAD+$00 ;get low byte of horizontal difference
cmpn ++$3c ;if within a certain distance of player, branch
cmpcy
bcc ChkPSpeed
ldan ++$3c ;otherwise set maximum distance
sta SCRATCHPAD+$00
ldax Enemy_ID,x ;check if lakitu is in our current enemy slot
cmpn ++Lakitu
bne ChkPSpeed ;if not, branch elsewhere
tya ;compare contents of Y, now in A
cmpx LakituMoveDirection,x ;to what is being used as horizontal movement direction
beq ChkPSpeed ;if moving toward the player, branch, do not alter
ldax LakituMoveDirection,x ;if moving to the left beyond maximum distance,
checka
beq SetLMovD ;branch and alter without delay
decx LakituMoveSpeed,x ;decrement horizontal speed
ldax LakituMoveSpeed,x ;if horizontal speed not yet at zero, branch to leave
checka
bne ExMoveLak
SetLMovD: tya ;set horizontal direction depending on horizontal
stax LakituMoveDirection,x ;difference between enemy and player if necessary
ChkPSpeed: lda SCRATCHPAD+$00
andn ++%00111100 ;mask out all but four bits in the middle
lsr ;divide masked difference by four
lsr
sta SCRATCHPAD+$00 ;store as new value
ldyn ++$00 ;init offset
lda Player_X_Speed
checka
beq SubDifAdj ;if player not moving horizontally, branch
lda ScrollAmount
checka
beq SubDifAdj ;if scroll speed not set, branch to same place
iny ;otherwise increment offset
lda Player_X_Speed
cmpn ++$19 ;if player not running, branch
cmpcy
bcc ChkSpinyO
lda ScrollAmount
cmpn ++$02 ;if scroll speed below a certain amount, branch
cmpcy
bcc ChkSpinyO ;to same place
iny ;otherwise increment once more
ChkSpinyO: ldax Enemy_ID,x ;check for spiny object
cmpn ++Spiny
bne ChkEmySpd ;branch if not found
lda Player_X_Speed ;if player not moving, skip this part
checka
bne SubDifAdj
ChkEmySpd: ldax Enemy_Y_Speed,x ;check vertical speed
checka
bne SubDifAdj ;branch if nonzero
ldyn ++$00 ;otherwise reinit offset
SubDifAdj: lday SCRATCHPAD+$0001,y ;get one of three saved values from earlier
ldy SCRATCHPAD+$00 ;get saved horizontal difference
SPixelLak: secsub ;subtract one for each pixel of horizontal difference
sbcn ++$01 ;from one of three saved values
dey
bpl SPixelLak ;branch until all pixels are subtracted, to adjust difference
ExMoveLak: rts ;leave!!!
;-------------------------------------------------------------------------------------
;$04-$05 - used to store name table address in little endian order
BridgeCollapseData:
.db $1a ;axe
.db $58 ;chain
.db $98, $96, $94, $92, $90, $8e, $8c ;bridge
.db $8a, $88, $86, $84, $82, $80
BridgeCollapse:
ldx BowserFront_Offset ;get enemy offset for bowser
ldax Enemy_ID,x ;check enemy object identifier for bowser
cmpn ++Bowser ;if not found, branch ahead,
bne SetM2 ;metatile removal not necessary
stx ObjectOffset ;store as enemy offset here
ldax Enemy_State,x ;if bowser in normal state, skip all of this
checka
beq RemoveBridge
andn ++%01000000 ;if bowser's state has d6 clear, skip to silence music
beq SetM2
ldax Enemy_Y_Position,x ;check bowser's vertical coordinate
cmpn ++$e0 ;if bowser not yet low enough, skip this part ahead
cmpcy
bcc MoveD_Bowser
SetM2: ldan ++Silence ;silence music
sta EventMusicQueue
inci OperMode_Task ;move onto next secondary mode in autoctrl mode
jmp KillAllEnemies ;jump to empty all enemy slots and then leave
MoveD_Bowser:
jsr MoveEnemySlowVert ;do a sub to move bowser downwards
jmp BowserGfxHandler ;jump to draw bowser's front and rear, then leave
RemoveBridge:
deci BowserFeetCounter ;decrement timer to control bowser's feet
bne NoBFall ;if not expired, skip all of this
ldan ++$04
sta BowserFeetCounter ;otherwise, set timer now
lda BowserBodyControls
eorn ++$01 ;invert bit to control bowser's feet
sta BowserBodyControls
ldan ++$22 ;put high byte of name table address here for now
sta SCRATCHPAD+$05
ldy BridgeCollapseOffset ;get bridge collapse offset here
lday BridgeCollapseData,y ;load low byte of name table address and store here
sta SCRATCHPAD+$04
ldy VRAM_Buffer1_Offset ;increment vram buffer offset
iny
ldxn ++$0c ;set offset for tile data for sub to draw blank metatile
jsr RemBridge ;do sub here to remove bowser's bridge metatiles
ldx ObjectOffset ;get enemy offset
jsr MoveVOffset ;set new vram buffer offset
ldan ++Sfx_Blast ;load the fireworks/gunfire sound into the square 2 sfx
sta Square2SoundQueue ;queue while at the same time loading the brick
ldan ++Sfx_BrickShatter ;shatter sound into the noise sfx queue thus
sta NoiseSoundQueue ;producing the unique sound of the bridge collapsing
inci BridgeCollapseOffset ;increment bridge collapse offset
lda BridgeCollapseOffset
cmpn ++$0f ;if bridge collapse offset has not yet reached
bne NoBFall ;the end, go ahead and skip this part
jsr InitVStf ;initialize whatever vertical speed bowser has
ldan ++%01000000
stax Enemy_State,x ;set bowser's state to one of defeated states (d6 set)
ldan ++Sfx_BowserFall
sta Square2SoundQueue ;play bowser defeat sound
NoBFall: jmp BowserGfxHandler ;jump to code that draws bowser
;--------------------------------
PRandomRange:
.db $21, $41, $11, $31
RunBowser:
ldax Enemy_State,x ;if d5 in enemy state is not set
andn ++%00100000 ;then branch elsewhere to run bowser
beq BowserControl
ldax Enemy_Y_Position,x ;otherwise check vertical position
cmpn ++$e0 ;if above a certain point, branch to move defeated bowser
cmpcy
bcc MoveD_Bowser ;otherwise proceed to KillAllEnemies
KillAllEnemies:
ldxn ++$04 ;start with last enemy slot
KillLoop: jsr EraseEnemyObject ;branch to kill enemy objects
dex ;move onto next enemy slot
bpl KillLoop ;do this until all slots are emptied
sta EnemyFrenzyBuffer ;empty frenzy buffer
ldx ObjectOffset ;get enemy object offset and leave
rts
BowserControl:
ldan ++$00
sta EnemyFrenzyBuffer ;empty frenzy buffer
lda TimerControl ;if master timer control not set,
checka
beq ChkMouth ;skip jump and execute code here
jmp SkipToFB ;otherwise, jump over a bunch of code
ChkMouth: lda BowserBodyControls ;check bowser's mouth
checka
bpl FeetTmr ;if bit clear, go ahead with code here
jmp HammerChk ;otherwise skip a whole section starting here
FeetTmr: deci BowserFeetCounter ;decrement timer to control bowser's feet
bne ResetMDr ;if not expired, skip this part
ldan ++$20 ;otherwise, reset timer
sta BowserFeetCounter
lda BowserBodyControls ;and invert bit used
eorn ++%00000001 ;to control bowser's feet
sta BowserBodyControls
ResetMDr: lda FrameCounter ;check frame counter
andn ++%00001111 ;if not on every sixteenth frame, skip
bne B_FaceP ;ahead to continue code
ldan ++$02 ;otherwise reset moving/facing direction every
stax Enemy_MovingDir,x ;sixteen frames
B_FaceP: ldax EnemyFrameTimer,x ;if timer set here expired,
checka
beq GetPRCmp ;branch to next section
jsr PlayerEnemyDiff ;get horizontal difference between player and bowser,
bpl GetPRCmp ;and branch if bowser to the right of the player
ldan ++$01
stax Enemy_MovingDir,x ;set bowser to move and face to the right
ldan ++$02
sta BowserMovementSpeed ;set movement speed
ldan ++$20
stax EnemyFrameTimer,x ;set timer here
sta BowserFireBreathTimer ;set timer used for bowser's flame
ldax Enemy_X_Position,x
cmpn ++$c8 ;if bowser to the right past a certain point,
cmpcy
bcs HammerChk ;skip ahead to some other section
GetPRCmp: lda FrameCounter ;get frame counter
andn ++%00000011
bne HammerChk ;execute this code every fourth frame, otherwise branch
ldax Enemy_X_Position,x
cmpi BowserOrigXPos ;if bowser not at original horizontal position,
bne GetDToO ;branch to skip this part
ldax PseudoRandomBitReg,x
andn ++%00000011 ;get pseudorandom offset
tay
lday PRandomRange,y ;load value using pseudorandom offset
sta MaxRangeFromOrigin ;and store here
GetDToO: ldax Enemy_X_Position,x
clc ;add movement speed to bowser's horizontal
adci BowserMovementSpeed ;coordinate and save as new horizontal position
stax Enemy_X_Position,x
ldyx Enemy_MovingDir,x
cpyn ++$01 ;if bowser moving and facing to the right, skip ahead
beq HammerChk
ldyn ++$ff ;set default movement speed here (move left)
secsub ;get difference of current vs. original
sbci BowserOrigXPos ;horizontal position
bpl CompDToO ;if current position to the right of original, skip ahead
eorn ++$ff
clc ;get two's compliment
adcn ++$01
ldyn ++$01 ;set alternate movement speed here (move right)
CompDToO: cmpi MaxRangeFromOrigin ;compare difference with pseudorandom value
cmpcy
bcc HammerChk ;if difference LOW pseudorandom value, leave speed alone
sty BowserMovementSpeed ;otherwise change bowser's movement speed
HammerChk: ldax EnemyFrameTimer,x ;if timer set here not expired yet, skip ahead to
checka
bne MakeBJump ;some other section of code
jsr MoveEnemySlowVert ;otherwise start by moving bowser downwards
lda WorldNumber ;check world number
cmpn ++World6
cmpcy
bcc SetHmrTmr ;if world 1-5, skip this part (not time to throw hammers yet)
lda FrameCounter
andn ++%00000011 ;check to see if it's time to execute sub
bne SetHmrTmr ;if not, skip sub, otherwise
jsr SpawnHammerObj ;execute sub on every fourth frame to spawn misc object (hammer)
SetHmrTmr: ldax Enemy_Y_Position,x ;get current vertical position
cmpn ++$80 ;if still above a certain point
cmpcy
bcc ChkFireB ;then skip to world number check for flames
ldax PseudoRandomBitReg,x
andn ++%00000011 ;get pseudorandom offset
tay
lday PRandomRange,y ;get value using pseudorandom offset
stax EnemyFrameTimer,x ;set for timer here
SkipToFB: jmp ChkFireB ;jump to execute flames code
MakeBJump: cmpn ++$01 ;if timer not yet about to expire,
bne ChkFireB ;skip ahead to next part
decx Enemy_Y_Position,x ;otherwise decrement vertical coordinate
jsr InitVStf ;initialize movement amount
ldan ++$fe
stax Enemy_Y_Speed,x ;set vertical speed to move bowser upwards
ChkFireB: lda WorldNumber ;check world number here
cmpn ++World8 ;world 8?
beq SpawnFBr ;if so, execute this part here
cmpn ++World6 ;world 6-7?
cmpcy
bcs BowserGfxHandler ;if so, skip this part here
SpawnFBr: lda BowserFireBreathTimer ;check timer here
checka
bne BowserGfxHandler ;if not expired yet, skip all of this
ldan ++$20
sta BowserFireBreathTimer ;set timer here
lda BowserBodyControls
eorn ++%10000000 ;invert bowser's mouth bit to open
sta BowserBodyControls ;and close bowser's mouth
bmi ChkFireB ;if bowser's mouth open, loop back
jsr SetFlameTimer ;get timing for bowser's flame
ldy SecondaryHardMode
checky
beq SetFBTmr ;if secondary hard mode flag not set, skip this
secsub
sbcn ++$10 ;otherwise subtract from value in A
SetFBTmr: sta BowserFireBreathTimer ;set value as timer here
ldan ++BowserFlame ;put bowser's flame identifier
sta EnemyFrenzyBuffer ;in enemy frenzy buffer
;--------------------------------
BowserGfxHandler:
jsr ProcessBowserHalf ;do a sub here to process bowser's front
ldyn ++$10 ;load default value here to position bowser's rear
ldax Enemy_MovingDir,x ;check moving direction
lsr
bcc CopyFToR ;if moving left, use default
ldyn ++$f0 ;otherwise load alternate positioning value here
CopyFToR: tya ;move bowser's rear object position value to A
clc
adcx Enemy_X_Position,x ;add to bowser's front object horizontal coordinate
ldy DuplicateObj_Offset ;get bowser's rear object offset
stay Enemy_X_Position,y ;store A as bowser's rear horizontal coordinate
ldax Enemy_Y_Position,x
clc ;add eight pixels to bowser's front object
adcn ++$08 ;vertical coordinate and store as vertical coordinate
stay Enemy_Y_Position,y ;for bowser's rear
ldax Enemy_State,x
stay Enemy_State,y ;copy enemy state directly from front to rear
ldax Enemy_MovingDir,x
stay Enemy_MovingDir,y ;copy moving direction also
lda ObjectOffset ;save enemy object offset of front to stack
pha
ldx DuplicateObj_Offset ;put enemy object offset of rear as current
stx ObjectOffset
ldan ++Bowser ;set bowser's enemy identifier
stax Enemy_ID,x ;store in bowser's rear object
jsr ProcessBowserHalf ;do a sub here to process bowser's rear
pla
sta ObjectOffset ;get original enemy object offset
tax
ldan ++$00 ;nullify bowser's front/rear graphics flag
sta BowserGfxFlag
ExBGfxH: rts ;leave!
ProcessBowserHalf:
inci BowserGfxFlag ;increment bowser's graphics flag, then run subroutines
jsr RunRetainerObj ;to get offscreen bits, relative position and draw bowser (finally!)
ldax Enemy_State,x
checka
bne ExBGfxH ;if either enemy object not in normal state, branch to leave
ldan ++$0a
stax Enemy_BoundBoxCtrl,x ;set bounding box size control
jsr GetEnemyBoundBox ;get bounding box coordinates
jmp PlayerEnemyCollision ;do player-to-enemy collision detection
;-------------------------------------------------------------------------------------
;$00 - used to hold movement force and tile number
;$01 - used to hold sprite attribute data
FlameTimerData:
.db $bf, $40, $bf, $bf, $bf, $40, $40, $bf
SetFlameTimer:
ldy BowserFlameTimerCtrl ;load counter as offset
inci BowserFlameTimerCtrl ;increment
lda BowserFlameTimerCtrl ;mask out all but 3 LSB
andn ++%00000111 ;to keep in range of 0-7
sta BowserFlameTimerCtrl
lday FlameTimerData,y ;load value to be used then leave
ExFl: rts
ProcBowserFlame:
lda TimerControl ;if master timer control flag set,
checka
bne SetGfxF ;skip all of this
ldan ++$40 ;load default movement force
ldy SecondaryHardMode
checky
beq SFlmX ;if secondary hard mode flag not set, use default
ldan ++$60 ;otherwise load alternate movement force to go faster
SFlmX: sta SCRATCHPAD+$00 ;store value here
ldax Enemy_X_MoveForce,x
secsub ;subtract value from movement force (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
sbci SCRATCHPAD+$00
push af
stax Enemy_X_MoveForce,x ;save new value (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
ldax Enemy_X_Position,x
ld h,a
pop af
ld a,h
sbcn ++$01 ;subtract one from horizontal position to move
push af
stax Enemy_X_Position,x ;to the left
ldax Enemy_PageLoc,x
ld h,a
pop af
ld a,h
sbcn ++$00 ;subtract borrow from page location
stax Enemy_PageLoc,x
ldyx BowserFlamePRandomOfs,x ;get some value here and use as offset
ldax Enemy_Y_Position,x ;load vertical coordinate
cmpy FlameYPosData,y ;compare against coordinate data using $0417,x (???) as offset
beq SetGfxF ;if equal, branch and do not modify coordinate
clc
adcx Enemy_Y_MoveForce,x ;otherwise add value here to coordinate and store
stax Enemy_Y_Position,x ;as new vertical coordinate
SetGfxF: jsr RelativeEnemyPosition ;get new relative coordinates
ldax Enemy_State,x ;if bowser's flame not in normal state,
checka
bne ExFl ;branch to leave
ldan ++$51 ;otherwise, continue
sta SCRATCHPAD+$00 ;write first tile number
ldyn ++$02 ;load attributes without vertical flip by default
lda FrameCounter
andn ++%00000010 ;invert vertical flip bit every 2 frames
beq FlmeAt ;if d1 not set, write default value
ldyn ++$82 ;otherwise write value with vertical flip bit set
FlmeAt: sty SCRATCHPAD+$01 ;set bowser's flame sprite attributes here
ldyx Enemy_SprDataOffset,x ;get OAM data offset
ldxn ++$00
DrawFlameLoop:
lda Enemy_Rel_YPos ;get Y relative coordinate of current enemy object
stay Sprite_Y_Position,y ;write into Y coordinate of OAM data
lda SCRATCHPAD+$00
stay Sprite_Tilenumber,y ;write current tile number into OAM data
inci SCRATCHPAD+$00 ;increment tile number to draw more bowser's flame
lda SCRATCHPAD+$01
stay Sprite_Attributes,y ;write saved attributes into OAM data
lda Enemy_Rel_XPos
stay Sprite_X_Position,y ;write X relative coordinate of current enemy object
clc
adcn ++$08
sta Enemy_Rel_XPos ;then add eight to it and store
iny
iny
iny
iny ;increment Y four times to move onto the next OAM
inx ;move onto the next OAM, and branch if three
cpxn ++$03 ;have not yet been done
cmpcy
bcc DrawFlameLoop
ldx ObjectOffset ;reload original enemy offset
jsr GetEnemyOffscreenBits ;get offscreen information
ldyx Enemy_SprDataOffset,x ;get OAM data offset
lda Enemy_OffscreenBits ;get enemy object offscreen bits
lsr ;move d0 to carry and result to stack
pha
bcc M3FOfs ;branch if carry not set
ldan ++$f8 ;otherwise move sprite offscreen, this part likely
stay Sprite_Y_Position+12,y ;residual since flame is only made of three sprites
M3FOfs: pla ;get bits from stack
lsr ;move d1 to carry and move bits back to stack
pha
bcc M2FOfs ;branch if carry not set again
ldan ++$f8 ;otherwise move third sprite offscreen
stay Sprite_Y_Position+8,y
M2FOfs: pla ;get bits from stack again
lsr ;move d2 to carry and move bits back to stack again
pha
bcc M1FOfs ;branch if carry not set yet again
ldan ++$f8 ;otherwise move second sprite offscreen
stay Sprite_Y_Position+4,y
M1FOfs: pla ;get bits from stack one last time
lsr ;move d3 to carry
bcc ExFlmeD ;branch if carry not set one last time
ldan ++$f8
stay Sprite_Y_Position,y ;otherwise move first sprite offscreen
ExFlmeD: rts ;leave
;--------------------------------
RunFireworks:
decx ExplosionTimerCounter,x ;decrement explosion timing counter here
bne SetupExpl ;if not expired, skip this part
ldan ++$08
stax ExplosionTimerCounter,x ;reset counter
incx ExplosionGfxCounter,x ;increment explosion graphics counter
ldax ExplosionGfxCounter,x
cmpn ++$03 ;check explosion graphics counter
cmpcy
bcs FireworksSoundScore ;if at a certain point, branch to kill this object
SetupExpl: jsr RelativeEnemyPosition ;get relative coordinates of explosion
lda Enemy_Rel_YPos ;copy relative coordinates
sta Fireball_Rel_YPos ;from the enemy object to the fireball object
lda Enemy_Rel_XPos ;first vertical, then horizontal
sta Fireball_Rel_XPos
ldyx Enemy_SprDataOffset,x ;get OAM data offset
ldax ExplosionGfxCounter,x ;get explosion graphics counter
jsr DrawExplosion_Fireworks ;do a sub to draw the explosion then leave
rts
FireworksSoundScore:
ldan ++$00 ;disable enemy buffer flag
stax Enemy_Flag,x
ldan ++Sfx_Blast ;play fireworks/gunfire sound
sta Square2SoundQueue
ldan ++$05 ;set part of score modifier for 500 points
sta DigitModifier+4
jmp EndAreaPoints ;jump to award points accordingly then leave
;--------------------------------
StarFlagYPosAdder:
.db $00, $00, $08, $08
StarFlagXPosAdder:
.db $00, $08, $00, $08
StarFlagTileData:
.db $54, $55, $56, $57
RunStarFlagObj:
ldan ++$00 ;initialize enemy frenzy buffer
sta EnemyFrenzyBuffer
lda StarFlagTaskControl ;check star flag object task number here
cmpn ++$05 ;if greater than 5, branch to exit
cmpcy
bcs StarFlagExit
jsr JumpEngine ;otherwise jump to appropriate sub
.dw StarFlagExit
.dw GameTimerFireworks
.dw AwardGameTimerPoints
.dw RaiseFlagSetoffFWorks
.dw DelayToAreaEnd
GameTimerFireworks:
ldyn ++$05 ;set default state for star flag object
lda GameTimerDisplay+2 ;get game timer's last digit
cmpn ++$01
beq SetFWC ;if last digit of game timer set to 1, skip ahead
ldyn ++$03 ;otherwise load new value for state
cmpn ++$03
beq SetFWC ;if last digit of game timer set to 3, skip ahead
ldyn ++$00 ;otherwise load one more potential value for state
cmpn ++$06
beq SetFWC ;if last digit of game timer set to 6, skip ahead
ldan ++$ff ;otherwise set value for no fireworks
SetFWC: sta FireworksCounter ;set fireworks counter here
styx Enemy_State,x ;set whatever state we have in star flag object
IncrementSFTask1:
inci StarFlagTaskControl ;increment star flag object task number
StarFlagExit:
rts ;leave
AwardGameTimerPoints:
lda GameTimerDisplay ;check all game timer digits for any intervals left
orai GameTimerDisplay+1
orai GameTimerDisplay+2
beq IncrementSFTask1 ;if no time left on game timer at all, branch to next task
lda FrameCounter
andn ++%00000100 ;check frame counter for d2 set (skip ahead
beq NoTTick ;for four frames every four frames) branch if not set
ldan ++Sfx_TimerTick
sta Square2SoundQueue ;load timer tick sound
NoTTick: ldyn ++GameTimerDisplay-DisplayDigits+2;++$23 ;set offset here to subtract from game timer's last digit
ldan ++$ff ;set adder here to $ff, or -1, to subtract one
sta DigitModifier+5 ;from the last digit of the game timer
jsr DigitsMathRoutine ;subtract digit
ldan ++$05 ;set now to add 50 points
sta DigitModifier+5 ;per game timer interval subtracted
EndAreaPoints:
ldyn ++$0b ;load offset for mario's score by default
lda CurrentPlayer ;check player on the screen
checka
beq ELPGive ;if mario, do not change
ldyn ++$11 ;otherwise load offset for luigi's score
ELPGive: jsr DigitsMathRoutine ;award 50 points per game timer interval
lda CurrentPlayer ;get player on the screen (or 500 points per
asl ;fireworks explosion if branched here from there)
asl ;shift to high nybble
asl
asl
oran ++%00000100 ;add four to set nybble for game timer
jmp UpdateNumber ;jump to print the new score and game timer
RaiseFlagSetoffFWorks:
ldax Enemy_Y_Position,x ;check star flag's vertical position
cmpn ++$72 ;against preset value
cmpcy
bcc SetoffF ;if star flag higher vertically, branch to other code
decx Enemy_Y_Position,x ;otherwise, raise star flag by one pixel
jmp DrawStarFlag ;and skip this part here
SetoffF: lda FireworksCounter ;check fireworks counter
checka
beq DrawFlagSetTimer ;if no fireworks left to go off, skip this part
bmi DrawFlagSetTimer ;if no fireworks set to go off, skip this part
ldan ++Fireworks
sta EnemyFrenzyBuffer ;otherwise set fireworks object in frenzy queue
DrawStarFlag:
jsr RelativeEnemyPosition ;get relative coordinates of star flag
ldyx Enemy_SprDataOffset,x ;get OAM data offset
ldxn ++$03 ;do four sprites
DSFLoop: lda Enemy_Rel_YPos ;get relative vertical coordinate
clc
adcx StarFlagYPosAdder,x ;add Y coordinate adder data
stay Sprite_Y_Position,y ;store as Y coordinate
ldax StarFlagTileData,x ;get tile number
stay Sprite_Tilenumber,y ;store as tile number
ldan ++$22 ;set palette and background priority bits
stay Sprite_Attributes,y ;store as attributes
lda Enemy_Rel_XPos ;get relative horizontal coordinate
clc
adcx StarFlagXPosAdder,x ;add X coordinate adder data
stay Sprite_X_Position,y ;store as X coordinate
iny
iny ;increment OAM data offset four bytes
iny ;for next sprite
iny
dex ;move onto next sprite
bpl DSFLoop ;do this until all sprites are done
ldx ObjectOffset ;get enemy object offset and leave
rts
DrawFlagSetTimer:
jsr DrawStarFlag ;do sub to draw star flag
ldan ++$06
stax EnemyIntervalTimer,x ;set interval timer here
IncrementSFTask2:
inci StarFlagTaskControl ;move onto next task
rts
DelayToAreaEnd:
jsr DrawStarFlag ;do sub to draw star flag
ldax EnemyIntervalTimer,x ;if interval timer set in previous task
checka
bne StarFlagExit2 ;not yet expired, branch to leave
;ъюёЄ√ы№: ¤Єю ьхёЄю ьх°рхЄ яЁхфёърчєхьюёЄш яЁш MUSICONINT=1
if MUSICONINT
lda EventMusicBuffer ;if event music buffer empty,
checka
ret nz
;ld hl,(EventMusicQueue_noint) ;if event music buffer empty,
;jr $ ;hl=fdcb/fdd0/fe2b/ (fdc9/fdce/fe29/fde0)
;ld a,(WorldNumber) ;check world number
;cp ++World4;8
;jr nc,IncrementSFTask2 ;юс√ўэ√щ ъюёЄ√ы№ эх яюьюурхЄ эр 8-2... ¤ЄюЄ Єюцх!
ld hl,SoundEngine_noint
ld (soundenginepatch),hl ;тючтЁр∙рхь MUSICONINT=1
ld a,0xcd
ld (soundenginecall),a ;тючтЁр∙рхь MUSICONINT=1
jr IncrementSFTask2 ;branch to increment task
else
lda EventMusicBuffer ;if event music buffer empty,
checka
beq IncrementSFTask2 ;branch to increment task
endif
StarFlagExit2:
rts ;otherwise leave
;--------------------------------
;$00 - used to store horizontal difference between player and piranha plant
MovePiranhaPlant:
ldax Enemy_State,x ;check enemy state
checka
bne PutinPipe ;if set at all, branch to leave
ldax EnemyFrameTimer,x ;check enemy's timer here
checka
bne PutinPipe ;branch to end if not yet expired
ldax PiranhaPlant_MoveFlag,x ;check movement flag
checka
bne SetupToMovePPlant ;if moving, skip to part ahead
ldax PiranhaPlant_Y_Speed,x ;if currently rising, branch
checka
bmi ReversePlantSpeed ;to move enemy upwards out of pipe
jsr PlayerEnemyDiff ;get horizontal difference between player and
bpl ChkPlayerNearPipe ;piranha plant, and branch if enemy to right of player
lda SCRATCHPAD+$00 ;otherwise get saved horizontal difference
eorn ++$ff
clc ;and change to two's compliment
adcn ++$01
sta SCRATCHPAD+$00 ;save as new horizontal difference
ChkPlayerNearPipe:
lda SCRATCHPAD+$00 ;get saved horizontal difference
cmpn ++$21
cmpcy
bcc PutinPipe ;if player within a certain distance, branch to leave
ReversePlantSpeed:
ldax PiranhaPlant_Y_Speed,x ;get vertical speed
eorn ++$ff
clc ;change to two's compliment
adcn ++$01
stax PiranhaPlant_Y_Speed,x ;save as new vertical speed
incx PiranhaPlant_MoveFlag,x ;increment to set movement flag
SetupToMovePPlant:
ldax PiranhaPlantDownYPos,x ;get original vertical coordinate (lowest point)
ldyx PiranhaPlant_Y_Speed,x ;get vertical speed
checky
bpl RiseFallPiranhaPlant ;branch if moving downwards
ldax PiranhaPlantUpYPos,x ;otherwise get other vertical coordinate (highest point)
RiseFallPiranhaPlant:
sta SCRATCHPAD+$00 ;save vertical coordinate here
lda FrameCounter ;get frame counter
lsr
bcc PutinPipe ;branch to leave if d0 set (execute code every other frame)
lda TimerControl ;get master timer control
checka
bne PutinPipe ;branch to leave if set (likely not necessary)
ldax Enemy_Y_Position,x ;get current vertical coordinate
clc
adcx PiranhaPlant_Y_Speed,x ;add vertical speed to move up or down
stax Enemy_Y_Position,x ;save as new vertical coordinate
cmpi SCRATCHPAD+$00 ;compare against low or high coordinate
bne PutinPipe ;branch to leave if not yet reached
ldan ++$00
stax PiranhaPlant_MoveFlag,x ;otherwise clear movement flag
ldan ++$40
stax EnemyFrameTimer,x ;set timer to delay piranha plant movement
PutinPipe:
ldan ++%00100000 ;set background priority bit in sprite
stax Enemy_SprAttrib,x ;attributes to give illusion of being inside pipe
rts ;then leave
;-------------------------------------------------------------------------------------
;$07 - spinning speed
FirebarSpin:
sta SCRATCHPAD+$07 ;save spinning speed here
ldax FirebarSpinDirection,x ;check spinning direction
checka
bne SpinCounterClockwise ;if moving counter-clockwise, branch to other part
ldyn ++$18 ;possibly residual ldy
ldax FirebarSpinState_Low,x
clc ;add spinning speed to what would normally be
adci SCRATCHPAD+$07 ;the horizontal speed
push af
stax FirebarSpinState_Low,x
ldax FirebarSpinState_High,x ;add carry to what would normally be the vertical speed
ld h,a
pop af
ld a,h
adcn ++$00
rts
SpinCounterClockwise:
ldyn ++$08 ;possibly residual ldy
ldax FirebarSpinState_Low,x
secsub ;subtract spinning speed to what would normally be
sbci SCRATCHPAD+$07 ;the horizontal speed
push af
stax FirebarSpinState_Low,x
ldax FirebarSpinState_High,x ;add carry to what would normally be the vertical speed
ld h,a
pop af
ld a,h
sbcn ++$00
rts
;-------------------------------------------------------------------------------------
;$00 - used to hold collision flag, Y movement force + 5 or low byte of name table for rope
;$01 - used to hold high byte of name table for rope
;$02 - used to hold page location of rope
BalancePlatform:
ldax Enemy_Y_HighPos,x ;check high byte of vertical position
cmpn ++$03
bne DoBPl
jmp EraseEnemyObject ;if far below screen, kill the object
DoBPl: ldax Enemy_State,x ;get object's state (set to $ff or other platform offset)
checka
bpl CheckBalPlatform ;if doing other balance platform, branch to leave
rts
CheckBalPlatform:
tay ;save offset from state as Y
ldax PlatformCollisionFlag,x ;get collision flag of platform
sta SCRATCHPAD+$00 ;store here
ldax Enemy_MovingDir,x ;get moving direction
checka
beq ChkForFall
jmp PlatformFall ;if set, jump here
ChkForFall:
ldan ++$2d ;check if platform is above a certain point
cmpx Enemy_Y_Position,x
cmpcy
bcc ChkOtherForFall ;if not, branch elsewhere
cpyi SCRATCHPAD+$00 ;if collision flag is set to same value as
beq MakePlatformFall ;enemy state, branch to make platforms fall
clc
adcn ++$02 ;otherwise add 2 pixels to vertical position
stax Enemy_Y_Position,x ;of current platform and branch elsewhere
jmp StopPlatforms ;to make platforms stop
MakePlatformFall:
jmp InitPlatformFall ;make platforms fall
ChkOtherForFall:
cmpy Enemy_Y_Position,y ;check if other platform is above a certain point
cmpcy
bcc ChkToMoveBalPlat ;if not, branch elsewhere
cpxi SCRATCHPAD+$00 ;if collision flag is set to same value as
beq MakePlatformFall ;enemy state, branch to make platforms fall
clc
adcn ++$02 ;otherwise add 2 pixels to vertical position
stay Enemy_Y_Position,y ;of other platform and branch elsewhere
jmp StopPlatforms ;jump to stop movement and do not return
ChkToMoveBalPlat:
ldax Enemy_Y_Position,x ;save vertical position to stack
pha
ldax PlatformCollisionFlag,x ;get collision flag
checka
bpl ColFlg ;branch if collision
ldax Enemy_Y_MoveForce,x
clc ;add $05 to contents of moveforce, whatever they be
adcn ++$05
sta SCRATCHPAD+$00 ;store here
ldaxkeepcy Enemy_Y_Speed,x
adcn ++$00 ;add carry to vertical speed
bmi PlatDn ;branch if moving downwards
bne PlatUp ;branch elsewhere if moving upwards
lda SCRATCHPAD+$00
cmpn ++$0b ;check if there's still a little force left
cmpcy
bcc PlatSt ;if not enough, branch to stop movement
bcs PlatUp ;otherwise keep branch to move upwards
ColFlg: cmpi ObjectOffset ;if collision flag matches
beq PlatDn ;current enemy object offset, branch
PlatUp: jsr MovePlatformUp ;do a sub to move upwards
jmp DoOtherPlatform ;jump ahead to remaining code
PlatSt: jsr StopPlatforms ;do a sub to stop movement
jmp DoOtherPlatform ;jump ahead to remaining code
PlatDn: jsr MovePlatformDown ;do a sub to move downwards
DoOtherPlatform:
ldyx Enemy_State,x ;get offset of other platform
pla ;get old vertical coordinate from stack
secsub
sbcx Enemy_Y_Position,x ;get difference of old vs. new coordinate
clc
adcy Enemy_Y_Position,y ;add difference to vertical coordinate of other
stay Enemy_Y_Position,y ;platform to move it in the opposite direction
ldax PlatformCollisionFlag,x ;if no collision, skip this part here
checka
bmi DrawEraseRope
tax ;put offset which collision occurred here
jsr PositionPlayerOnVPlat ;and use it to position player accordingly
DrawEraseRope:
ldy ObjectOffset ;get enemy object offset
lday Enemy_Y_Speed,y ;check to see if current platform is
oray Enemy_Y_MoveForce,y ;moving at all
beq ExitRp ;if not, skip all of this and branch to leave
ldx VRAM_Buffer1_Offset ;get vram buffer offset
cpxn ++$20 ;if offset beyond a certain point, go ahead
cmpcy
bcs ExitRp ;and skip this, branch to leave
lday Enemy_Y_Speed,y
pha ;save two copies of vertical speed to stack
pha
jsr SetupPlatformRope ;do a sub to figure out where to put new bg tiles
lda SCRATCHPAD+$01 ;write name table address to vram buffer
stax VRAM_Buffer1,x ;first the high byte, then the low
lda SCRATCHPAD+$00
stax VRAM_Buffer1+1,x
ldan ++$02 ;set length for 2 bytes
stax VRAM_Buffer1+2,x
lday Enemy_Y_Speed,y ;if platform moving upwards, branch
checka
bmi EraseR1 ;to do something else
ldan ++xa2
stax VRAM_Buffer1+3,x ;otherwise put tile numbers for left
ldan ++xa3 ;and right sides of rope in vram buffer
stax VRAM_Buffer1+4,x
jmp OtherRope ;jump to skip this part
EraseR1: ldan ++$24 ;put blank tiles in vram buffer
stax VRAM_Buffer1+3,x ;to erase rope
stax VRAM_Buffer1+4,x
OtherRope:
lday Enemy_State,y ;get offset of other platform from state
tay ;use as Y here
pla ;pull second copy of vertical speed from stack
eorn ++$ff ;invert bits to reverse speed
jsr SetupPlatformRope ;do sub again to figure out where to put bg tiles
lda SCRATCHPAD+$01 ;write name table address to vram buffer
stax VRAM_Buffer1+5,x ;this time we're doing putting tiles for
lda SCRATCHPAD+$00 ;the other platform
stax VRAM_Buffer1+6,x
ldan ++$02
stax VRAM_Buffer1+7,x ;set length again for 2 bytes
pla ;pull first copy of vertical speed from stack
checka
bpl EraseR2 ;if moving upwards (note inversion earlier), skip this
ldan ++xa2
stax VRAM_Buffer1+8,x ;otherwise put tile numbers for left
ldan ++xa3 ;and right sides of rope in vram
stax VRAM_Buffer1+9,x ;transfer buffer
jmp EndRp ;jump to skip this part
EraseR2: ldan ++$24 ;put blank tiles in vram buffer
stax VRAM_Buffer1+8,x ;to erase rope
stax VRAM_Buffer1+9,x
EndRp: ldan ++$00 ;put null terminator at the end
stax VRAM_Buffer1+10,x
lda VRAM_Buffer1_Offset ;add ten bytes to the vram buffer offset
clc ;and store
adcn ++10
sta VRAM_Buffer1_Offset
ExitRp: ldx ObjectOffset ;get enemy object buffer offset and leave
rts
SetupPlatformRope:
pha ;save second/third copy to stack
lday Enemy_X_Position,y ;get horizontal coordinate
clc
adcn ++$08 ;add eight pixels
ldx SecondaryHardMode ;if secondary hard mode flag set,
checkx
bne GetLRp ;use coordinate as-is
clc
adcn ++$10 ;otherwise add sixteen more pixels
GetLRp: pha ;save modified horizontal coordinate to stack
ldaykeepcy Enemy_PageLoc,y
adcn ++$00 ;add carry to page location
sta SCRATCHPAD+$02 ;and save here
pla ;pull modified horizontal coordinate
andn ++%11110000 ;from the stack, mask out low nybble
lsr ;and shift three bits to the right
lsr
lsr
sta SCRATCHPAD+$00 ;store result here as part of name table low byte
ldxy Enemy_Y_Position,y ;get vertical coordinate
pla ;get second/third copy of vertical speed from stack
checka
bpl GetHRp ;skip this part if moving downwards or not at all
txa
clc
adcn ++$08 ;add eight to vertical coordinate and
tax ;save as X
GetHRp: txa ;move vertical coordinate to A
ldx VRAM_Buffer1_Offset ;get vram buffer offset
asl
rol ;rotate d7 to d0 and d6 into carry
pha ;save modified vertical coordinate to stack
rol ;rotate carry to d0, thus d7 and d6 are at 2 LSB
andn ++%00000011 ;mask out all bits but d7 and d6, then set
oran ++%00100000 ;d5 to get appropriate high byte of name table
sta SCRATCHPAD+$01 ;address, then store
lda SCRATCHPAD+$02 ;get saved page location from earlier
andn ++$01 ;mask out all but LSB
asl
asl ;shift twice to the left and save with the
orai SCRATCHPAD+$01 ;rest of the bits of the high byte, to get
sta SCRATCHPAD+$01 ;the proper name table and the right place on it
pla ;get modified vertical coordinate from stack
andn ++%11100000 ;mask out low nybble and LSB of high nybble
clc
adci SCRATCHPAD+$00 ;add to horizontal part saved here
sta SCRATCHPAD+$00 ;save as name table low byte
lday Enemy_Y_Position,y
cmpn ++$e8 ;if vertical position not below the
cmpcy
bcc ExPRp ;bottom of the screen, we're done, branch to leave
lda SCRATCHPAD+$00
andn ++%10111111 ;mask out d6 of low byte of name table address
sta SCRATCHPAD+$00
ExPRp: rts ;leave!
InitPlatformFall:
tya ;move offset of other platform from Y to X
tax
jsr GetEnemyOffscreenBits ;get offscreen bits
ldan ++$06
jsr SetupFloateyNumber ;award 1000 points to player
lda Player_Rel_XPos
stax FloateyNum_X_Pos,x ;put floatey number coordinates where player is
lda Player_Y_Position
stax FloateyNum_Y_Pos,x
ldan ++$01 ;set moving direction as flag for
stax Enemy_MovingDir,x ;falling platforms
StopPlatforms:
jsr InitVStf ;initialize vertical speed and low byte
stay Enemy_Y_Speed,y ;for both platforms and leave
stay Enemy_Y_MoveForce,y
rts
PlatformFall:
tya ;save offset for other platform to stack
pha
jsr MoveFallingPlatform ;make current platform fall
pla
tax ;pull offset from stack and save to X
jsr MoveFallingPlatform ;make other platform fall
ldx ObjectOffset
ldax PlatformCollisionFlag,x ;if player not standing on either platform,
checka
bmi ExPF ;skip this part
tax ;transfer collision flag offset as offset to X
jsr PositionPlayerOnVPlat ;and position player appropriately
ExPF: ldx ObjectOffset ;get enemy object buffer offset and leave
rts
;--------------------------------
YMovingPlatform:
ldax Enemy_Y_Speed,x ;if platform moving up or down, skip ahead to
orax Enemy_Y_MoveForce,x ;check on other position
bne ChkYCenterPos
stax Enemy_YMF_Dummy,x ;initialize dummy variable
ldax Enemy_Y_Position,x
cmpx YPlatformTopYPos,x ;if current vertical position =HIGH top position, branch
cmpcy
bcs ChkYCenterPos ;ahead of all this
lda FrameCounter
andn ++%00000111 ;check for every eighth frame
bne SkipIY
incx Enemy_Y_Position,x ;increase vertical position every eighth frame
SkipIY: jmp ChkYPCollision ;skip ahead to last part
ChkYCenterPos:
ldax Enemy_Y_Position,x ;if current vertical position LOW central position, branch
cmpx YPlatformCenterYPos,x ;to slow ascent/move downwards
cmpcy
bcc YMDown
jsr MovePlatformUp ;otherwise start slowing descent/moving upwards
jmp ChkYPCollision
YMDown: jsr MovePlatformDown ;start slowing ascent/moving downwards
ChkYPCollision:
ldax PlatformCollisionFlag,x ;if collision flag not set here, branch
checka
bmi ExYPl ;to leave
jsr PositionPlayerOnVPlat ;otherwise position player appropriately
ExYPl: rts ;leave
;--------------------------------
;$00 - used as adder to position player hotizontally
XMovingPlatform:
ldan ++$0e ;load preset maximum value for secondary counter
jsr XMoveCntr_Platform ;do a sub to increment counters for movement
jsr MoveWithXMCntrs ;do a sub to move platform accordingly, and return value
ldax PlatformCollisionFlag,x ;if no collision with player,
checka
bmi ExXMP ;branch ahead to leave
PositionPlayerOnHPlat:
lda Player_X_Position
clc ;add saved value from second subroutine to
adci SCRATCHPAD+$00 ;current player's position to position (яЁшсрты хь ёьх∙хэшх шуЁюър яю X юЄэюёшЄхы№эю яЁю°ыюую Ёрчр)
sta Player_X_Position ;player accordingly in horizontal position
lda Player_PageLoc ;get player's page location
ldy SCRATCHPAD+$00 ;check to see if saved value here is positive or negative
checky
bmi PPHSubt ;if negative, branch to subtract
adcn ++$00 ;otherwise add carry to page location
jmp SetPVar ;jump to skip subtraction
PPHSubt:
cmpcy
sbcn ++$00 ;subtract borrow from page location
SetPVar: sta Player_PageLoc ;save result to player's page location
sty Platform_X_Scroll ;put saved value from second sub here to be used later
jsr PositionPlayerOnVPlat ;position player vertically and appropriately
ExXMP: rts ;and we are done here
;--------------------------------
DropPlatform:
ldax PlatformCollisionFlag,x ;if no collision between platform and player
checka
bmi ExDPl ;occurred, just leave without moving anything
jsr MoveDropPlatform ;otherwise do a sub to move platform down very quickly
jsr PositionPlayerOnVPlat ;do a sub to position player appropriately
ExDPl: rts ;leave
;--------------------------------
;$00 - residual value from sub
RightPlatform:
jsr MoveEnemyHorizontally ;move platform with current horizontal speed, if any
sta SCRATCHPAD+$00 ;store saved value here (residual code)
ldax PlatformCollisionFlag,x ;check collision flag, if no collision between player
checka
bmi ExRPl ;and platform, branch ahead, leave speed unaltered
ldan ++$10
stax Enemy_X_Speed,x ;otherwise set new speed (gets moving if motionless)
jsr PositionPlayerOnHPlat ;use saved value from earlier sub to position player
ExRPl: rts ;then leave
;--------------------------------
MoveLargeLiftPlat:
jsr MoveLiftPlatforms ;execute common to all large and small lift platforms
jmp ChkYPCollision ;branch to position player correctly
MoveSmallPlatform:
jsr MoveLiftPlatforms ;execute common to all large and small lift platforms
jmp ChkSmallPlatCollision ;branch to position player correctly
MoveLiftPlatforms:
lda TimerControl ;if master timer control set, skip all of this
checka
bne ExLiftP ;and branch to leave
ldax Enemy_YMF_Dummy,x
clc ;add contents of movement amount to whatever's here
adcx Enemy_Y_MoveForce,x
push af
stax Enemy_YMF_Dummy,x
ldax Enemy_Y_Position,x ;add whatever vertical speed is set to current
ld h,a
pop af
ld a,h
adcx Enemy_Y_Speed,x ;vertical position plus carry to move up or down
stax Enemy_Y_Position,x ;and then leave
rts
ChkSmallPlatCollision:
ldax PlatformCollisionFlag,x ;get bounding box counter saved in collision flag
checka
beq ExLiftP ;if none found, leave player position alone
jsr PositionPlayerOnS_Plat ;use to position player correctly
ExLiftP: rts ;then leave
;-------------------------------------------------------------------------------------
;$00 - page location of extended left boundary
;$01 - extended left boundary position
;$02 - page location of extended right boundary
;$03 - extended right boundary position
OffscreenBoundsCheck:
ldax Enemy_ID,x ;check for cheep-cheep object
cmpn ++FlyingCheepCheep ;branch to leave if found
beq ExScrnBd
lda ScreenLeft_X_Pos ;get horizontal coordinate for left side of screen
ldyx Enemy_ID,x
cpyn ++HammerBro ;check for hammer bro object
beq LimitB
cpyn ++PiranhaPlant ;check for piranha plant object
bne ExtendLB ;these two will be erased sooner than others if too far left
LimitB:
cmpcy
adcn ++$38 ;add 56 pixels to coordinate if hammer bro or piranha plant
cmpcy
ExtendLB:
sbcn ++$48 ;subtract 72 pixels regardless of enemy object
sta SCRATCHPAD+$01 ;store result here
lda ScreenLeft_PageLoc
sbcn ++$00 ;subtract borrow from page location of left side
cmpcy
sta SCRATCHPAD+$00 ;store result here
lda ScreenRight_X_Pos ;add 72 pixels to the right side horizontal coordinate
adcn ++$48
sta SCRATCHPAD+$03 ;store result here
lda ScreenRight_PageLoc
adcn ++$00 ;then add the carry to the page location
sta SCRATCHPAD+$02 ;and store result here
ldax Enemy_X_Position,x ;compare horizontal coordinate of the enemy object
cmpi SCRATCHPAD+$01 ;to modified horizontal left edge coordinate to get carry
ldaxkeepcy Enemy_PageLoc,x
sbci SCRATCHPAD+$00 ;then subtract it from the page coordinate of the enemy object
bmi TooFar ;if enemy object is too far left, branch to erase it
ldax Enemy_X_Position,x ;compare horizontal coordinate of the enemy object
cmpi SCRATCHPAD+$03 ;to modified horizontal right edge coordinate to get carry
ldaxkeepcy Enemy_PageLoc,x
sbci SCRATCHPAD+$02 ;then subtract it from the page coordinate of the enemy object
bmi ExScrnBd ;if enemy object is on the screen, leave, do not erase enemy
ldax Enemy_State,x ;if at this point, enemy is offscreen to the right, so check
cmpn ++HammerBro ;if in state used by spiny's egg, do not erase
beq ExScrnBd
cpyn ++PiranhaPlant ;if piranha plant, do not erase
beq ExScrnBd
cpyn ++FlagpoleFlagObject ;if flagpole flag, do not erase
beq ExScrnBd
cpyn ++StarFlagObject ;if star flag, do not erase
beq ExScrnBd
cpyn ++JumpspringObject ;if jumpspring, do not erase
beq ExScrnBd ;erase all others too far to the right
TooFar: jsr EraseEnemyObject ;erase object if necessary
ExScrnBd: rts ;leave
;-------------------------------------------------------------------------------------
;some unused space
.db $ff, $ff, $ff
;-------------------------------------------------------------------------------------
;$01 - enemy buffer offset
FireballEnemyCollision:
ldax Fireball_State,x ;check to see if fireball state is set at all
checka
beq ExitFBallEnemy ;branch to leave if not
asl
bcs ExitFBallEnemy ;branch to leave also if d7 in state is set
lda FrameCounter
lsr ;get LSB of frame counter
bcs ExitFBallEnemy ;branch to leave if set (do routine every other frame)
txa
asl ;multiply fireball offset by four
asl
clc
adcn ++$1c ;then add $1c or 28 bytes to it
tay ;to use fireball's bounding box coordinates
ldxn ++$04
FireballEnemyCDLoop:
stx SCRATCHPAD+$01 ;store enemy object offset here
tya
pha ;push fireball offset to the stack
ldax Enemy_State,x
andn ++%00100000 ;check to see if d5 is set in enemy state
bne NoFToECol ;if so, skip to next enemy slot
ldax Enemy_Flag,x ;check to see if buffer flag is set
checka
beq NoFToECol ;if not, skip to next enemy slot
ldax Enemy_ID,x ;check enemy identifier
cmpn ++$24
cmpcy
bcc GoombaDie ;if LOW $24, branch to check further
cmpn ++$2b
cmpcy
bcc NoFToECol ;if in range $24-$2a, skip to next enemy slot
GoombaDie: cmpn ++Goomba ;check for goomba identifier
bne NotGoomba ;if not found, continue with code
ldax Enemy_State,x ;otherwise check for defeated state
cmpn ++$02 ;if stomped or otherwise defeated,
cmpcy
bcs NoFToECol ;skip to next enemy slot
NotGoomba: ldax EnemyOffscrBitsMasked,x ;if any masked offscreen bits set,
checka
bne NoFToECol ;skip to next enemy slot
txa
asl ;otherwise multiply enemy offset by four
asl
clc
adcn ++$04 ;add 4 bytes to it
tax ;to use enemy's bounding box coordinates
jsr SprObjectCollisionCore ;do fireball-to-enemy collision detection
ldx ObjectOffset ;return fireball's original offset
bcc NoFToECol ;if carry clear, no collision, thus do next enemy slot
ldan ++%10000000
stax Fireball_State,x ;set d7 in enemy state
ldx SCRATCHPAD+$01 ;get enemy offset
jsr HandleEnemyFBallCol ;jump to handle fireball to enemy collision
NoFToECol: pla ;pull fireball offset from stack
tay ;put it in Y
ldx SCRATCHPAD+$01 ;get enemy object offset
dex ;decrement it
bpl FireballEnemyCDLoop ;loop back until collision detection done on all enemies
ExitFBallEnemy:
ldx ObjectOffset ;get original fireball offset and leave
rts
BowserIdentities:
.db Goomba, GreenKoopa, BuzzyBeetle, Spiny, Lakitu, Bloober, HammerBro, Bowser
HandleEnemyFBallCol:
jsr RelativeEnemyPosition ;get relative coordinate of enemy
ldx SCRATCHPAD+$01 ;get current enemy object offset
ldax Enemy_Flag,x ;check buffer flag for d7 set
checka
bpl ChkBuzzyBeetle ;branch if not set to continue
andn ++%00001111 ;otherwise mask out high nybble and
tax ;use low nybble as enemy offset
ldax Enemy_ID,x
cmpn ++Bowser ;check enemy identifier for bowser
beq HurtBowser ;branch if found
ldx SCRATCHPAD+$01 ;otherwise retrieve current enemy offset
ChkBuzzyBeetle:
ldax Enemy_ID,x
cmpn ++BuzzyBeetle ;check for buzzy beetle
beq ExHCF ;branch if found to leave (buzzy beetles fireproof)
cmpn ++Bowser ;check for bowser one more time (necessary if d7 of flag was clear)
bne ChkOtherEnemies ;if not found, branch to check other enemies
HurtBowser:
deci BowserHitPoints ;decrement bowser's hit points
bne ExHCF ;if bowser still has hit points, branch to leave
jsr InitVStf ;otherwise do sub to init vertical speed and movement force
stax Enemy_X_Speed,x ;initialize horizontal speed
sta EnemyFrenzyBuffer ;init enemy frenzy buffer
ldan ++$fe
stax Enemy_Y_Speed,x ;set vertical speed to make defeated bowser jump a little
ldy WorldNumber ;use world number as offset
lday BowserIdentities,y ;get enemy identifier to replace bowser with
stax Enemy_ID,x ;set as new enemy identifier
ldan ++$20 ;set A to use starting value for state
cpyn ++$03 ;check to see if using offset of 3 or more
cmpcy
bcs SetDBSte ;branch if so
oran ++$03 ;otherwise add 3 to enemy state
SetDBSte: stax Enemy_State,x ;set defeated enemy state
ldan ++Sfx_BowserFall
sta Square2SoundQueue ;load bowser defeat sound
ldx SCRATCHPAD+$01 ;get enemy offset
ldan ++$09 ;award 5000 points to player for defeating bowser
checka
bne EnemySmackScore ;unconditional branch to award points
ChkOtherEnemies:
cmpn ++BulletBill_FrenzyVar
beq ExHCF ;branch to leave if bullet bill (frenzy variant)
cmpn ++Podoboo
beq ExHCF ;branch to leave if podoboo
cmpn ++$15
cmpcy
bcs ExHCF ;branch to leave if identifier =HIGH $15
ShellOrBlockDefeat:
ldax Enemy_ID,x ;check for piranha plant
cmpn ++PiranhaPlant
cmpcy
bne StnE ;branch if not found
ldaxkeepcy Enemy_Y_Position,x
adcn ++$18 ;add 24 pixels to enemy object's vertical position
stax Enemy_Y_Position,x
StnE: jsr ChkToStunEnemies ;do yet another sub
ldax Enemy_State,x
andn ++%00011111 ;mask out 2 MSB of enemy object's state
oran ++%00100000 ;set d5 to defeat enemy and save as new state
stax Enemy_State,x
ldan ++$02 ;award 200 points by default
ldyx Enemy_ID,x ;check for hammer bro
cpyn ++HammerBro
bne GoombaPoints ;branch if not found
ldan ++$06 ;award 1000 points for hammer bro
GoombaPoints:
cpyn ++Goomba ;check for goomba
bne EnemySmackScore ;branch if not found
ldan ++$01 ;award 100 points for goomba
EnemySmackScore:
jsr SetupFloateyNumber ;update necessary score variables
ldan ++Sfx_EnemySmack ;play smack enemy sound
sta Square1SoundQueue
ExHCF: rts ;and now let's leave
;-------------------------------------------------------------------------------------
PlayerHammerCollision:
lda FrameCounter ;get frame counter
lsr ;shift d0 into carry
bcc ExPHC ;branch to leave if d0 not set to execute every other frame
lda TimerControl ;if either master timer control
orai Misc_OffscreenBits ;or any offscreen bits for hammer are set,
bne ExPHC ;branch to leave
txa
asl ;multiply misc object offset by four
asl
clc
adcn ++$24 ;add 36 or $24 bytes to get proper offset
tay ;for misc object bounding box coordinates
jsr PlayerCollisionCore ;do player-to-hammer collision detection
ldx ObjectOffset ;get misc object offset
bcc ClHCol ;if no collision, then branch
ldax Misc_Collision_Flag,x ;otherwise read collision flag
checka
bne ExPHC ;if collision flag already set, branch to leave
ldan ++$01
stax Misc_Collision_Flag,x ;otherwise set collision flag now
ldax Misc_X_Speed,x
eorn ++$ff ;get two's compliment of
clc ;hammer's horizontal speed
adcn ++$01
stax Misc_X_Speed,x ;set to send hammer flying the opposite direction
lda StarInvincibleTimer ;if star mario invincibility timer set,
checka
bne ExPHC ;branch to leave
jmp InjurePlayer ;otherwise jump to hurt player, do not return
ClHCol: ldan ++$00 ;clear collision flag
stax Misc_Collision_Flag,x
ExPHC: rts
;-------------------------------------------------------------------------------------
HandlePowerUpCollision:
jsr EraseEnemyObject ;erase the power-up object
ldan ++$06
jsr SetupFloateyNumber ;award 1000 points to player by default
ldan ++Sfx_PowerUpGrab
sta Square2SoundQueue ;play the power-up sound
lda PowerUpType ;check power-up type
cmpn ++$02
cmpcy
bcc Shroom_Flower_PUp ;if mushroom or fire flower, branch
cmpn ++$03
beq SetFor1Up ;if 1-up mushroom, branch
ldan ++$23 ;otherwise set star mario invincibility
sta StarInvincibleTimer ;timer, and load the star mario music
ldan ++StarPowerMusic ;into the area music queue, then leave
sta AreaMusicQueue
rts
Shroom_Flower_PUp:
lda PlayerStatus ;if player status = small, branch
checka
beq UpToSuper
cmpn ++$01 ;if player status not super, leave
bne NoPUp
ldx ObjectOffset ;get enemy offset, not necessary
ldan ++$02 ;set player status to fiery
sta PlayerStatus
jsr GetPlayerColors ;run sub to change colors of player
ldx ObjectOffset ;get enemy offset again, and again not necessary
ldan ++$0c ;set value to be used by subroutine tree (fiery)
jmp UpToFiery ;jump to set values accordingly
SetFor1Up:
ldan ++$0b ;change 1000 points into 1-up instead
stax FloateyNum_Control,x ;and then leave
rts
UpToSuper:
ldan ++$01 ;set player status to super
sta PlayerStatus
ldan ++$09 ;set value to be used by subroutine tree (super)
UpToFiery:
ldyn ++$00 ;set value to be used as new player state
jsr SetPRout ;set values to stop certain things in motion
NoPUp: rts
;--------------------------------
ResidualXSpdData:
.db $18, $e8
KickedShellXSpdData:
.db $30, $d0
DemotedKoopaXSpdData:
.db $08, $f8
PlayerEnemyCollision:
lda FrameCounter ;check counter for d0 set
lsr
;ccf ;эх яюьюурхЄ эр 1-2 (яЁш Їшъёрї CY Єюцх эх яюьюурхЄ)
;scf ;схч ъюыышчшщ ё ьюэёЄЁрьш
bcs NoPUp ;if set, branch to leave
jsr CheckPlayerVertical ;if player object is completely offscreen or
bcs NoPECol ;if down past 224th pixel row, branch to leave
ldax EnemyOffscrBitsMasked,x ;if current enemy is offscreen by any amount,
checka
bne NoPECol ;go ahead and branch to leave
lda GameEngineSubroutine
cmpn ++$08 ;if not set to run player control routine
bne NoPECol ;on next frame, branch to leave
ldax Enemy_State,x
andn ++%00100000 ;if enemy state has d5 set, branch to leave
bne NoPECol
jsr GetEnemyBoundBoxOfs ;get bounding box offset for current enemy object
jsr PlayerCollisionCore ;do collision detection on player vs. enemy
ldx ObjectOffset ;get enemy object buffer offset
bcs CheckForPUpCollision ;if collision, branch past this part here
ldax Enemy_CollisionBits,x
andn ++%11111110 ;otherwise, clear d0 of current enemy object's
stax Enemy_CollisionBits,x ;collision bit
NoPECol: rts
CheckForPUpCollision:
ldyx Enemy_ID,x
cpyn ++PowerUpObject ;check for power-up object
bne EColl ;if not found, branch to next part
jmp HandlePowerUpCollision ;otherwise, unconditional jump backwards
EColl: lda StarInvincibleTimer ;if star mario invincibility timer expired,
checka
beq HandlePECollisions ;perform task here, otherwise kill enemy like
jmp ShellOrBlockDefeat ;hit with a shell, or from beneath
KickedShellPtsData:
.db $0a, $06, $04
HandlePECollisions:
ldax Enemy_CollisionBits,x ;check enemy collision bits for d0 set
andn ++%00000001 ;or for being offscreen at all
orax EnemyOffscrBitsMasked,x
bne ExPEC ;branch to leave if either is true
ldan ++$01
orax Enemy_CollisionBits,x ;otherwise set d0 now
stax Enemy_CollisionBits,x
cpyn ++Spiny ;branch if spiny
beq ChkForPlayerInjury
cpyn ++PiranhaPlant ;branch if piranha plant
beq InjurePlayer_PiranhaPlant
cpyn ++Podoboo ;branch if podoboo
beq InjurePlayer_PiranhaPlant
cpyn ++BulletBill_CannonVar ;branch if bullet bill
beq ChkForPlayerInjury
cpyn ++$15 ;branch if object => $15
cmpcy
bcs InjurePlayer
lda AreaType ;branch if water type level
checka
beq InjurePlayer
ldax Enemy_State,x ;branch if d7 of enemy state was set
asl
bcs ChkForPlayerInjury
ldax Enemy_State,x ;mask out all but 3 LSB of enemy state
andn ++%00000111
cmpn ++$02 ;branch if enemy is in normal or falling state
cmpcy
bcc ChkForPlayerInjury
ldax Enemy_ID,x ;branch to leave if goomba in defeated state
cmpn ++Goomba
beq ExPEC
ldan ++Sfx_EnemySmack ;play smack enemy sound
sta Square1SoundQueue
ldax Enemy_State,x ;set d7 in enemy state, thus become moving shell
oran ++%10000000
stax Enemy_State,x
jsr EnemyFacePlayer ;set moving direction and get offset
lday KickedShellXSpdData,y ;load and set horizontal speed data with offset
stax Enemy_X_Speed,x
ldan ++$03 ;add three to whatever the stomp counter contains
clc ;to give points for kicking the shell
adci StompChainCounter
ldyx EnemyIntervalTimer,x ;check shell enemy's timer
cpyn ++$03 ;if above a certain point, branch using the points
cmpcy
bcs KSPts ;data obtained from the stomp counter + 3
lday KickedShellPtsData,y ;otherwise, set points based on proximity to timer expiration
KSPts: jsr SetupFloateyNumber ;set values for floatey number now
ExPEC: rts ;leave!!!
ChkForPlayerInjury:
lda Player_Y_Speed ;check player's vertical speed
checka
bmi ChkInj ;perform procedure below if player moving upwards
;jr $ ;хёыш чфхё№ яющьрЄ№ ёЄюыъэютхэшх ё уЁшсюь эр 1-2 (yspeed=0!!!) ш яхЁхэряЁртшЄ№ тЁєўэє■ эр EnemyStomped (їюЄ яю шфхх ь√ эх фюыцэ√ хую фртшЄ№), Єю фры№°х єьшЁрхь эр ъръЄєёх
bne EnemyStomped ;or not at all, and branch elsewhere if moving downwards
ChkInj: ldax Enemy_ID,x ;branch if enemy object < $07
if GOODBULLET
cp BulletBill_CannonVar
ret z
endif
cmpn ++Bloober
cmpcy
bcc ChkETmrs
lda Player_Y_Position ;add 12 pixels to player's vertical position
clc
adcn ++$0c
cmpx Enemy_Y_Position,x ;compare modified player's position to enemy's position
cmpcy
bcc EnemyStomped ;branch if this player's position above (less than) enemy's
ChkETmrs: lda StompTimer ;check stomp timer
checka
bne EnemyStomped ;branch if set
lda InjuryTimer ;check to see if injured invincibility timer still
checka
bne ExInjColRoutines ;counting down, and branch elsewhere to leave if so
lda Player_Rel_XPos
cmpi Enemy_Rel_XPos ;if player's relative position to the left of enemy's
cmpcy
bcc TInjE ;relative position, branch here
jmp ChkEnemyFaceRight ;otherwise do a jump here
TInjE: ldax Enemy_MovingDir,x ;if enemy moving towards the left,
cmpn ++$01 ;branch, otherwise do a jump here
bne InjurePlayer ;to turn the enemy around
jmp LInj
InjurePlayer_PiranhaPlant:
if GOODPIRANHAPLANT
ret
endif
InjurePlayer:
lda InjuryTimer ;check again to see if injured invincibility timer is
checka
bne ExInjColRoutines ;at zero, and branch to leave if so
ForceInjury:
ldx PlayerStatus ;check player's status
checkx
beq KillPlayer ;branch if small
sta PlayerStatus ;otherwise set player's status to small
ldan ++$08
sta InjuryTimer ;set injured invincibility timer
asl
sta Square1SoundQueue ;play pipedown/injury sound
jsr GetPlayerColors ;change player's palette if necessary
ldan ++$0a ;set subroutine to run on next frame
SetKRout: ldyn ++$01 ;set new player state
SetPRout: sta GameEngineSubroutine ;load new value to run subroutine on next frame
sty Player_State ;store new player state
ldyn ++$ff
sty TimerControl ;set master timer control flag to halt timers
iny
sty ScrollAmount ;initialize scroll speed
ExInjColRoutines:
ldx ObjectOffset ;get enemy offset and leave
rts
KillPlayer:
stx Player_X_Speed ;halt player's horizontal movement by initializing speed
inx
stx EventMusicQueue ;set event music queue to death music
ldan ++$fc
sta Player_Y_Speed ;set new vertical speed
ldan ++$0b ;set subroutine to run on next frame
checka
bne SetKRout ;branch to set player's state and other things
StompedEnemyPtsData:
.db $02, $06, $05, $06
EnemyStomped:
ldax Enemy_ID,x ;check for spiny, branch to hurt player
cmpn ++Spiny ;if found
beq InjurePlayer
ldan ++Sfx_EnemyStomp ;otherwise play stomp/swim sound
sta Square1SoundQueue
ldax Enemy_ID,x
ldyn ++$00 ;initialize points data offset for stomped enemies
cmpn ++FlyingCheepCheep ;branch for cheep-cheep
beq EnemyStompedPts
cmpn ++BulletBill_FrenzyVar ;branch for either bullet bill object
beq EnemyStompedPts
cmpn ++BulletBill_CannonVar
beq EnemyStompedPts
cmpn ++Podoboo ;branch for podoboo (this branch is logically impossible
beq EnemyStompedPts ;for cpu to take due to earlier checking of podoboo)
iny ;increment points data offset
cmpn ++HammerBro ;branch for hammer bro
beq EnemyStompedPts
iny ;increment points data offset
cmpn ++Lakitu ;branch for lakitu
beq EnemyStompedPts
iny ;increment points data offset
cmpn ++Bloober ;branch if NOT bloober
bne ChkForDemoteKoopa
EnemyStompedPts:
lday StompedEnemyPtsData,y ;load points data using offset in Y
jsr SetupFloateyNumber ;run sub to set floatey number controls
ldax Enemy_MovingDir,x
pha ;save enemy movement direction to stack
jsr SetStun ;run sub to kill enemy
pla
stax Enemy_MovingDir,x ;return enemy movement direction from stack
ldan ++%00100000
stax Enemy_State,x ;set d5 in enemy state
jsr InitVStf ;nullify vertical speed, physics-related thing,
stax Enemy_X_Speed,x ;and horizontal speed
ldan ++$fd ;set player's vertical speed, to give bounce
sta Player_Y_Speed
rts
ChkForDemoteKoopa:
cmpn ++$09 ;branch elsewhere if enemy object < $09 ;эрўшэр ё юс·хъЄр #9 шфєЄ misc objects (ўЄю ¤Єю???)
cmpcy
bcc HandleStompedShellE
andn ++%00000001 ;demote koopa paratroopas to ordinary troopas
stax Enemy_ID,x
ldyn ++$00 ;return enemy to normal state
styx Enemy_State,x
ldan ++$03 ;award 400 points to the player
jsr SetupFloateyNumber
jsr InitVStf ;nullify physics-related thing and vertical speed
jsr EnemyFacePlayer ;turn enemy around if necessary
lday DemotedKoopaXSpdData,y
stax Enemy_X_Speed,x ;set appropriate moving speed based on direction
jmp SBnce ;then move onto something else
RevivalRateData:
.db $10, $0b
HandleStompedShellE:
ldan ++$04 ;set defeated state for enemy
stax Enemy_State,x
inci StompChainCounter ;increment the stomp counter
lda StompChainCounter ;add whatever is in the stomp counter
clc ;to whatever is in the stomp timer
adci StompTimer
jsr SetupFloateyNumber ;award points accordingly
inci StompTimer ;increment stomp timer of some sort
ldy PrimaryHardMode ;check primary hard mode flag
lday RevivalRateData,y ;load timer setting according to flag
stax EnemyIntervalTimer,x ;set as enemy timer to revive stomped enemy
SBnce: ldan ++$fc ;set player's vertical speed for bounce
sta Player_Y_Speed ;and then leave!!!
rts
ChkEnemyFaceRight:
ldax Enemy_MovingDir,x ;check to see if enemy is moving to the right
cmpn ++$01
bne LInj ;if not, branch
jmp InjurePlayer ;otherwise go back to hurt player
LInj: jsr EnemyTurnAround ;turn the enemy around, if necessary
jmp InjurePlayer ;go back to hurt player
EnemyFacePlayer:
ldyn ++$01 ;set to move right by default
jsr PlayerEnemyDiff ;get horizontal difference between player and enemy
bpl SFcRt ;if enemy is to the right of player, do not increment
iny ;otherwise, increment to set to move to the left
SFcRt: styx Enemy_MovingDir,x ;set moving direction here
dey ;then decrement to use as a proper offset
rts
SetupFloateyNumber:
stax FloateyNum_Control,x ;set number of points control for floatey numbers
ldan ++$30
stax FloateyNum_Timer,x ;set timer for floatey numbers
ldax Enemy_Y_Position,x
stax FloateyNum_Y_Pos,x ;set vertical coordinate
lda Enemy_Rel_XPos
stax FloateyNum_X_Pos,x ;set horizontal coordinate and leave
ExSFN: rts
;-------------------------------------------------------------------------------------
;$01 - used to hold enemy offset for second enemy
SetBitsMask:
.db %10000000, %01000000, %00100000, %00010000, %00001000, %00000100, %00000010
ClearBitsMask:
.db %01111111, %10111111, %11011111, %11101111, %11110111, %11111011, %11111101
EnemiesCollision:
;TODO єёъюЁшЄ№ ўхЁхч ix тьхёЄю c
if Z80OPT2
ld a,(FrameCounter) ;check counter for d0 set
rra
ret nc;bcc ExSFN ;if d0 not set, leave
ld a,(AreaType)
or a
ret z;beq ExSFN ;if water area type, leave
ld hl,Enemy_ID
add hl,bc
ld a,(hl)
cp ++$15 ;if enemy object => $15, branch to leave
jp nc,ExitECRoutine
cp ++Lakitu ;if lakitu, branch to leave
jp z,ExitECRoutine
cp ++PiranhaPlant ;if piranha plant, branch to leave
jp z,ExitECRoutine
ldax EnemyOffscrBitsMasked,x ;if masked offscreen bits nonzero, branch to leave
or a
jp nz,ExitECRoutine
call GetEnemyBoundBoxOfs ;otherwise, do sub, get appropriate bounding box offset for
dec c ;first enemy we're going to compare, then decrement for second
jp m,ExitECRoutine ;branch to leave if there are no other enemies
ECLoop: ;ld ly,c;stx SCRATCHPAD+$01 ;save enemy object buffer offset for second enemy here
;ld hy,e;tya ;save first enemy's bounding box offset to stack
;pha
ldax Enemy_Flag,x ;check enemy object enable flag
or a
jr z,ReadyNextEnemy_fast ;branch if flag not set
ldax Enemy_ID,x
cp ++$15 ;check for enemy object => $15
jr nc,ReadyNextEnemy_fast ;branch if true
cp ++Lakitu
jr z,ReadyNextEnemy_fast ;branch if enemy object is lakitu
cp ++PiranhaPlant
jr z,ReadyNextEnemy_fast ;branch if enemy object is piranha plant
ldax EnemyOffscrBitsMasked,x
or a
jr nz,ReadyNextEnemy_fast ;branch if masked offscreen bits set
ld ly,c
;ld hy,e ;эх яюьюурхЄ
ld a,c ;get second enemy object's bounding box offset
inc a
add a,a ;multiply by four, then add four
add a,a
ld c,a ;use as new contents of X
push ix
push iy
call SprObjectCollisionCore ;do collision detection using the two enemies here ;эх яюЁЄшЄ y
pop iy
pop ix
ld hy,e
ld hl,ObjectOffset
ld e,(hl);ld c,(hl) ;use first enemy offset for X
ld c,ly;ld e,ly;ldy SCRATCHPAD+$01 ;use second enemy offset for Y
jr nc,NoEnemyCollision ;if carry clear, no collision, branch ahead of this
ld hl,Enemy_State
add hl,de;bc
ld a,(hl) ;ldax Enemy_State,x
ld hl,Enemy_State
add hl,bc;de
or (hl) ;oray Enemy_State,y ;check both enemy states for d7 set
jp m,YesEC ;branch if at least one of them is set
ld ix,Enemy_CollisionBits
add ix,bc;de
ld a,(ix);lday Enemy_CollisionBits,y ;load first enemy's collision-related bits
ld hl,SetBitsMask
add hl,de;bc
and (hl);andx SetBitsMask,x ;check to see if bit connected to second enemy is
jr nz,ReadyNextEnemy ;already set, and move onto next enemy slot if set
ld a,(ix);lday Enemy_CollisionBits,y
or (hl);orax SetBitsMask,x ;if the bit is not set, set it now
ld (ix),a;stay Enemy_CollisionBits,y
YesEC: ;push iy ;эх яюьюурхЄ
;ly чрьхэ хЄ SCRATCHPAD+$01!!!
if Z80OPT2bug==0
ld a,c
ld c,e
ld e,a ;яюўхьє-Єю яюЁ фюъ тыш хЄ! шэрўх ўхЁхярїш эх ёЄрыъштр■Єё фЁєу юс фЁєур
endif
call ProcEnemyCollisions ;react according to the nature of collision ;яюЁЄшЄ x,y
;pop iy
jp ReadyNextEnemy ;move onto next enemy slot
NoEnemyCollision:
ld hl,ClearBitsMask
add hl,de;bc
ld a,(hl)
ld hl,Enemy_CollisionBits
add hl,bc;de
;lday Enemy_CollisionBits,y ;load first enemy's collision-related bits
and (hl) ;andx ClearBitsMask,x ;clear bit connected to second enemy
ld (hl),a ;stay Enemy_CollisionBits,y ;then move onto next enemy slot
ReadyNextEnemy:
;pla ;get first enemy's bounding box offset from the stack
ld e,hy ;tay ;use as Y again
ld c,ly;ldx SCRATCHPAD+$01 ;get and decrement second enemy's object buffer offset
ReadyNextEnemy_fast:
dec c
jp p,ECLoop ;loop until all enemy slots have been checked
ExitECRoutine:
ld hl,ObjectOffset
ld c,(hl) ;get enemy object buffer offset
ret ;leave
else ;~Z80
lda FrameCounter ;check counter for d0 set
lsr
bcc ExSFN ;if d0 not set, leave
lda AreaType
checka
beq ExSFN ;if water area type, leave
ldax Enemy_ID,x
cmpn ++$15 ;if enemy object => $15, branch to leave
cmpcy
bcs ExitECRoutine
cmpn ++Lakitu ;if lakitu, branch to leave
beq ExitECRoutine
cmpn ++PiranhaPlant ;if piranha plant, branch to leave
beq ExitECRoutine
ldax EnemyOffscrBitsMasked,x ;if masked offscreen bits nonzero, branch to leave
checka
bne ExitECRoutine
jsr GetEnemyBoundBoxOfs ;otherwise, do sub, get appropriate bounding box offset for
dex ;first enemy we're going to compare, then decrement for second
bmi ExitECRoutine ;branch to leave if there are no other enemies
ECLoop: stx SCRATCHPAD+$01 ;save enemy object buffer offset for second enemy here
tya ;save first enemy's bounding box offset to stack
pha
ldax Enemy_Flag,x ;check enemy object enable flag
checka
beq ReadyNextEnemy ;branch if flag not set
ldax Enemy_ID,x
cmpn ++$15 ;check for enemy object => $15
cmpcy
bcs ReadyNextEnemy ;branch if true
cmpn ++Lakitu
beq ReadyNextEnemy ;branch if enemy object is lakitu
cmpn ++PiranhaPlant
beq ReadyNextEnemy ;branch if enemy object is piranha plant
ldax EnemyOffscrBitsMasked,x
checka
bne ReadyNextEnemy ;branch if masked offscreen bits set
txa ;get second enemy object's bounding box offset
asl ;multiply by four, then add four
asl
clc
adcn ++$04
tax ;use as new contents of X
jsr SprObjectCollisionCore ;do collision detection using the two enemies here
ldx ObjectOffset ;use first enemy offset for X
ldy SCRATCHPAD+$01 ;use second enemy offset for Y
bcc NoEnemyCollision ;if carry clear, no collision, branch ahead of this
ldax Enemy_State,x
oray Enemy_State,y ;check both enemy states for d7 set
andn ++%10000000
bne YesEC ;branch if at least one of them is set
lday Enemy_CollisionBits,y ;load first enemy's collision-related bits
andx SetBitsMask,x ;check to see if bit connected to second enemy is
bne ReadyNextEnemy ;already set, and move onto next enemy slot if set
lday Enemy_CollisionBits,y
orax SetBitsMask,x ;if the bit is not set, set it now
stay Enemy_CollisionBits,y
YesEC: jsr ProcEnemyCollisions ;react according to the nature of collision
jmp ReadyNextEnemy ;move onto next enemy slot
NoEnemyCollision:
lday Enemy_CollisionBits,y ;load first enemy's collision-related bits
andx ClearBitsMask,x ;clear bit connected to second enemy
stay Enemy_CollisionBits,y ;then move onto next enemy slot
ReadyNextEnemy:
pla ;get first enemy's bounding box offset from the stack
tay ;use as Y again
ldx SCRATCHPAD+$01 ;get and decrement second enemy's object buffer offset
dex
bpl ECLoop ;loop until all enemy slots have been checked
ExitECRoutine:
ldx ObjectOffset ;get enemy object buffer offset
rts ;leave
endif
ProcEnemyCollisions:
;ly чрьхэ хЄ SCRATCHPAD+$01!!!
lday Enemy_State,y ;check both enemy states for d5 set
orax Enemy_State,x
andn ++%00100000 ;if d5 is set in either state, or both, branch
bne ExitProcessEColl ;to leave and do nothing else at this point
ldax Enemy_State,x
cmpn ++$06 ;if second enemy state < $06, branch elsewhere
cmpcy
bcc ProcSecondEnemyColl
ldax Enemy_ID,x ;check second enemy identifier for hammer bro
cmpn ++HammerBro ;if hammer bro found in alt state, branch to leave
beq ExitProcessEColl
lday Enemy_State,y ;check first enemy state for d7 set
asl
bcc ShellCollisions ;branch if d7 is clear
ldan ++$06
jsr SetupFloateyNumber ;award 1000 points for killing enemy
jsr ShellOrBlockDefeat ;then kill enemy, then load
if Z80OPT2
ld e,ly;ldy SCRATCHPAD+$01 ;original offset of second enemy
else
ldy SCRATCHPAD+$01 ;original offset of second enemy
endif
ShellCollisions:
tya ;move Y to X
tax
jsr ShellOrBlockDefeat ;kill second enemy
ldx ObjectOffset
ldax ShellChainCounter,x ;get chain counter for shell
clc
adcn ++$04 ;add four to get appropriate point offset
if Z80OPT2
ld c,ly;ldx SCRATCHPAD+$01
else
ldx SCRATCHPAD+$01
endif
jsr SetupFloateyNumber ;award appropriate number of points for second enemy
ldx ObjectOffset ;load original offset of first enemy
incx ShellChainCounter,x ;increment chain counter for additional enemies
ExitProcessEColl:
rts ;leave!!!
ProcSecondEnemyColl:
lday Enemy_State,y ;if first enemy state < $06, branch elsewhere
cmpn ++$06
cmpcy
bcc MoveEOfs
lday Enemy_ID,y ;check first enemy identifier for hammer bro
cmpn ++HammerBro ;if hammer bro found in alt state, branch to leave
beq ExitProcessEColl
jsr ShellOrBlockDefeat ;otherwise, kill first enemy
if Z80OPT2
ld e,ly;ldy SCRATCHPAD+$01
else
ldy SCRATCHPAD+$01
endif
lday ShellChainCounter,y ;get chain counter for shell
clc
adcn ++$04 ;add four to get appropriate point offset
ldx ObjectOffset
jsr SetupFloateyNumber ;award appropriate number of points for first enemy
if Z80OPT2
ld c,ly;ldx SCRATCHPAD+$01 ;load original offset of second enemy
else
ldx SCRATCHPAD+$01 ;load original offset of second enemy
endif
incx ShellChainCounter,x ;increment chain counter for additional enemies
rts ;leave!!!
MoveEOfs:
tya ;move Y ($01) to X
tax
jsr EnemyTurnAround ;do the sub here using value from $01
ldx ObjectOffset ;then do it again using value from $08
EnemyTurnAround:
ldax Enemy_ID,x ;check for specific enemies
cmpn ++PiranhaPlant
beq ExTA ;if piranha plant, leave
cmpn ++Lakitu
beq ExTA ;if lakitu, leave
cmpn ++HammerBro
beq ExTA ;if hammer bro, leave
cmpn ++Spiny
beq RXSpd ;if spiny, turn it around
cmpn ++GreenParatroopaJump
beq RXSpd ;if green paratroopa, turn it around
cmpn ++$07
cmpcy
bcs ExTA ;if any OTHER enemy object => $07, leave
RXSpd: ldax Enemy_X_Speed,x ;load horizontal speed
eorn ++$ff ;get two's compliment for horizontal speed
tay
iny
styx Enemy_X_Speed,x ;store as new horizontal speed
ldax Enemy_MovingDir,x
eorn ++%00000011 ;invert moving direction and store, then leave
stax Enemy_MovingDir,x ;thus effectively turning the enemy around
ExTA: rts ;leave!!!
;-------------------------------------------------------------------------------------
;$00 - vertical position of platform
LargePlatformCollision:
ldan ++$ff ;save value here
stax PlatformCollisionFlag,x
lda TimerControl ;check master timer control
checka
bne ExLPC ;if set, branch to leave
ldax Enemy_State,x ;if d7 set in object state,
checka
bmi ExLPC ;branch to leave
ldax Enemy_ID,x
cmpn ++$24 ;check enemy object identifier for
bne ChkForPlayerC_LargeP ;balance platform, branch if not found
ldax Enemy_State,x
tax ;set state as enemy offset here
jsr ChkForPlayerC_LargeP ;perform code with state offset, then original offset, in X
ChkForPlayerC_LargeP:
jsr CheckPlayerVertical ;figure out if player is below a certain point
bcs ExLPC ;or offscreen, branch to leave if true
txa
jsr GetEnemyBoundBoxOfsArg ;get bounding box offset in Y
ldax Enemy_Y_Position,x ;store vertical coordinate in
sta SCRATCHPAD+$00 ;temp variable for now
txa ;send offset we're on to the stack
pha
jsr PlayerCollisionCore ;do player-to-platform collision detection
plakeepcy ;retrieve offset from the stack
tax
bcc ExLPC ;if no collision, branch to leave
jsr ProcLPlatCollisions ;otherwise collision, perform sub
ExLPC: ldx ObjectOffset ;get enemy object buffer offset and leave
rts
;--------------------------------
;$00 - counter for bounding boxes
SmallPlatformCollision:
lda TimerControl ;if master timer control set,
checka
bne ExSPC ;branch to leave
stax PlatformCollisionFlag,x ;otherwise initialize collision flag
jsr CheckPlayerVertical ;do a sub to see if player is below a certain point
bcs ExSPC ;or entirely offscreen, and branch to leave if true
ldan ++$02
sta SCRATCHPAD+$00 ;load counter here for 2 bounding boxes
ChkSmallPlatLoop:
ldx ObjectOffset ;get enemy object offset
jsr GetEnemyBoundBoxOfs ;get bounding box offset in Y
andn ++%00000010 ;if d1 of offscreen lower nybble bits was set
bne ExSPC ;then branch to leave
lday BoundingBox_UL_YPos,y ;check top of platform's bounding box for being
cmpn ++$20 ;above a specific point
cmpcy
bcc MoveBoundBox ;if so, branch, don't do collision detection
jsr PlayerCollisionCore ;otherwise, perform player-to-platform collision detection
bcs ProcSPlatCollisions ;skip ahead if collision
MoveBoundBox:
lday BoundingBox_UL_YPos,y ;move bounding box vertical coordinates
clc ;128 pixels downwards
adcn ++$80
stay BoundingBox_UL_YPos,y
lday BoundingBox_DR_YPos,y
clc
adcn ++$80
stay BoundingBox_DR_YPos,y
deci SCRATCHPAD+$00 ;decrement counter we set earlier
bne ChkSmallPlatLoop ;loop back until both bounding boxes are checked
ExSPC: ldx ObjectOffset ;get enemy object buffer offset, then leave
rts
;--------------------------------
ProcSPlatCollisions:
ldx ObjectOffset ;return enemy object buffer offset to X, then continue
ProcLPlatCollisions:
lday BoundingBox_DR_YPos,y ;get difference by subtracting the top
secsub ;of the player's bounding box from the bottom
sbci BoundingBox_UL_YPos ;of the platform's bounding box
cmpn ++$04 ;if difference too large or negative,
cmpcy
bcs ChkForTopCollision ;branch, do not alter vertical speed of player
lda Player_Y_Speed ;check to see if player's vertical speed is moving down
checka
bpl ChkForTopCollision ;if so, don't mess with it
ldan ++$01 ;otherwise, set vertical
sta Player_Y_Speed ;speed of player to kill jump
ChkForTopCollision:
lda BoundingBox_DR_YPos ;get difference by subtracting the top
secsub ;of the platform's bounding box from the bottom
sbcy BoundingBox_UL_YPos,y ;of the player's bounding box
cmpn ++$06
cmpcy
bcs PlatformSideCollisions ;if difference not close enough, skip all of this
lda Player_Y_Speed
checka
bmi PlatformSideCollisions ;if player's vertical speed moving upwards, skip this
lda SCRATCHPAD+$00 ;get saved bounding box counter from earlier
ldyx Enemy_ID,x
cpyn ++$2b ;if either of the two small platform objects are found,
beq SetCollisionFlag ;regardless of which one, branch to use bounding box counter
cpyn ++$2c ;as contents of collision flag
beq SetCollisionFlag
txa ;otherwise use enemy object buffer offset
SetCollisionFlag: ;яюўхьє ё■фр эх яюярфрхь???
ldx ObjectOffset ;get enemy object buffer offset
stax PlatformCollisionFlag,x ;save either bounding box counter or enemy offset here
ldan ++$00
sta Player_State ;set player state to normal then leave
rts
PlatformSideCollisions:
ldan ++$01 ;set value here to indicate possible horizontal
sta SCRATCHPAD+$00 ;collision on left side of platform
lda BoundingBox_DR_XPos ;get difference by subtracting platform's left edge
secsub ;from player's right edge
sbcy BoundingBox_UL_XPos,y
cmpn ++$08 ;if difference close enough, skip all of this
cmpcy
bcc SideC
inci SCRATCHPAD+$00 ;otherwise increment value set here for right side collision
lday BoundingBox_DR_XPos,y ;get difference by subtracting player's left edge
clc ;from platform's right edge
sbci BoundingBox_UL_XPos
cmpn ++$09 ;if difference not close enough, skip subroutine
cmpcy
bcs NoSideC ;and instead branch to leave (no collision)
SideC: jsr ImpedePlayerMove ;deal with horizontal collision
NoSideC: ldx ObjectOffset ;return with enemy object buffer offset
rts
;-------------------------------------------------------------------------------------
PlayerPosSPlatData:
.db $80, $00
PositionPlayerOnS_Plat:
tay ;use bounding box counter saved in collision flag
ldax Enemy_Y_Position,x ;for offset
clc ;add positioning data using offset to the vertical
adcy PlayerPosSPlatData-1,y ;coordinate
jr PositionPlayerOnVPlat_go;.db $2c ;BIT instruction opcode
PositionPlayerOnVPlat:
ldax Enemy_Y_Position,x ;get vertical coordinate
PositionPlayerOnVPlat_go
ldy GameEngineSubroutine
cpyn ++$0b ;if certain routine being executed on this frame,
beq ExPlPos ;skip all of this
ldyx Enemy_Y_HighPos,x
cpyn ++$01 ;if vertical high byte offscreen, skip this
bne ExPlPos
secsub ;subtract 32 pixels from vertical coordinate
sbcn ++$20 ;for the player object's height
sta Player_Y_Position ;save as player's new vertical coordinate
tya
sbcn ++$00 ;subtract borrow and store as player's
sta Player_Y_HighPos ;new vertical high byte
ldan ++$00
sta Player_Y_Speed ;initialize vertical speed and low byte of force
sta Player_Y_MoveForce ;and then leave
ExPlPos: rts
;-------------------------------------------------------------------------------------
CheckPlayerVertical:
lda Player_OffscreenBits ;if player object is completely offscreen
cmpn ++$f0 ;vertically, leave this routine
cmpcy
bcs ExCPV
ldy Player_Y_HighPos ;if player high vertical byte is not
dey ;within the screen, leave this routine
bne ExCPV
lda Player_Y_Position ;if on the screen, check to see how far down
cmpn ++$d0 ;the player is vertically
cmpcy
ExCPV: rts
;-------------------------------------------------------------------------------------
GetEnemyBoundBoxOfs:
lda ObjectOffset ;get enemy object buffer offset
GetEnemyBoundBoxOfsArg:
asl ;multiply A by four, then add four
asl ;to skip player's bounding box
clc
adcn ++$04
tay ;send to Y
lda Enemy_OffscreenBits ;get offscreen bits for enemy object
andn ++%00001111 ;save low nybble
cmpn ++%00001111 ;check for all bits set
cmpcy
rts
;-------------------------------------------------------------------------------------
;$00-$01 - used to hold many values, essentially temp variables
;$04 - holds lower nybble of vertical coordinate from block buffer routine
;$eb - used to hold block buffer adder
PlayerBGUpperExtent:
.db $20, $10
PlayerBGCollision:
lda DisableCollisionDet ;if collision detection disabled flag set,
checka
bne ExPBGCol ;branch to leave
lda GameEngineSubroutine
cmpn ++$0b ;if running routine ++11 or $0b
beq ExPBGCol ;branch to leave
cmpn ++$04
cmpcy
bcc ExPBGCol ;if running routines $00-$03 branch to leave
ldan ++$01 ;load default player state for swimming
ldy SwimmingFlag ;if swimming flag set,
checky
bne SetPSte ;branch ahead to set default state
lda Player_State ;if player in normal state,
checka
beq SetFallS ;branch to set default state for falling
cmpn ++$03
bne ChkOnScr ;if in any other state besides climbing, skip to next part
SetFallS: ldan ++$02 ;load default player state for falling
SetPSte: sta Player_State ;set whatever player state is appropriate
ChkOnScr: lda Player_Y_HighPos
cmpn ++$01 ;check player's vertical high byte for still on the screen
bne ExPBGCol ;branch to leave if not
ldan ++$ff
sta Player_CollisionBits ;initialize player's collision flag
lda Player_Y_Position
cmpn ++$cf ;check player's vertical coordinate
cmpcy
bcc ChkCollSize ;if not too close to the bottom of screen, continue
ExPBGCol: rts ;otherwise leave
ChkCollSize:
ldyn ++$02 ;load default offset
lda CrouchingFlag
checka
bne GBBAdr ;if player crouching, skip ahead
lda PlayerSize
checka
bne GBBAdr ;if player small, skip ahead
dey ;otherwise decrement offset for big player not crouching
lda SwimmingFlag
checka
bne GBBAdr ;if swimming flag set, skip ahead
dey ;otherwise decrement offset
GBBAdr: lday BlockBufferAdderData,y ;get value using offset ;0,7,14
sta SCRATCHPAD+$eb ;store value here
tay ;put value into Y, as offset for block buffer routine
ldx PlayerSize ;get player's size as offset
lda CrouchingFlag
checka
beq HeadChk ;if player not crouching, branch ahead
inx ;otherwise increment size as offset
HeadChk: lda Player_Y_Position ;get player's vertical coordinate ;є эрё ёэрўрыр $b0???
cmpx PlayerBGUpperExtent,x ;compare with upper extent value based on offset ;$20 шыш $10
cmpcy
bcc DoFootCheck ;if player is too high, skip this part
jsr BlockBufferColli_Head ;do player-to-bg collision detection on top of
beq DoFootCheck ;player, and branch if nothing above player's head
jsr CheckForCoinMTiles ;check to see if player touched coin with their head
bcs AwardTouchedCoin ;if so, branch to some other part of code
ldy Player_Y_Speed ;check player's vertical speed
checky
bpl DoFootCheck ;if player not moving upwards, branch elsewhere
ldy SCRATCHPAD+$04 ;check lower nybble of vertical coordinate returned
cpyn ++$04 ;from collision detection routine
cmpcy
bcc DoFootCheck ;if low nybble < 4, branch
jsr CheckForSolidMTiles ;check to see what player's head bumped on
bcs SolidOrClimb ;if player collided with solid metatile, branch
ldy AreaType ;otherwise check area type
checky
beq NYSpd ;if water level, branch ahead
ldy BlockBounceTimer ;if block bounce timer not expired,
checky
bne NYSpd ;branch ahead, do not process collision
jsr PlayerHeadCollision ;otherwise do a sub to process collision
jmp DoFootCheck ;jump ahead to skip these other parts here
SolidOrClimb:
cmpn ++$26 ;if climbing metatile,
beq NYSpd ;branch ahead and do not play sound
ldan ++Sfx_Bump
sta Square1SoundQueue ;otherwise load bump sound
NYSpd: ldan ++$01 ;set player's vertical speed to nullify
sta Player_Y_Speed ;jump or swim
DoFootCheck:
ldy SCRATCHPAD+$eb ;get block buffer adder offset ;0,7,14?
lda Player_Y_Position
cmpn ++$cf ;check to see how low player is
cmpcy
bcs DoPlayerSideCheck ;if player is too far down on screen, skip all of this
jsr BlockBufferColli_Feet ;do player-to-bg collision detection on bottom left of player
jsr CheckForCoinMTiles ;check to see if player touched coin with their left foot
bcs AwardTouchedCoin ;if so, branch to some other part of code
pha ;save bottom left metatile to stack
jsr BlockBufferColli_Feet ;do player-to-bg collision detection on bottom right of player
sta SCRATCHPAD+$00 ;save bottom right metatile here
pla
sta SCRATCHPAD+$01 ;pull bottom left metatile and save here
checka
bne ChkFootMTile ;if anything here, skip this part
lda SCRATCHPAD+$00 ;otherwise check for anything in bottom right metatile
checka
beq DoPlayerSideCheck ;and skip ahead if not
jsr CheckForCoinMTiles ;check to see if player touched coin with their right foot
bcc ChkFootMTile ;if not, skip unconditional jump and continue code
AwardTouchedCoin:
jmp HandleCoinMetatile ;follow the code to erase coin and award to player 1 coin
ChkFootMTile:
jsr CheckForClimbMTiles ;check to see if player landed on climbable metatiles
bcs DoPlayerSideCheck ;if so, branch
ldy Player_Y_Speed ;check player's vertical speed
checky
bmi DoPlayerSideCheck ;if player moving upwards, branch
cmpn ++$c5
bne ContChk ;if player did not touch axe, skip ahead
jmp HandleAxeMetatile ;otherwise jump to set modes of operation
ContChk: jsr ChkInvisibleMTiles ;do sub to check for hidden coin or 1-up blocks
beq DoPlayerSideCheck ;if either found, branch
ldy JumpspringAnimCtrl ;if jumpspring animating right now,
checky
bne InitSteP ;branch ahead
ldy SCRATCHPAD+$04 ;check lower nybble of vertical coordinate returned
cpyn ++$05 ;from collision detection routine
cmpcy
bcc LandPlyr ;if lower nybble < 5, branch
lda Player_MovingDir
sta SCRATCHPAD+$00 ;use player's moving direction as temp variable
jmp ImpedePlayerMove ;jump to impede player's movement in that direction
LandPlyr: jsr ChkForLandJumpSpring ;do sub to check for jumpspring metatiles and deal with it
ldan ++$f0
andi Player_Y_Position ;mask out lower nybble of player's vertical position
sta Player_Y_Position ;and store as new vertical position to land player properly
jsr HandlePipeEntry ;do sub to process potential pipe entry
ldan ++$00
sta Player_Y_Speed ;initialize vertical speed and fractional
sta Player_Y_MoveForce ;movement force to stop player's vertical movement
sta StompChainCounter ;initialize enemy stomp counter
InitSteP: ldan ++$00
sta Player_State ;set player's state to normal
DoPlayerSideCheck:
ldy SCRATCHPAD+$eb ;get block buffer adder offset ;ъръюх шёїюфэюх ёюёЄю эшх???0,7,14?
iny
iny ;increment offset 2 bytes to use adders for side collisions ;ёэрўрыр яЁютхЁ хь ёяЁртр??? (р т ЄрсышЎрї ссюъёют т√уы фшЄ ъръ ыхтр ёЄюЁюэр???)
ldan ++$02 ;set value here to be used as counter
sta SCRATCHPAD+$00
SideCheckLoop:
iny ;move onto the next one ;эрўшэрхь ёю ёьх∙хэш 3 т ЄрсышЎрї ссюъёют, яюЄюь 4,5,6???
sty SCRATCHPAD+$eb ;store it
lda Player_Y_Position
cmpn ++$20 ;check player's vertical position
cmpcy
bcc BHalf ;if player is in status bar area, branch ahead to skip this part
cmpn ++$e4
cmpcy
bcs ExSCH ;branch to leave if player is too far down
jsr BlockBufferColli_Side ;do player-to-bg collision detection on one half of player
beq BHalf ;branch ahead if nothing found
cmpn ++$1c ;otherwise check for pipe metatiles
beq BHalf ;if collided with sideways pipe (top), branch ahead
cmpn ++$6b
beq BHalf ;if collided with water pipe (top), branch ahead
jsr CheckForClimbMTiles ;do sub to see if player bumped into anything climbable
bcc CheckSideMTiles ;if not, branch to alternate section of code
BHalf: ldy SCRATCHPAD+$eb ;load block adder offset
iny ;increment it ;???
lda Player_Y_Position ;get player's vertical position
cmpn ++$08
cmpcy
bcc ExSCH ;if too high, branch to leave
cmpn ++$d0
cmpcy
bcs ExSCH ;if too low, branch to leave
jsr BlockBufferColli_Side ;do player-to-bg collision detection on other half of player
bne CheckSideMTiles ;if something found, branch
deci SCRATCHPAD+$00 ;otherwise decrement counter ;єўшЄ√трхЄё т ImpedePlayerMove (ыхтр ёЄюЁюэр=1??? шыш эрюсюЁюЄ???)
bne SideCheckLoop ;run code until both sides of player are checked ;хёыш чръюььхэЄшЁютрЄ№, Єю эх яюьюурхЄ тющЄш т ЄЁєсє ёЁрчє, эю ЄхяхЁ№ яЁюїюфшЄ ъшЁяшўш тяЁртю эрёътюч№
ExSCH: rts ;leave
CheckSideMTiles:
jsr ChkInvisibleMTiles ;check for hidden or coin 1-up blocks
beq ExCSM ;branch to leave if either found
jsr CheckForClimbMTiles ;check for climbable metatiles
bcc ContSChk ;if not found, skip and continue with code
jmp HandleClimbing ;otherwise jump to handle climbing
ContSChk: jsr CheckForCoinMTiles ;check to see if player touched coin
bcs HandleCoinMetatile ;if so, execute code to erase coin and award to player 1 coin
jsr ChkJumpspringMetatiles ;check for jumpspring metatiles
bcc ChkPBtm ;if not found, branch ahead to continue cude
lda JumpspringAnimCtrl ;otherwise check jumpspring animation control
checka
bne ExCSM ;branch to leave if set
jmp StopPlayerMove ;otherwise jump to impede player's movement
ChkPBtm: ldy Player_State ;get player's state
;jr $ ;ёЄюыъэєышё№ ёяЁртр ё тхЁїюь ЄЁєс√, шфє∙хщ тяЁртю ($1c)
cpyn ++$00 ;check for player's state set to normal
bne StopPlayerMove ;if not, branch to impede player's movement ;эх яюьюурхЄ тющЄш т ЄЁєсє ёЁрчє ;ўхьє Ёртхэ (SCRATCHPAD+$00)? (ыхтр ёЄюЁюэр=1???)
ldy PlayerFacingDir ;get player's facing direction
dey
bne StopPlayerMove ;if facing left, branch to impede movement
cmpn ++$6c ;otherwise check for pipe metatiles
beq PipeDwnS ;if collided with sideways pipe (bottom), branch
cmpn ++$1f ;if collided with water pipe (bottom), continue
bne StopPlayerMove ;otherwise branch to impede player's movement
PipeDwnS: lda Player_SprAttrib ;check player's attributes
;jr $ ;ё■фр яюярфрхь эх ёЁрчє, ъръ Єюы№ъю тёЄрыш ёэшчє є ЄЁєс√, р яюёых эрцрЄш тяЁртю Єрь - ¤Єю яюЁЄшЄ фхьє
checka
bne PlyrPipe ;if already set, branch, do not play sound again
ldyn ++Sfx_PipeDown_Injury
sty Square1SoundQueue ;otherwise load pipedown/injury sound
PlyrPipe: oran ++%00100000
sta Player_SprAttrib ;set background priority bit in player attributes
lda Player_X_Position
andn ++%00001111 ;get lower nybble of player's horizontal coordinate
beq ChkGERtn ;if at zero, branch ahead to skip this part
ldyn ++$00 ;set default offset for timer setting data
lda ScreenLeft_PageLoc ;load page location for left side of screen
checka
beq SetCATmr ;if at page zero, use default offset
iny ;otherwise increment offset
SetCATmr: lday AreaChangeTimerData,y ;set timer for change of area as appropriate
sta ChangeAreaTimer
ChkGERtn: lda GameEngineSubroutine ;get number of game engine routine running
cmpn ++$07
beq ExCSM ;if running player entrance routine or
cmpn ++$08 ;player control routine, go ahead and branch to leave
bne ExCSM
ldan ++$02
sta GameEngineSubroutine ;otherwise set sideways pipe entry routine to run
rts ;and leave
;--------------------------------
;$02 - high nybble of vertical coordinate from block buffer
;$04 - low nybble of horizontal coordinate from block buffer
;$06-$07 - block buffer address
StopPlayerMove:
;хёыш яЁютхЁ хь ъюыышчш■ ёяЁртр, Єю (SCRATCHPAD+$00) != 1!!! шыш эрюсюЁюЄ?
jsr ImpedePlayerMove ;stop player's movement
ExCSM: rts ;leave
AreaChangeTimerData:
.db $a0, $34
HandleCoinMetatile:
;jr $
jsr ErACM ;do sub to erase coin metatile from block buffer
inci CoinTallyFor1Ups ;increment coin tally used for 1-up blocks
jmp GiveOneCoin ;update coin amount and tally on the screen
HandleAxeMetatile:
ldan ++$00
sta OperMode_Task ;reset secondary mode
ldan ++$02
sta OperMode ;set primary mode to autoctrl mode
ldan ++$18
sta Player_X_Speed ;set horizontal speed and continue to erase axe metatile
ErACM: ldy SCRATCHPAD+$02 ;load vertical high nybble offset for block buffer
ldan ++$00 ;load blank metatile
stayindirect (SCRATCHPAD+$06),y ;store to remove old contents from block buffer
jmp RemoveCoin_Axe ;update the screen accordingly
;--------------------------------
;$02 - high nybble of vertical coordinate from block buffer
;$04 - low nybble of horizontal coordinate from block buffer
;$06-$07 - block buffer address
ClimbXPosAdder:
.db $f9, $07
ClimbPLocAdder:
.db $ff, $00
FlagpoleYPosData:
.db $18, $22, $50, $68, $90
HandleClimbing:
ldy SCRATCHPAD+$04 ;check low nybble of horizontal coordinate returned from
cpyn ++$06 ;collision detection routine against certain values, this
cmpcy
bcc ExHC ;makes actual physical part of vine or flagpole thinner
cpyn ++$0a ;than 16 pixels
cmpcy
bcc ChkForFlagpole
ExHC: rts ;leave if too far left or too far right
ChkForFlagpole:
cmpn ++$24 ;check climbing metatiles
beq FlagpoleCollision ;branch if flagpole ball found
cmpn ++$25
bne VineCollision ;branch to alternate code if flagpole shaft not found
FlagpoleCollision:
lda GameEngineSubroutine
cmpn ++$05 ;check for end-of-level routine running
beq PutPlayerOnVine ;if running, branch to end of climbing code
ldan ++$01
sta PlayerFacingDir ;set player's facing direction to right
inci ScrollLock ;set scroll lock flag
lda GameEngineSubroutine
cmpn ++$04 ;check for flagpole slide routine running
beq RunFR ;if running, branch to end of flagpole code here
ldan ++BulletBill_CannonVar ;load identifier for bullet bills (cannon variant)
jsr KillEnemies ;get rid of them
ldan ++Silence
sta EventMusicQueue ;silence music
lsr
sta FlagpoleSoundQueue ;load flagpole sound into flagpole sound queue
ldxn ++$04 ;start at end of vertical coordinate data
lda Player_Y_Position
sta FlagpoleCollisionYPos ;store player's vertical coordinate here to be used later
ChkFlagpoleYPosLoop:
cmpx FlagpoleYPosData,x ;compare with current vertical coordinate data
cmpcy
bcs MtchF ;if player's => current, branch to use current offset
dex ;otherwise decrement offset to use
bne ChkFlagpoleYPosLoop ;do this until all data is checked (use last one if all checked)
MtchF: stx FlagpoleScore ;store offset here to be used later
RunFR: ldan ++$04
sta GameEngineSubroutine ;set value to run flagpole slide routine
jmp PutPlayerOnVine ;jump to end of climbing code
VineCollision:
cmpn ++$26 ;check for climbing metatile used on vines
bne PutPlayerOnVine
lda Player_Y_Position ;check player's vertical coordinate
cmpn ++$20 ;for being in status bar area
cmpcy
bcs PutPlayerOnVine ;branch if not that far up
ldan ++$01
sta GameEngineSubroutine ;otherwise set to run autoclimb routine next frame
PutPlayerOnVine:
ldan ++$03 ;set player state to climbing
sta Player_State
ldan ++$00 ;nullify player's horizontal speed
sta Player_X_Speed ;and fractional horizontal movement force
sta Player_X_MoveForce ; (ьырф°р ўрёЄ№ X-ъююЁфшэрЄ√)
lda Player_X_Position ;get player's horizontal coordinate
secsub
sbci ScreenLeft_X_Pos ;subtract from left side horizontal coordinate
cmpn ++$10
cmpcy
bcs SetVXPl ;if 16 or more pixels difference, do not alter facing direction
ldan ++$02
sta PlayerFacingDir ;otherwise force player to face left
SetVXPl: ldy PlayerFacingDir ;get current facing direction, use as offset
lda SCRATCHPAD+$06 ;get low byte of block buffer address
asl
asl ;move low nybble to high
asl
asl
clc
adcy ClimbXPosAdder-1,y ;add pixels depending on facing direction
sta Player_X_Position ;store as player's horizontal coordinate
lda SCRATCHPAD+$06 ;get low byte of block buffer address again
checka
bne ExPVne ;if not zero, branch
lda ScreenRight_PageLoc ;load page location of right side of screen
clc
adcy ClimbPLocAdder-1,y ;add depending on facing location
sta Player_PageLoc ;store as player's page location
ExPVne: rts ;finally, we're done!
;--------------------------------
ChkInvisibleMTiles:
cmpn ++$5f ;check for hidden coin block
beq ExCInvT ;branch to leave if found
cmpn ++$60 ;check for hidden 1-up block
ExCInvT:
cmpcy ;???
rts ;leave with zero flag set if either found
;--------------------------------
;$00-$01 - used to hold bottom right and bottom left metatiles (in that order)
;$00 - used as flag by ImpedePlayerMove to restrict specific movement
ChkForLandJumpSpring:
jsr ChkJumpspringMetatiles ;do sub to check if player landed on jumpspring
bcc ExCJSp ;if carry not set, jumpspring not found, therefore leave
ldan ++$70
sta VerticalForce ;otherwise set vertical movement force for player
ldan ++$f9
sta JumpspringForce ;set default jumpspring force
ldan ++$03
sta JumpspringTimer ;set jumpspring timer to be used later
lsr
sta JumpspringAnimCtrl ;set jumpspring animation control to start animating
ExCJSp: rts ;and leave
ChkJumpspringMetatiles:
cmpn ++$67 ;check for top jumpspring metatile
beq JSFnd ;branch to set carry if found
cmpn ++$68 ;check for bottom jumpspring metatile
clc ;clear carry flag
bne NoJSFnd ;branch to use cleared carry if not found
JSFnd: sec ;set carry if found
NoJSFnd: rts ;leave
HandlePipeEntry:
lda Up_Down_Buttons ;check saved controller bits from earlier
andn ++%00000100 ;for pressing down
beq ExPipeE ;if not pressing down, branch to leave
lda SCRATCHPAD+$00
cmpn ++$11 ;check right foot metatile for warp pipe right metatile
bne ExPipeE ;branch to leave if not found
lda SCRATCHPAD+$01
cmpn ++$10 ;check left foot metatile for warp pipe left metatile
bne ExPipeE ;branch to leave if not found
ldan ++$30
sta ChangeAreaTimer ;set timer for change of area
ldan ++$03
sta GameEngineSubroutine ;set to run vertical pipe entry routine on next frame
ldan ++Sfx_PipeDown_Injury
sta Square1SoundQueue ;load pipedown/injury sound
ldan ++%00100000
sta Player_SprAttrib ;set background priority bit in player's attributes
lda WarpZoneControl ;check warp zone control
checka
beq ExPipeE ;branch to leave if none found
andn ++%00000011 ;mask out all but 2 LSB
asl
asl ;multiply by four
tax ;save as offset to warp zone numbers (starts at left pipe)
lda Player_X_Position ;get player's horizontal position
cmpn ++$60
cmpcy
bcc GetWNum ;if player at left, not near middle, use offset and skip ahead
inx ;otherwise increment for middle pipe
cmpn ++$a0
cmpcy
bcc GetWNum ;if player at middle, but not too far right, use offset and skip
inx ;otherwise increment for last pipe
GetWNum: ldyx WarpZoneNumbers,x ;get warp zone numbers
dey ;decrement for use as world number
sty WorldNumber ;store as world number and offset
ldxy WorldAddrOffsets,y ;get offset to where this world's area offsets are
ldax AreaAddrOffsets,x ;get area offset based on world offset
sta AreaPointer ;store area offset here to be used to change areas
ldan ++Silence
sta EventMusicQueue ;silence music
ldan ++$00
sta EntrancePage ;initialize starting page number
sta AreaNumber ;initialize area number used for area address offset
sta LevelNumber ;initialize level number used for world display
sta AltEntranceControl ;initialize mode of entry
inci Hidden1UpFlag ;set flag for hidden 1-up blocks
inci FetchNewGameTimerFlag ;set flag to load new game timer
ExPipeE: rts ;leave!!!
ImpedePlayerMove:
;хёыш яЁютхЁ хь ъюыышчш■ ёяЁртр, Єю (SCRATCHPAD+$00) != 1!!! шыш эрюсюЁюЄ??? яюЁЄшЄ (SCRATCHPAD+$00)
;єёЄрэртыштрхЄ Player_CollisionBits, SideCollisionTimer, фтшурхЄ X ш XSpeed
ldan ++$00 ;initialize value here
ldy Player_X_Speed ;get player's horizontal speed
ldx SCRATCHPAD+$00 ;check value set earlier for
dex ;left side collision
;jr RImpd
bne RImpd ;if right side collision, skip this part ;хёыш jr RImpd, Єю т 53672 яЁюїюфшЄ т яЁртє■ ЄЁєсє яЁртшы№эю, эю тїюфшЄ тыхтю т ёЄхэ√ ш эх юёЄрэртыштрхЄё эр чрьъх!!!???
;яЁютхЁ ыш ёяЁртр???
inx ;return value to X
cpyn ++$00 ;if player moving to the left,
bmi ExIPM ;branch to invert bit and leave ;шуЁюъ ёЄюыъэєыё ёяЁртр, эю шф╕Є тыхтю - эшўхую эх фхырхь
ldan ++$ff ;otherwise load A with value to be used later (¤Єю ёфтшэхЄ шуЁюър тыхтю эр 1 яшъё)
jmp NXSpd ;and jump to affect movement
RImpd: ldxn ++$02 ;return $02 to X
cpyn ++$01 ;if player moving to the right,
bpl ExIPM ;branch to invert bit and leave
ldan ++$01 ;otherwise load A with value to be used here
NXSpd: ldyn ++$10
sty SideCollisionTimer ;set timer of some sort
ldyn ++$00
sty Player_X_Speed ;nullify player's horizontal speed
cmpn ++$00 ;if value set in A not set to $ff,
bpl PlatF ;branch ahead, do not decrement Y
dey ;otherwise decrement Y now
PlatF: sty SCRATCHPAD+$00 ;store Y as high bits of horizontal adder
clc
adci Player_X_Position ;add contents of A to player's horizontal
sta Player_X_Position ;position to move player left or right
lda Player_PageLoc
adci SCRATCHPAD+$00 ;add high bits and carry to
sta Player_PageLoc ;page location if necessary
ExIPM: txa ;invert contents of X
eorn ++$ff ;1->fe, 2->fd
andi Player_CollisionBits ;mask out bit that was set here
sta Player_CollisionBits ;store to clear bit
rts
;--------------------------------
SolidMTileUpperExt:
.db $10, $61, $88, $c4
CheckForSolidMTiles:
;a=metatile
;out: CY=???
jsr GetMTileAttrib ;find appropriate offset based on metatile's 2 MSB
cmpx SolidMTileUpperExt,x ;compare current metatile with solid metatiles
cmpcy
rts
ClimbMTileUpperExt:
.db $24, $6d, $8a, $c6
CheckForClimbMTiles:
;a=metatile
;out: CY=???
jsr GetMTileAttrib ;find appropriate offset based on metatile's 2 MSB
cmpx ClimbMTileUpperExt,x ;compare current metatile with climbable metatiles
cmpcy
rts
CheckForCoinMTiles:
;out: CY=1:touched coin
cmpn ++$c2 ;check for regular coin
beq CoinSd ;branch if found
cmpn ++$c3 ;check for underwater coin
beq CoinSd ;branch if found
clc ;otherwise clear carry and leave
rts
CoinSd: ldan ++Sfx_CoinGrab
sta Square2SoundQueue ;load coin grab sound and leave
scf
rts
GetMTileAttrib:
tay ;save metatile value into Y
andn ++%11000000 ;mask out all but 2 MSB
asl
rol ;shift and rotate d7-d6 to d1-d0
rol
tax ;use as offset for metatile data
tya ;get original metatile value back
ExEBG: rts ;leave
;-------------------------------------------------------------------------------------
;$06-$07 - address from block buffer routine
EnemyBGCStateData:
.db $01, $01, $02, $02, $02, $05
EnemyBGCXSpdData:
.db $10, $f0
EnemyToBGCollisionDet:
ldax Enemy_State,x ;check enemy state for d6 set
andn ++%00100000
bne ExEBG ;if set, branch to leave
jsr SubtEnemyYPos ;otherwise, do a subroutine here
bcc ExEBG ;if enemy vertical coord + 62 < 68, branch to leave
ldyx Enemy_ID,x
cpyn ++Spiny ;if enemy object is not spiny, branch elsewhere
bne DoIDCheckBGColl
ldax Enemy_Y_Position,x
cmpn ++$25 ;if enemy vertical coordinate < 36 branch to leave
cmpcy
bcc ExEBG
DoIDCheckBGColl:
cpyn ++GreenParatroopaJump ;check for some other enemy object
bne HBChk ;branch if not found
jmp EnemyJump ;otherwise jump elsewhere
HBChk: cpyn ++HammerBro ;check for hammer bro
bne CInvu ;branch if not found
jmp HammerBroBGColl ;otherwise jump elsewhere
CInvu: cpyn ++Spiny ;if enemy object is spiny, branch
beq YesIn
cpyn ++PowerUpObject ;if special power-up object, branch
beq YesIn
cpyn ++$07 ;if enemy object => $07, branch to leave
cmpcy
bcs ExEBGChk
YesIn: jsr ChkUnderEnemy ;if enemy object < $07, or = $12 or $2e, do this sub
bne HandleEToBGCollision ;if block underneath enemy, branch
NoEToBGCollision:
jmp ChkForRedKoopa ;otherwise skip and do something else
;--------------------------------
;$02 - vertical coordinate from block buffer routine
HandleEToBGCollision:
jsr ChkForNonSolids ;if something is underneath enemy, find out what
beq NoEToBGCollision ;if blank $26, coins, or hidden blocks, jump, enemy falls through
cmpn ++$23
bne LandEnemyProperly ;check for blank metatile $23 and branch if not found
ldy SCRATCHPAD+$02 ;get vertical coordinate used to find block
ldan ++$00 ;store default blank metatile in that spot so we won't
stayindirect (SCRATCHPAD+$06),y ;trigger this routine accidentally again
ldax Enemy_ID,x
cmpn ++$15 ;if enemy object =HIGH $15, branch ahead
cmpcy
bcs ChkToStunEnemies
cmpn ++Goomba ;if enemy object not goomba, branch ahead of this routine
bne GiveOEPoints
jsr KillEnemyAboveBlock ;if enemy object IS goomba, do this sub
GiveOEPoints:
ldan ++$01 ;award 100 points for hitting block beneath enemy
jsr SetupFloateyNumber
ChkToStunEnemies:
cmpn ++$09 ;perform many comparisons on enemy object identifier
cmpcy
bcc SetStun
cmpn ++$11 ;if the enemy object identifier is equal to the values
cmpcy
bcs SetStun ;$09, $0e, $0f or $10, it will be modified, and not
cmpn ++$0a ;modified if not any of those values, note that piranha plant will
cmpcy
bcc Demote ;always fail this test because A will still have vertical
cmpn ++PiranhaPlant ;coordinate from previous addition, also these comparisons
cmpcy
bcc SetStun ;are only necessary if branching from $d7a1
Demote: andn ++%00000001 ;erase all but LSB, essentially turning enemy object
stax Enemy_ID,x ;into green or red koopa troopa to demote them
SetStun: ldax Enemy_State,x ;load enemy state
andn ++%11110000 ;save high nybble
oran ++%00000010
stax Enemy_State,x ;set d1 of enemy state
decx Enemy_Y_Position,x
decx Enemy_Y_Position,x ;subtract two pixels from enemy's vertical position
ldax Enemy_ID,x
cmpn ++Bloober ;check for bloober object
beq SetWYSpd
ldan ++$fd ;set default vertical speed
ldy AreaType
checky
bne SetNotW ;if area type not water, set as speed, otherwise
SetWYSpd: ldan ++$ff ;change the vertical speed
SetNotW: stax Enemy_Y_Speed,x ;set vertical speed now
ldyn ++$01
jsr PlayerEnemyDiff ;get horizontal difference between player and enemy object
bpl ChkBBill ;branch if enemy is to the right of player
iny ;increment Y if not
ChkBBill: ldax Enemy_ID,x
cmpn ++BulletBill_CannonVar ;check for bullet bill (cannon variant)
beq NoCDirF
cmpn ++BulletBill_FrenzyVar ;check for bullet bill (frenzy variant)
beq NoCDirF ;branch if either found, direction does not change
styx Enemy_MovingDir,x ;store as moving direction
NoCDirF: dey ;decrement and use as offset
lday EnemyBGCXSpdData,y ;get proper horizontal speed
stax Enemy_X_Speed,x ;and store, then leave
ExEBGChk: rts
;--------------------------------
;$04 - low nybble of vertical coordinate from block buffer routine
LandEnemyProperly:
lda SCRATCHPAD+$04 ;check lower nybble of vertical coordinate saved earlier
secsub
sbcn ++$08 ;subtract eight pixels
cmpn ++$05 ;used to determine whether enemy landed from falling
cmpcy
bcs ChkForRedKoopa ;branch if lower nybble in range of $0d-$0f before subtract
ldax Enemy_State,x
andn ++%01000000 ;branch if d6 in enemy state is set
bne LandEnemyInitState
ldax Enemy_State,x
asl ;branch if d7 in enemy state is not set
bcc ChkLandedEnemyState
SChkA: jmp DoEnemySideCheck ;if lower nybble LOW $0d, d7 set but d6 not set, jump here
ChkLandedEnemyState:
ldax Enemy_State,x ;if enemy in normal state, branch back to jump here
checka
beq SChkA
cmpn ++$05 ;if in state used by spiny's egg
beq ProcEnemyDirection ;then branch elsewhere
cmpn ++$03 ;if already in state used by koopas and buzzy beetles
cmpcy
bcs ExSteChk ;or in higher numbered state, branch to leave
ldax Enemy_State,x ;load enemy state again (why?)
cmpn ++$02 ;if not in $02 state (used by koopas and buzzy beetles)
bne ProcEnemyDirection ;then branch elsewhere
ldan ++$10 ;load default timer here
ldyx Enemy_ID,x ;check enemy identifier for spiny
cpyn ++Spiny
bne SetForStn ;branch if not found
ldan ++$00 ;set timer for $00 if spiny
SetForStn: stax EnemyIntervalTimer,x ;set timer here
ldan ++$03 ;set state here, apparently used to render
stax Enemy_State,x ;upside-down koopas and buzzy beetles
jsr EnemyLanding ;then land it properly
ExSteChk: rts ;then leave
ProcEnemyDirection:
ldax Enemy_ID,x ;check enemy identifier for goomba
cmpn ++Goomba ;branch if found
beq LandEnemyInitState
cmpn ++Spiny ;check for spiny
bne InvtD ;branch if not found
ldan ++$01
stax Enemy_MovingDir,x ;send enemy moving to the right by default
ldan ++$08
stax Enemy_X_Speed,x ;set horizontal speed accordingly
lda FrameCounter
andn ++%00000111 ;if timed appropriately, spiny will skip over
beq LandEnemyInitState ;trying to face the player
InvtD: ldyn ++$01 ;load 1 for enemy to face the left (inverted here)
jsr PlayerEnemyDiff ;get horizontal difference between player and enemy
bpl CNwCDir ;if enemy to the right of player, branch
iny ;if to the left, increment by one for enemy to face right (inverted)
CNwCDir: tya
cmpx Enemy_MovingDir,x ;compare direction in A with current direction in memory
bne LandEnemyInitState
jsr ChkForBump_HammerBroJ ;if equal, not facing in correct dir, do sub to turn around
LandEnemyInitState:
jsr EnemyLanding ;land enemy properly
ldax Enemy_State,x
andn ++%10000000 ;if d7 of enemy state is set, branch
bne NMovShellFallBit
ldan ++$00 ;otherwise initialize enemy state and leave
stax Enemy_State,x ;note this will also turn spiny's egg into spiny
rts
NMovShellFallBit:
ldax Enemy_State,x ;nullify d6 of enemy state, save other bits
andn ++%10111111 ;and store, then leave
stax Enemy_State,x
rts
;--------------------------------
ChkForRedKoopa:
ldax Enemy_ID,x ;check for red koopa troopa $03
cmpn ++RedKoopa
bne Chk2MSBSt ;branch if not found
ldax Enemy_State,x
checka
beq ChkForBump_HammerBroJ ;if enemy found and in normal state, branch
Chk2MSBSt: ldax Enemy_State,x ;save enemy state into Y
tay
asl ;check for d7 set
bcc GetSteFromD ;branch if not set
ldax Enemy_State,x
oran ++%01000000 ;set d6
jmp SetD6Ste ;jump ahead of this part
GetSteFromD: lday EnemyBGCStateData,y ;load new enemy state with old as offset
SetD6Ste: stax Enemy_State,x ;set as new state
;--------------------------------
;$00 - used to store bitmask (not used but initialized here)
;$eb - used in DoEnemySideCheck as counter and to compare moving directions
DoEnemySideCheck:
ldax Enemy_Y_Position,x ;if enemy within status bar, branch to leave
cmpn ++$20 ;because there's nothing there that impedes movement
cmpcy
bcc ExESdeC
ldyn ++$16 ;start by finding block to the left of enemy ($00,$14)
ldan ++$02 ;set value here in what is also used as
sta SCRATCHPAD+$eb ;OAM data offset
SdeCLoop: lda SCRATCHPAD+$eb ;check value
cmpx Enemy_MovingDir,x ;compare value against moving direction
bne NextSdeC ;branch if different and do not seek block there
ldan ++$01 ;set flag in A for save horizontal coordinate
jsr BlockBufferChk_Enemy ;find block to left or right of enemy object
beq NextSdeC ;if nothing found, branch
jsr ChkForNonSolids ;check for non-solid blocks
bne ChkForBump_HammerBroJ ;branch if not found
NextSdeC: deci SCRATCHPAD+$eb ;move to the next direction
iny
cpyn ++$18 ;increment Y, loop only if Y < $18, thus we check
cmpcy
bcc SdeCLoop ;enemy ($00, $14) and ($10, $14) pixel coordinates
ExESdeC: rts
ChkForBump_HammerBroJ:
cpxn ++$05 ;check if we're on the special use slot
beq NoBump ;and if so, branch ahead and do not play sound
ldax Enemy_State,x ;if enemy state d7 not set, branch
asl ;ahead and do not play sound
bcc NoBump
ldan ++Sfx_Bump ;otherwise, play bump sound
sta Square1SoundQueue ;sound will never be played if branching from ChkForRedKoopa
NoBump: ldax Enemy_ID,x ;check for hammer bro
cmpn ++$05
bne InvEnemyDir ;branch if not found
ldan ++$00
sta SCRATCHPAD+$00 ;initialize value here for bitmask
ldyn ++$fa ;load default vertical speed for jumping
jmp SetHJ ;jump to code that makes hammer bro jump
InvEnemyDir:
jmp RXSpd ;jump to turn the enemy around
;--------------------------------
;$00 - used to hold horizontal difference between player and enemy
PlayerEnemyDiff:
ldax Enemy_X_Position,x ;get distance between enemy object's
secsub ;horizontal coordinate and the player's
sbci Player_X_Position ;horizontal coordinate
sta SCRATCHPAD+$00 ;and store here
ldaxkeepcy Enemy_PageLoc,x
sbci Player_PageLoc ;subtract borrow, then leave
cmpcy ;шёяюы№чєхЄё юфшэ Ёрч
rts
;--------------------------------
EnemyLanding:
jsr InitVStf ;do something here to vertical speed and something else
ldax Enemy_Y_Position,x
andn ++%11110000 ;save high nybble of vertical coordinate, and
oran ++%00001000 ;set d3, then store, probably used to set enemy object
stax Enemy_Y_Position,x ;neatly on whatever it's landing on
rts
SubtEnemyYPos:
ldax Enemy_Y_Position,x ;add 62 pixels to enemy object's
clc ;vertical coordinate
adcn ++$3e
cmpn ++$44 ;compare against a certain range
cmpcy
rts ;and leave with flags set for conditional branch
EnemyJump:
jsr SubtEnemyYPos ;do a sub here
bcc DoSide ;if enemy vertical coord + 62 LOW 68, branch to leave
ldax Enemy_Y_Speed,x
clc ;add two to vertical speed
adcn ++$02
cmpn ++$03 ;if green paratroopa not falling, branch ahead
cmpcy
bcc DoSide
jsr ChkUnderEnemy ;otherwise, check to see if green paratroopa is
beq DoSide ;standing on anything, then branch to same place if not
jsr ChkForNonSolids ;check for non-solid blocks
beq DoSide ;branch if found
jsr EnemyLanding ;change vertical coordinate and speed
ldan ++$fd
stax Enemy_Y_Speed,x ;make the paratroopa jump again
DoSide: jmp DoEnemySideCheck ;check for horizontal blockage, then leave
;--------------------------------
HammerBroBGColl:
jsr ChkUnderEnemy ;check to see if hammer bro is standing on anything
beq NoUnderHammerBro
cmpn ++$23 ;check for blank metatile $23 and branch if not found
bne UnderHammerBro
KillEnemyAboveBlock:
jsr ShellOrBlockDefeat ;do this sub to kill enemy
ldan ++$fc ;alter vertical speed of enemy and leave
stax Enemy_Y_Speed,x
rts
UnderHammerBro:
ldax EnemyFrameTimer,x ;check timer used by hammer bro
checka
bne NoUnderHammerBro ;branch if not expired
ldax Enemy_State,x
andn ++%10001000 ;save d7 and d3 from enemy state, nullify other bits
stax Enemy_State,x ;and store
jsr EnemyLanding ;modify vertical coordinate, speed and something else
jmp DoEnemySideCheck ;then check for horizontal blockage and leave
NoUnderHammerBro:
ldax Enemy_State,x ;if hammer bro is not standing on anything, set d0
oran ++$01 ;in the enemy state to indicate jumping or falling, then leave
stax Enemy_State,x
rts
ChkUnderEnemy:
ldan ++$00 ;set flag in A for save vertical coordinate
ldyn ++$15 ;set Y to check the bottom middle (8,18) of enemy object
jmp BlockBufferChk_Enemy ;hop to it!
ChkForNonSolids:
cmpn ++$26 ;blank metatile used for vines?
beq NSFnd
cmpn ++$c2 ;regular coin?
beq NSFnd
cmpn ++$c3 ;underwater coin?
beq NSFnd
cmpn ++$5f ;hidden coin block?
beq NSFnd
cmpn ++$60 ;hidden 1-up block?
NSFnd:
cmpcy ;???
rts
;-------------------------------------------------------------------------------------
FireballBGCollision:
ldax Fireball_Y_Position,x ;check fireball's vertical coordinate
cmpn ++$18
cmpcy
bcc ClearBounceFlag ;if within the status bar area of the screen, branch ahead
jsr BlockBufferChk_FBall ;do fireball to background collision detection on bottom of it
beq ClearBounceFlag ;if nothing underneath fireball, branch
jsr ChkForNonSolids ;check for non-solid metatiles
beq ClearBounceFlag ;branch if any found
ldax Fireball_Y_Speed,x ;if fireball's vertical speed set to move upwards,
checka
bmi InitFireballExplode ;branch to set exploding bit in fireball's state
ldax FireballBouncingFlag,x ;if bouncing flag already set,
checka
bne InitFireballExplode ;branch to set exploding bit in fireball's state
ldan ++$fd
stax Fireball_Y_Speed,x ;otherwise set vertical speed to move upwards (give it bounce)
ldan ++$01
stax FireballBouncingFlag,x ;set bouncing flag
ldax Fireball_Y_Position,x
andn ++$f8 ;modify vertical coordinate to land it properly
stax Fireball_Y_Position,x ;store as new vertical coordinate
rts ;leave
ClearBounceFlag:
ldan ++$00
stax FireballBouncingFlag,x ;clear bouncing flag by default
rts ;leave
InitFireballExplode:
ldan ++$80
stax Fireball_State,x ;set exploding flag in fireball's state
ldan ++Sfx_Bump
sta Square1SoundQueue ;load bump sound
rts ;leave
;-------------------------------------------------------------------------------------
;$00 - used to hold one of bitmasks, or offset
;$01 - used for relative X coordinate, also used to store middle screen page location
;$02 - used for relative Y coordinate, also used to store middle screen coordinate
;this data added to relative coordinates of sprite objects
;stored in order: left edge, top edge, right edge, bottom edge
BoundBoxCtrlData:
.db $02, $08, $0e, $20
.db $03, $14, $0d, $20
.db $02, $14, $0e, $20
.db $02, $09, $0e, $15
.db $00, $00, $18, $06
.db $00, $00, $20, $0d
.db $00, $00, $30, $0d
.db $00, $00, $08, $08
.db $06, $04, $0a, $08
.db $03, $0e, $0d, $14
.db $00, $02, $10, $15
.db $04, $04, $0c, $1c
GetFireballBoundBox:
txa ;add seven bytes to offset
clc ;to use in routines as offset for fireball
adcn ++$07
tax
ldyn ++$02 ;set offset for relative coordinates
checky
bne FBallB ;unconditional branch
GetMiscBoundBox:
txa ;add nine bytes to offset
clc ;to use in routines as offset for misc object
adcn ++$09 ;эрўшэр ё юс·хъЄр #9 шфєЄ misc objects (ўЄю ¤Єю???)
tax
ldyn ++$06 ;set offset for relative coordinates
FBallB: jsr BoundingBoxCore ;get bounding box coordinates
jmp CheckRightScreenBBox ;jump to handle any offscreen coordinates
GetEnemyBoundBox:
ldyn ++$48 ;store bitmask here for now
sty SCRATCHPAD+$00
ldyn ++$44 ;store another bitmask here for now and jump
jmp GetMaskedOffScrBits
SmallPlatformBoundBox:
ldyn ++$08 ;store bitmask here for now
sty SCRATCHPAD+$00
ldyn ++$04 ;store another bitmask here for now
GetMaskedOffScrBits:
ldax Enemy_X_Position,x ;get enemy object position relative
secsub ;to the left side of the screen
sbci ScreenLeft_X_Pos
sta SCRATCHPAD+$01 ;store here
ldaxkeepcy Enemy_PageLoc,x ;subtract borrow from current page location
sbci ScreenLeft_PageLoc ;of left side
bmi CMBits ;if enemy object is beyond left edge, branch
orai SCRATCHPAD+$01 ;ёЄртшЄ Z???
beq CMBits ;if precisely at the left edge, branch
ldy SCRATCHPAD+$00 ;if to the right of left edge, use value in $00 for A
CMBits: tya ;otherwise use contents of Y
andi Enemy_OffscreenBits ;preserve bitwise whatever's in here
stax EnemyOffscrBitsMasked,x ;save masked offscreen bits here
checka
bne MoveBoundBoxOffscreen ;if anything set here, branch
jmp SetupEOffsetFBBox ;otherwise, do something else
LargePlatformBoundBox:
if Z80OPT
push bc
inc c ;increment X to get the proper offset
call GetXOffscreenBits ;then jump directly to the sub for horizontal offscreen bits
pop bc ;decrement to return to original offset
else
inx ;increment X to get the proper offset
jsr GetXOffscreenBits ;then jump directly to the sub for horizontal offscreen bits
dex ;decrement to return to original offset
endif
cmpn ++$fe ;if completely offscreen, branch to put entire bounding ;TODO >>4 т GetXOffscreenBits
cmpcy
bcs MoveBoundBoxOffscreen ;box offscreen, otherwise start getting coordinates
SetupEOffsetFBBox:
txa ;add 1 to offset to properly address
clc ;the enemy object memory locations
adcn ++$01
tax
ldyn ++$01 ;load 1 as offset here, same reason
jsr BoundingBoxCore ;do a sub to get the coordinates of the bounding box
jmp CheckRightScreenBBox ;jump to handle offscreen coordinates of bounding box
MoveBoundBoxOffscreen:
txa ;multiply offset by 4
asl
asl
tay ;use as offset here
ldan ++$ff
stay EnemyBoundingBoxCoord,y ;load value into four locations here and leave
stay EnemyBoundingBoxCoord+1,y
stay EnemyBoundingBoxCoord+2,y
stay EnemyBoundingBoxCoord+3,y
rts
BoundingBoxCore:
;x=obj?
;y=obj2?
;keep x
;out: y=x*4 (фы яюёыхфє■∙хую CheckRightScreenBBox, т юёЄры№э√ї ёыєўр ї эх эєцэю)
if Z80OPT ;???эхЄ ЁрчэшЎ√ т 1-2
ld iy,SprObject_Rel_YPos
add iy,de
ld hl,SprObj_BoundBoxCtrl
add hl,bc
ld a,(hl)
add a,a
add a,a ;*4
ld hl,BoundBoxCtrlData ;left edge, top edge, right edge, bottom edge (12 Єръшї ¤ыхьхэЄют)
add a,l
ld l,a
adc a,h
sub l
ld h,a
ld a,c ;txa ;multiply offset by four and save to stack
add a,a
add a,a ;*4
ld e,a ;use as offset
;push de
ld ix,BoundingBox_UL_Corner
add ix,de
ld a,(iy+SprObject_Rel_XPos-SprObject_Rel_YPos) ;object X coordinate relative to screen
add a,(hl) ;adcx BoundBoxCtrlData,x ;add the first number in the bounding box data to the
ld (ix+BoundingBox_UL_Corner-BoundingBox_UL_Corner),a ;stay BoundingBox_UL_Corner,y ;store here
sub (hl)
inc hl
inc hl
add a,(hl) ;adcx BoundBoxCtrlData+2,x ;add the third number in the bounding box data to the
ld (ix+BoundingBox_LR_Corner-BoundingBox_UL_Corner),a ;stay BoundingBox_LR_Corner,y ;relative horizontal coordinate and store
dec hl
ld a,(iy+SprObject_Rel_YPos-SprObject_Rel_YPos) ;object Y coordinate relative to screen
add a,(hl);adcx BoundBoxCtrlData,x ;add the second number to the relative vertical coordinate
ld (ix+BoundingBox_UL_Corner-BoundingBox_UL_Corner+1),a ;stay BoundingBox_UL_Corner,y
sub (hl)
inc hl
inc hl
add a,(hl) ;adcx BoundBoxCtrlData+2,x ;add the fourth number to the relative vertical coordinate
ld (ix+BoundingBox_LR_Corner-BoundingBox_UL_Corner+1),a ;stay BoundingBox_LR_Corner,y ;and store
;pop de
ret
CheckRightScreenBBox:
;y = x*4
;ix = BoundingBox_UL_Corner + (x*4)
ld iy,SprObject_X_Position
add iy,bc
lda ScreenLeft_X_Pos ;add 128 pixels to left side of screen
add a,0x80
ld h,a;sta SCRATCHPAD+$02 ;and store as horizontal coordinate of middle
lda ScreenLeft_PageLoc ;add carry to page location of left side of screen
adcn ++$00 ;and store as page location of middle
ld l,a;sta SCRATCHPAD+$01
ld a,(iy+SprObject_X_Position-SprObject_X_Position) ;ldax SprObject_X_Position,x ;get horizontal coordinate
sub h;cmpi SCRATCHPAD+$02 ;compare against middle horizontal coordinate
ld a,(iy+SprObject_PageLoc-SprObject_X_Position) ;ldaxkeepcy SprObject_PageLoc,x ;get page location
sbc a,l;sbci SCRATCHPAD+$01 ;subtract from middle page location
jr c,CheckLeftScreenBBox ;if object is on the left side of the screen, branch
ld a,(ix+BoundingBox_DR_XPos-BoundingBox_UL_Corner) ;lday BoundingBox_DR_XPos,y ;check right-side edge of bounding box for offscreen
or a
bmi NoOfs ;coordinates, branch if still on the screen
ld a,(ix+BoundingBox_UL_XPos-BoundingBox_UL_Corner) ;ldxy BoundingBox_UL_XPos,y ;check left-side edge of bounding box for offscreen
or a
ldan ++$ff ;load offscreen value here to use on one or both horizontal sides
bmi SORte ;coordinates, and branch if still on the screen
ld (ix+BoundingBox_UL_XPos-BoundingBox_UL_Corner),a ;stay BoundingBox_UL_XPos,y ;store offscreen value for left side
SORte: ld (ix+BoundingBox_DR_XPos-BoundingBox_UL_Corner),a ;stay BoundingBox_DR_XPos,y ;store offscreen value for right side
NoOfs: ldx ObjectOffset ;get object offset and leave
ret
CheckLeftScreenBBox:
ld a,(ix+BoundingBox_UL_XPos-BoundingBox_UL_Corner) ;lday BoundingBox_UL_XPos,y ;check left-side edge of bounding box for offscreen
or a
bpl NoOfs2 ;coordinates, and branch if still on the screen
cmpn ++$a0 ;check to see if left-side edge is in the middle of the
jr c,NoOfs2 ;screen or really offscreen, and branch if still on
ld a,(ix+BoundingBox_DR_XPos-BoundingBox_UL_Corner) ;ldxy BoundingBox_DR_XPos,y ;check right-side edge of bounding box for offscreen
or a
ldan ++$00
bpl SOLft ;coordinates, branch if still onscreen
ld (ix+BoundingBox_DR_XPos-BoundingBox_UL_Corner),a ;stay BoundingBox_DR_XPos,y ;store offscreen value for right side
SOLft: ld (ix+BoundingBox_UL_XPos-BoundingBox_UL_Corner),a ;stay BoundingBox_UL_XPos,y ;store offscreen value for left side
NoOfs2: ldx ObjectOffset ;get object offset and leave
rts
else ;~Z80
;ЄєЄ ыюьрыюё№ юЄюсЁрцхэшх ыхтющ эюуш уЁшср
;фхыю эх т яюЁўх SCRATCHPAD (яЁютхЁхэю ўхЁхч юЄфхы№э√щ сєЇхЁ)
;фхыю эх т CY эр т√їюфх (яЁютхЁхэю ўхЁхч plakeepcy)
;фхыю с√ыю т эхёючфрэшш ix фы Z80-тхЁёшш CheckRightScreenBBox
stx zSCRATCHPAD+$00 ;save offset here
lday SprObject_Rel_YPos,y ;store object coordinates relative to screen
sta zSCRATCHPAD+$02 ;vertically and horizontally, respectively
lday SprObject_Rel_XPos,y
sta zSCRATCHPAD+$01
txa ;multiply offset by four and save to stack
asl
asl
pha
tay ;use as offset for Y, X is left alone
ldax SprObj_BoundBoxCtrl,x ;load value here to be used as offset for X
asl ;multiply that by four and use as X
asl
tax
lda zSCRATCHPAD+$01 ;add the first number in the bounding box data to the
clc ;relative horizontal coordinate using enemy object offset
adcx BoundBoxCtrlData,x ;and store somewhere using same offset * 4
stay BoundingBox_UL_Corner,y ;store here
lda zSCRATCHPAD+$01
clc
adcx BoundBoxCtrlData+2,x ;add the third number in the bounding box data to the
stay BoundingBox_LR_Corner,y ;relative horizontal coordinate and store
inx ;increment both offsets
iny
lda zSCRATCHPAD+$02 ;add the second number to the relative vertical coordinate
clc ;using incremented offset and store using the other
adcx BoundBoxCtrlData,x ;incremented offset
stay BoundingBox_UL_Corner,y
lda zSCRATCHPAD+$02
clc
adcx BoundBoxCtrlData+2,x ;add the fourth number to the relative vertical coordinate
stay BoundingBox_LR_Corner,y ;and store
plakeepcy;pla ;get original offset loaded into $00 * y from stack
tay ;use as Y
ldx zSCRATCHPAD+$00 ;get original offset and use as X again
rts
zSCRATCHPAD=SCRATCHPAD
;ds 3
CheckRightScreenBBox:
;y = x*4
lda ScreenLeft_X_Pos ;add 128 pixels to left side of screen
clc ;and store as horizontal coordinate of middle
adcn ++$80
sta SCRATCHPAD+$02
lda ScreenLeft_PageLoc ;add carry to page location of left side of screen
adcn ++$00 ;and store as page location of middle
sta SCRATCHPAD+$01
ldax SprObject_X_Position,x ;get horizontal coordinate
cmpi SCRATCHPAD+$02 ;compare against middle horizontal coordinate
ldaxkeepcy SprObject_PageLoc,x ;get page location
sbci SCRATCHPAD+$01 ;subtract from middle page location
cmpcy
bcc CheckLeftScreenBBox ;if object is on the left side of the screen, branch
lday BoundingBox_DR_XPos,y ;check right-side edge of bounding box for offscreen
checka
bmi NoOfs ;coordinates, branch if still on the screen
ldan ++$ff ;load offscreen value here to use on one or both horizontal sides
ldxy BoundingBox_UL_XPos,y ;check left-side edge of bounding box for offscreen
checkx
bmi SORte ;coordinates, and branch if still on the screen
stay BoundingBox_UL_XPos,y ;store offscreen value for left side
SORte: stay BoundingBox_DR_XPos,y ;store offscreen value for right side
NoOfs: ldx ObjectOffset ;get object offset and leave
rts
CheckLeftScreenBBox:
lday BoundingBox_UL_XPos,y ;check left-side edge of bounding box for offscreen
checka
bpl NoOfs2 ;coordinates, and branch if still on the screen
cmpn ++$a0 ;check to see if left-side edge is in the middle of the
cmpcy
bcc NoOfs2 ;screen or really offscreen, and branch if still on
ldan ++$00
ldxy BoundingBox_DR_XPos,y ;check right-side edge of bounding box for offscreen
checkx
bpl SOLft ;coordinates, branch if still onscreen
stay BoundingBox_DR_XPos,y ;store offscreen value for right side
SOLft: stay BoundingBox_UL_XPos,y ;store offscreen value for left side
NoOfs2: ldx ObjectOffset ;get object offset and leave
rts
endif
;-------------------------------------------------------------------------------------
;$06 - second object's offset
;$07 - counter
;y=second object's offset
;эх яюЁЄшЄ y
;TODO ЁрёъЁ√Є№ Ўшъы
PlayerCollisionCore:
ldxn ++$00 ;initialize X to use player's bounding box for comparison
SprObjectCollisionCore:
if Z80OPT2a
push de;sty SCRATCHPAD+$06 ;save contents of Y here
ld iy,BoundingBox_UL_Corner
add iy,de
ld ix,BoundingBox_UL_Corner
add ix,bc
;ldan ++$01
ld d,1 ;sta SCRATCHPAD+$07 ;save value 1 here as counter, compare horizontal coordinates first
CollisionCoreLoop:
ld e,(ix)
ld c,(ix+BoundingBox_LR_Corner-BoundingBox_UL_Corner)
ld a,(iy);lday BoundingBox_UL_Corner,y ;compare left/top coordinates
cp e;(ix);icmpx BoundingBox_UL_Corner,x ;of first and second objects' bounding boxes
jr nc,FirstBoxGreater ;if first left/top => second, branch
cp c;(ix+BoundingBox_LR_Corner-BoundingBox_UL_Corner);cmpx BoundingBox_LR_Corner,x ;otherwise compare to right/bottom of second
jr c,SecondBoxVerticalChk ;if first left/top < second right/bottom, branch elsewhere
jr z,CollisionFound ;if somehow equal, collision, thus branch
ld c,a
ld a,(iy+BoundingBox_LR_Corner-BoundingBox_UL_Corner);lday BoundingBox_LR_Corner,y ;if somehow greater, check to see if bottom of
cp c;(iy);cmpy BoundingBox_UL_Corner,y ;first object's bounding box is greater than its top
jr c,CollisionFound ;if somehow less, vertical wrap collision, thus branch
cp e;(ix);cmpx BoundingBox_UL_Corner,x ;otherwise compare bottom of first bounding box to the top
jr nc,CollisionFound ;of second box, and if equal or greater, collision, thus branch
or a
pop de;ldy SCRATCHPAD+$06 ;otherwise return with carry clear and Y = $0006
ret ;note horizontal wrapping never occurs
SecondBoxVerticalChk:
ld a,(ix+BoundingBox_LR_Corner-BoundingBox_UL_Corner);ldax BoundingBox_LR_Corner,x ;check to see if the vertical bottom of the box
cp e;(ix);cmpx BoundingBox_UL_Corner,x ;is greater than the vertical top
jr c,CollisionFound ;if somehow less, vertical wrap collision, thus branch
ld a,(iy+BoundingBox_LR_Corner-BoundingBox_UL_Corner);lday BoundingBox_LR_Corner,y ;otherwise compare horizontal right or vertical bottom
cp e;(ix);cmpx BoundingBox_UL_Corner,x ;of first box with horizontal left or vertical top of second box
jr nc,CollisionFound ;if equal or greater, collision, thus branch
or a
pop de;ldy SCRATCHPAD+$06 ;otherwise return with carry clear and Y = $0006
ret
FirstBoxGreater:
cp e;(ix);cmpx BoundingBox_UL_Corner,x ;compare first and second box horizontal left/vertical top again
jr z,CollisionFound ;if first coordinate = second, collision, thus branch
cp c;(ix+BoundingBox_LR_Corner-BoundingBox_UL_Corner);cmpx BoundingBox_LR_Corner,x ;if not, compare with second object right or bottom edge
jr c,CollisionFound ;if left/top of first less than or equal to right/bottom of second
jr z,CollisionFound ;then collision, thus branch
cp (iy+BoundingBox_LR_Corner-BoundingBox_UL_Corner);cmpy BoundingBox_LR_Corner,y ;otherwise check to see if top of first box is greater than bottom
jr c,NoCollisionFound ;if less than or equal, no collision, branch to end
jr z,NoCollisionFound
ld a,(iy+BoundingBox_LR_Corner-BoundingBox_UL_Corner);lday BoundingBox_LR_Corner,y ;otherwise compare bottom of first to top of second
cp c;(ix+BoundingBox_LR_Corner-BoundingBox_UL_Corner);cmpx BoundingBox_UL_Corner,x ;if bottom of first is greater than top of second, vertical wrap
jr nc,CollisionFound ;collision, and branch, otherwise, proceed onwards here
NoCollisionFound:
or a ;clear carry, then load value set earlier, then leave
pop de;ldy SCRATCHPAD+$06 ;like previous ones, if horizontal coordinates do not collide, we do
ret ;not bother checking vertical ones, because what's the point?
CollisionFound:
inc ix ;inx ;increment offsets on both objects to check
inc iy ;iny ;the vertical coordinates
dec d ;deci SCRATCHPAD+$07 ;decrement counter to reflect this
jp p,CollisionCoreLoop ;if counter not expired, branch to loop
scf ;otherwise we already did both sets, therefore collision, so set carry
pop de;ldy SCRATCHPAD+$06 ;load original value set here earlier, then leave
ret
else ;~Z80
sty SCRATCHPAD+$06 ;save contents of Y here
ldan ++$01
sta SCRATCHPAD+$07 ;save value 1 here as counter, compare horizontal coordinates first
CollisionCoreLoop:
lday BoundingBox_UL_Corner,y ;compare left/top coordinates
cmpx BoundingBox_UL_Corner,x ;of first and second objects' bounding boxes
cmpcy
bcs FirstBoxGreater ;if first left/top => second, branch
cmpx BoundingBox_LR_Corner,x ;otherwise compare to right/bottom of second
cmpcy
bcc SecondBoxVerticalChk ;if first left/top < second right/bottom, branch elsewhere
beq CollisionFound ;if somehow equal, collision, thus branch
lday BoundingBox_LR_Corner,y ;if somehow greater, check to see if bottom of
cmpy BoundingBox_UL_Corner,y ;first object's bounding box is greater than its top
cmpcy
bcc CollisionFound ;if somehow less, vertical wrap collision, thus branch
cmpx BoundingBox_UL_Corner,x ;otherwise compare bottom of first bounding box to the top
cmpcy
bcs CollisionFound ;of second box, and if equal or greater, collision, thus branch
ldy SCRATCHPAD+$06 ;otherwise return with carry clear and Y = $0006
rts ;note horizontal wrapping never occurs
SecondBoxVerticalChk:
ldax BoundingBox_LR_Corner,x ;check to see if the vertical bottom of the box
cmpx BoundingBox_UL_Corner,x ;is greater than the vertical top
cmpcy
bcc CollisionFound ;if somehow less, vertical wrap collision, thus branch
lday BoundingBox_LR_Corner,y ;otherwise compare horizontal right or vertical bottom
cmpx BoundingBox_UL_Corner,x ;of first box with horizontal left or vertical top of second box
cmpcy
bcs CollisionFound ;if equal or greater, collision, thus branch
ldy SCRATCHPAD+$06 ;otherwise return with carry clear and Y = $0006
rts
FirstBoxGreater:
cmpx BoundingBox_UL_Corner,x ;compare first and second box horizontal left/vertical top again
beq CollisionFound ;if first coordinate = second, collision, thus branch
cmpx BoundingBox_LR_Corner,x ;if not, compare with second object right or bottom edge
cmpcy
bcc CollisionFound ;if left/top of first less than or equal to right/bottom of second
beq CollisionFound ;then collision, thus branch
cmpy BoundingBox_LR_Corner,y ;otherwise check to see if top of first box is greater than bottom
cmpcy
bcc NoCollisionFound ;if less than or equal, no collision, branch to end
beq NoCollisionFound
lday BoundingBox_LR_Corner,y ;otherwise compare bottom of first to top of second
cmpx BoundingBox_UL_Corner,x ;if bottom of first is greater than top of second, vertical wrap
cmpcy
bcs CollisionFound ;collision, and branch, otherwise, proceed onwards here
NoCollisionFound:
clc ;clear carry, then load value set earlier, then leave
ldy SCRATCHPAD+$06 ;like previous ones, if horizontal coordinates do not collide, we do
rts ;not bother checking vertical ones, because what's the point?
CollisionFound:
inx ;increment offsets on both objects to check
iny ;the vertical coordinates
deci SCRATCHPAD+$07 ;decrement counter to reflect this
bpl CollisionCoreLoop ;if counter not expired, branch to loop
sec ;otherwise we already did both sets, therefore collision, so set carry
ldy SCRATCHPAD+$06 ;load original value set here earlier, then leave
rts
endif
;-------------------------------------------------------------------------------------
;$02 - modified y coordinate
;$03 - stores metatile involved in block buffer collisions
;$04 - comes in with offset to block buffer adder data, goes out with low nybble x/y coordinate
;$05 - modified x coordinate
;$06-$07 - block buffer address
BlockBufferChk_Enemy:
pha ;save contents of A to stack
txa
clc ;add 1 to X to run sub with enemy offset in mind
adcn ++$01
tax
pla ;pull A from stack and jump elsewhere
jmp BBChk_E
ResidualMiscObjectCode:
txa
clc ;supposedly used once to set offset for
adcn ++$0d ;miscellaneous objects
tax
ldyn ++$1b ;supposedly used once to set offset for block buffer data
jmp ResJmpM ;probably used in early stages to do misc to bg collision detection
BlockBufferChk_FBall:
ldyn ++$1a ;set offset for block buffer adder data
txa
clc
adcn ++$07 ;add seven bytes to use
tax
ResJmpM: ldan ++$00 ;set A to return vertical coordinate
BBChk_E: jsr BlockBufferCollision ;do collision detection subroutine for sprite object
ldx ObjectOffset ;get object offset
cmpn ++$00 ;check to see if object bumped into anything
cmpcy ;???
rts
BlockBufferAdderData:
.db $00, $07, $0e
;юяшёрэшх срєэфшэу сюъёют фы ёЄюыъэютхэш ё ърЁЄющ???
BlockBuffer_X_Adder:
;яхЁшюф 7 (т ърцфющ уЁєяях яхЁт√х 3 ўшёыр шёяюы№чє■Єё Єюы№ъю фы ьюэёЄЁют, юёЄры№э√х 4 фы шуЁюър)
.db $08, $03, $0c, $02, $02, $0d, $0d
.db $08, $03, $0c, $02, $02, $0d, $0d
.db $08, $03, $0c, $02, $02, $0d, $0d
.db $08, $00, $10, $04, $14, $04, $04 ;ъръ ё■фр яюярёЄ№???
BlockBuffer_Y_Adder:
;яхЁшюф 7 (т ърцфющ уЁєяях яхЁт√х 3 ўшёыр шёяюы№чє■Єё Єюы№ъю фы ьюэёЄЁют, юёЄры№э√х 4 фы шуЁюър)
.db $04, $20, $20, $08, $18, $08, $18
.db $02, $20, $20, $08, $18, $08, $18
.db $12, $20, $20, $18, $18, $18, $18
.db $18, $14, $14, $06, $06, $08, $10 ;ъръ ё■фр яюярёЄ№???
BlockBufferColli_Feet:
iny ;if branched here, increment to next set of adders
BlockBufferColli_Head:
;out: NZ=collision
ldan ++$00 ;set flag to return vertical coordinate
jr BlockBufferColli_Side_go;.db $2c ;BIT instruction opcode
BlockBufferColli_Side:
ldan ++$01 ;set flag to return horizontal coordinate
BlockBufferColli_Side_go
ldxn ++$00 ;set offset for player object
BlockBufferCollision:
;x=obj
;y=??? (эхы№ч яюЁЄшЄ№!) эхъшщ шэфхъё фы BlockBuffer_X_Adder, BlockBuffer_Y_Adder
;a=1: return X to ($04)
;a=0: return Y to ($04)
;х∙╕ єёЄрэртыштрхЄ ($02) = (y+yadder)&0xf0 - 32
;х∙╕ єёЄрэртыштрхЄ ($06..07) = blockbuffer+x
if Z80OPT
ld hl,BlockBuffer_X_Adder
add hl,de
ld ix,SprObject_X_Position
add ix,bc
or a
jr nz,RetXC ;if A = 1, branch
ld a,(ix+SprObject_Y_Position-SprObject_X_Position) ;ldax SprObject_Y_Position,x ;if A = 0, load vertical coordinate
jp RetYC ;and jump
RetXC: ld a,(ix+SprObject_X_Position-SprObject_X_Position) ;ldax SprObject_X_Position,x ;otherwise load horizontal coordinate
RetYC: and ++%00001111 ;and mask out high nybble
ld (SCRATCHPAD+$04),a ;store masked out result here
ld a,(hl) ;lday BlockBuffer_X_Adder,y ;add horizontal coordinate
add a,(ix+SprObject_X_Position-SprObject_X_Position) ;adcx SprObject_X_Position,x ;of object to value obtained using Y as offset
ld l,a ;sta SCRATCHPAD+$05 ;store here ;є эрё яюър 3???
adc a,(ix+SprObject_PageLoc-SprObject_X_Position) ;adcn ++$00 ;add carry to page location
sub l
rra ;move to carry ;xHSB
ld a,l ;orai SCRATCHPAD+$05 ;get stored value ;xLSB
ld hl,Block_Buffer_1
jr nc,$+5
ld hl,Block_Buffer_2
rra
rra
rra
rra
and 0x0f
add a,l
ld l,a
;adc a,h
;sub l
;ld h,a
ld (SCRATCHPAD+$06),hl ;???
ld hl,BlockBuffer_Y_Adder
add hl,de
ld a,(ix+SprObject_Y_Position-SprObject_X_Position) ;ldax SprObject_Y_Position,x ;get vertical coordinate of object
add a,(hl);adcy BlockBuffer_Y_Adder,y ;add it to value obtained using Y as offset
and ++%11110000 ;mask out low nybble
sub 32 ;subtract 32 pixels for the status bar
ld (SCRATCHPAD+$02),a ;store result here ;???
;use as offset for block buffer
ld hl,(SCRATCHPAD+$06)
add a,l
ld l,a
adc a,h
sub l
ld h,a
;ld a,(hl) ;ldayindirect (SCRATCHPAD+$06),y ;check current content of block buffer
;sta SCRATCHPAD+$03 ;and store here
ld a,(hl) ;lda SCRATCHPAD+$03 ;get saved content of block buffer
;lda SCRATCHPAD+$03 ;get saved content of block buffer
or a
ret ;and leave
else ;~Z80
pha ;save contents of A to stack
sty SCRATCHPAD+$04 ;save contents of Y here
lday BlockBuffer_X_Adder,y ;add horizontal coordinate
clc ;of object to value obtained using Y as offset
adcx SprObject_X_Position,x
sta SCRATCHPAD+$05 ;store here ;є эрё яюър 3???
ldaxkeepcy SprObject_PageLoc,x
adcn ++$00 ;add carry to page location
andn ++$01 ;get LSB, mask out all other bits
lsr ;move to carry
push af
orai SCRATCHPAD+$05 ;get stored value
ld h,a
pop af
ld a,h
ror ;rotate carry to MSB of A
lsr ;and effectively move high nybble to
lsr ;lower, LSB which became MSB will be
lsr ;d4 at this point
jsr GetBlockBufferAddr ;get address of block buffer into $06, $07
ldy SCRATCHPAD+$04 ;get old contents of Y
ldax SprObject_Y_Position,x ;get vertical coordinate of object
clc
adcy BlockBuffer_Y_Adder,y ;add it to value obtained using Y as offset
andn ++%11110000 ;mask out low nybble
secsub
sbcn ++$20 ;subtract 32 pixels for the status bar
sta SCRATCHPAD+$02 ;store result here
tay ;use as offset for block buffer
ldayindirect (SCRATCHPAD+$06),y ;check current content of block buffer
sta SCRATCHPAD+$03 ;and store here
ldy SCRATCHPAD+$04 ;get old contents of Y again
pla ;pull A from stack
checka
bne RetXC ;if A = 1, branch
ldax SprObject_Y_Position,x ;if A = 0, load vertical coordinate
jmp RetYC ;and jump
RetXC: ldax SprObject_X_Position,x ;otherwise load horizontal coordinate
RetYC: andn ++%00001111 ;and mask out high nybble
sta SCRATCHPAD+$04 ;store masked out result here
lda SCRATCHPAD+$03 ;get saved content of block buffer
checka
rts ;and leave
endif
;-------------------------------------------------------------------------------------
if Z80==0
;unused byte
.db $ff
endif
;-------------------------------------------------------------------------------------
;$00 - offset to vine Y coordinate adder
;$02 - offset to sprite data
VineYPosAdder:
.db $00, $30
DrawVine:
sty SCRATCHPAD+$00 ;save offset here
lda Enemy_Rel_YPos ;get relative vertical coordinate
clc
adcy VineYPosAdder,y ;add value using offset in Y to get value
ldxy VineObjOffset,y ;get offset to vine
ldyx Enemy_SprDataOffset,x ;get sprite data offset
sty SCRATCHPAD+$02 ;store sprite data offset here
jsr SixSpriteStacker ;stack six sprites on top of each other vertically
lda Enemy_Rel_XPos ;get relative horizontal coordinate
stay Sprite_X_Position,y ;store in first, third and fifth sprites
stay Sprite_X_Position+8,y
stay Sprite_X_Position+16,y
clc
adcn ++$06 ;add six pixels to second, fourth and sixth sprites
stay Sprite_X_Position+4,y ;to give characteristic staggered vine shape to
stay Sprite_X_Position+12,y ;our vertical stack of sprites
stay Sprite_X_Position+20,y
ldan ++%00100001 ;set bg priority and palette attribute bits
stay Sprite_Attributes,y ;set in first, third and fifth sprites
stay Sprite_Attributes+8,y
stay Sprite_Attributes+16,y
oran ++%01000000 ;additionally, set horizontal flip bit
stay Sprite_Attributes+4,y ;for second, fourth and sixth sprites
stay Sprite_Attributes+12,y
stay Sprite_Attributes+20,y
ldxn ++$05 ;set tiles for six sprites
VineTL: ldan ++$e1 ;set tile number for sprite
stay Sprite_Tilenumber,y
iny ;move offset to next sprite data
iny
iny
iny
dex ;move onto next sprite
bpl VineTL ;loop until all sprites are done
ldy SCRATCHPAD+$02 ;get original offset
lda SCRATCHPAD+$00 ;get offset to vine adding data
checka
bne SkpVTop ;if offset not zero, skip this part
ldan ++$e0
stay Sprite_Tilenumber,y ;set other tile number for top of vine
SkpVTop: ldxn ++$00 ;start with the first sprite again
ChkFTop: lda VineStart_Y_Position ;get original starting vertical coordinate
secsub
sbcy Sprite_Y_Position,y ;subtract top-most sprite's Y coordinate
cmpn ++$64 ;if two coordinates are less than 100/$64 pixels
cmpcy
bcc NextVSp ;apart, skip this to leave sprite alone
ldan ++$f8
stay Sprite_Y_Position,y ;otherwise move sprite offscreen
NextVSp: iny ;move offset to next OAM data
iny
iny
iny
inx ;move onto next sprite
cpxn ++$06 ;do this until all sprites are checked
bne ChkFTop
ldy SCRATCHPAD+$00 ;return offset set earlier
rts
SixSpriteStacker:
ldxn ++$06 ;do six sprites
StkLp: stay Sprite_Data,y ;store X or Y coordinate into OAM data
clc
adcn ++$08 ;add eight pixels
iny
iny ;move offset four bytes forward
iny
iny
dex ;do another sprite
bne StkLp ;do this until all sprites are done
ldy SCRATCHPAD+$02 ;get saved OAM data offset and leave
rts
;-------------------------------------------------------------------------------------
FirstSprXPos:
.db $04, $00, $04, $00
FirstSprYPos:
.db $00, $04, $00, $04
SecondSprXPos:
.db $00, $08, $00, $08
SecondSprYPos:
.db $08, $00, $08, $00
FirstSprTilenum:
.db $80, $82, $81, $83
SecondSprTilenum:
.db $81, $83, $80, $82
HammerSprAttrib:
.db $03, $03, $c3, $c3
DrawHammer:
ldyx Misc_SprDataOffset,x ;get misc object OAM data offset
lda TimerControl
checka
bne ForceHPose ;if master timer control set, skip this part
ldax Misc_State,x ;otherwise get hammer's state
andn ++%01111111 ;mask out d7
cmpn ++$01 ;check to see if set to 1 yet
beq GetHPose ;if so, branch
ForceHPose: ldxn ++$00 ;reset offset here
checkx
beq RenderH ;do unconditional branch to rendering part
GetHPose: lda FrameCounter ;get frame counter
lsr ;move d3-d2 to d1-d0
lsr
andn ++%00000011 ;mask out all but d1-d0 (changes every four frames)
tax ;use as timing offset
RenderH: lda Misc_Rel_YPos ;get relative vertical coordinate
clc
adcx FirstSprYPos,x ;add first sprite vertical adder based on offset
stay Sprite_Y_Position,y ;store as sprite Y coordinate for first sprite
clc
adcx SecondSprYPos,x ;add second sprite vertical adder based on offset
stay Sprite_Y_Position+4,y ;store as sprite Y coordinate for second sprite
lda Misc_Rel_XPos ;get relative horizontal coordinate
clc
adcx FirstSprXPos,x ;add first sprite horizontal adder based on offset
stay Sprite_X_Position,y ;store as sprite X coordinate for first sprite
clc
adcx SecondSprXPos,x ;add second sprite horizontal adder based on offset
stay Sprite_X_Position+4,y ;store as sprite X coordinate for second sprite
ldax FirstSprTilenum,x
stay Sprite_Tilenumber,y ;get and store tile number of first sprite
ldax SecondSprTilenum,x
stay Sprite_Tilenumber+4,y ;get and store tile number of second sprite
ldax HammerSprAttrib,x
stay Sprite_Attributes,y ;get and store attribute bytes for both
stay Sprite_Attributes+4,y ;note in this case they use the same data
ldx ObjectOffset ;get misc object offset
lda Misc_OffscreenBits
andn ++%11111100 ;check offscreen bits
beq NoHOffscr ;if all bits clear, leave object alone
ldan ++$00
stax Misc_State,x ;otherwise nullify misc object state
ldan ++$f8
jsr DumpTwoSpr ;do sub to move hammer sprites offscreen
NoHOffscr: rts ;leave
;-------------------------------------------------------------------------------------
;$00-$01 - used to hold tile numbers ($01 addressed in draw floatey number part)
;$02 - used to hold Y coordinate for floatey number
;$03 - residual byte used for flip (but value set here affects nothing)
;$04 - attribute byte for floatey number
;$05 - used as X coordinate for floatey number
FlagpoleScoreNumTiles:
.db $f9, $50
.db $f7, $50
.db $fa, $fb
.db $f8, $fb
.db $f6, $fb
FlagpoleGfxHandler:
ldyx Enemy_SprDataOffset,x ;get sprite data offset for flagpole flag
lda Enemy_Rel_XPos ;get relative horizontal coordinate
stay Sprite_X_Position,y ;store as X coordinate for first sprite
clc
adcn ++$08 ;add eight pixels and store
stay Sprite_X_Position+4,y ;as X coordinate for second and third sprites
stay Sprite_X_Position+8,y
clc
adcn ++$0c ;add twelve more pixels and
push af
sta SCRATCHPAD+$05 ;store here to be used later by floatey number
ldax Enemy_Y_Position,x ;get vertical coordinate
jsr DumpTwoSpr ;and do sub to dump into first and second sprites
ld h,a
pop af
ld a,h
adcn ++$08 ;add eight pixels
stay Sprite_Y_Position+8,y ;and store into third sprite
lda FlagpoleFNum_Y_Pos ;get vertical coordinate for floatey number
sta SCRATCHPAD+$02 ;store it here
ldan ++$01
sta SCRATCHPAD+$03 ;set value for flip which will not be used, and
sta SCRATCHPAD+$04 ;attribute byte for floatey number
stay Sprite_Attributes,y ;set attribute bytes for all three sprites
stay Sprite_Attributes+4,y
stay Sprite_Attributes+8,y
ldan ++$7e
stay Sprite_Tilenumber,y ;put triangle shaped tile
stay Sprite_Tilenumber+8,y ;into first and third sprites
ldan ++$7f
stay Sprite_Tilenumber+4,y ;put skull tile into second sprite
lda FlagpoleCollisionYPos ;get vertical coordinate at time of collision
checka
beq ChkFlagOffscreen ;if zero, branch ahead
tya
clc ;add 12 bytes to sprite data offset
adcn ++$0c
tay ;put back in Y
lda FlagpoleScore ;get offset used to award points for touching flagpole
asl ;multiply by 2 to get proper offset here
tax
if Z80OPT
ld hl,FlagpoleScoreNumTiles
add hl,bc
call DrawSpriteObject
else
ldax FlagpoleScoreNumTiles,x ;get appropriate tile data
sta SCRATCHPAD+$00
ldax FlagpoleScoreNumTiles+1,x
jsr DrawOneSpriteRow ;use it to render floatey number
endif
ChkFlagOffscreen:
ldx ObjectOffset ;get object offset for flag
ldyx Enemy_SprDataOffset,x ;get OAM data offset
lda Enemy_OffscreenBits ;get offscreen bits
andn ++%00001110 ;mask out all but d3-d1
beq ExitDumpSpr ;if none of these bits set, branch to leave
;-------------------------------------------------------------------------------------
MoveSixSpritesOffscreen:
ldan ++$f8 ;set offscreen coordinate if jumping here
DumpSixSpr:
stay Sprite_Data+20,y ;dump A contents
stay Sprite_Data+16,y ;into third row sprites
DumpFourSpr:
stay Sprite_Data+12,y ;into second row sprites
DumpThreeSpr:
stay Sprite_Data+8,y
DumpTwoSpr:
stay Sprite_Data+4,y ;and into first row sprites
stay Sprite_Data,y
ExitDumpSpr:
rts
;-------------------------------------------------------------------------------------
DrawLargePlatform:
ldyx Enemy_SprDataOffset,x ;get OAM data offset
sty SCRATCHPAD+$02 ;store here
iny ;add 3 to it for offset
iny ;to X coordinate
iny
lda Enemy_Rel_XPos ;get horizontal relative coordinate
jsr SixSpriteStacker ;store X coordinates using A as base, stack horizontally
ldx ObjectOffset
ldax Enemy_Y_Position,x ;get vertical coordinate
jsr DumpFourSpr ;dump into first four sprites as Y coordinate
ldy AreaType
cpyn ++$03 ;check for castle-type level
beq ShrinkPlatform
ldy SecondaryHardMode ;check for secondary hard mode flag set
checky
beq SetLast2Platform ;branch if not set elsewhere
ShrinkPlatform:
ldan ++$f8 ;load offscreen coordinate if flag set or castle-type level
SetLast2Platform:
ldyx Enemy_SprDataOffset,x ;get OAM data offset
stay Sprite_Y_Position+16,y ;store vertical coordinate or offscreen
stay Sprite_Y_Position+20,y ;coordinate into last two sprites as Y coordinate
ldan ++$5b ;load default tile for platform (girder)
ldx CloudTypeOverride
checkx
beq SetPlatformTilenum ;if cloud level override flag not set, use
ldan ++$75 ;otherwise load other tile for platform (puff)
SetPlatformTilenum:
;TODO ўхЁхч rla:jr nc,$+5;ld (ix),L
ldx ObjectOffset ;get enemy object buffer offset
iny ;increment Y for tile offset
jsr DumpSixSpr ;dump tile number into all six sprites
ldan ++$02 ;set palette controls
iny ;increment Y for sprite attributes
jsr DumpSixSpr ;dump attributes into all six sprites
if Z80OPT
push bc
inc c ;increment X to get the proper offset
call GetXOffscreenBits ;then jump directly to the sub for horizontal offscreen bits
pop bc ;decrement to return to original offset
else
inx ;increment X for enemy objects
jsr GetXOffscreenBits ;get offscreen bits again
dex
endif
ldyx Enemy_SprDataOffset,x ;get OAM data offset
asl ;rotate d7 into carry, save remaining ;TODO >>4 т GetXOffscreenBits
pha ;bits to the stack
bcc SChk2
ldan ++$f8 ;if d7 was set, move first sprite offscreen
stay Sprite_Y_Position,y
SChk2: pla ;get bits from stack
asl ;rotate d6 into carry ;TODO >>4 т GetXOffscreenBits
pha ;save to stack
bcc SChk3
ldan ++$f8 ;if d6 was set, move second sprite offscreen
stay Sprite_Y_Position+4,y
SChk3: pla ;get bits from stack
asl ;rotate d5 into carry ;TODO >>4 т GetXOffscreenBits
pha ;save to stack
bcc SChk4
ldan ++$f8 ;if d5 was set, move third sprite offscreen
stay Sprite_Y_Position+8,y
SChk4: pla ;get bits from stack
asl ;rotate d4 into carry ;TODO >>4 т GetXOffscreenBits
pha ;save to stack
bcc SChk5
ldan ++$f8 ;if d4 was set, move fourth sprite offscreen
stay Sprite_Y_Position+12,y
SChk5: pla ;get bits from stack
asl ;rotate d3 into carry ;???
pha ;save to stack
bcc SChk6
ldan ++$f8 ;if d3 was set, move fifth sprite offscreen
stay Sprite_Y_Position+16,y
SChk6: pla ;get bits from stack
asl ;rotate d2 into carry ;???
bcc SLChk ;save to stack
ldan ++$f8
stay Sprite_Y_Position+20,y ;if d2 was set, move sixth sprite offscreen
SLChk: lda Enemy_OffscreenBits ;check d7 of offscreen bits
asl ;and if d7 is not set, skip sub
bcc ExDLPl
jsr MoveSixSpritesOffscreen ;otherwise branch to move all sprites offscreen
ExDLPl: rts
;-------------------------------------------------------------------------------------
DrawFloateyNumber_Coin:
lda FrameCounter ;get frame counter
lsr ;divide by 2
bcs NotRsNum ;branch if d0 not set to raise number every other frame
decx Misc_Y_Position,x ;otherwise, decrement vertical coordinate
NotRsNum: ldax Misc_Y_Position,x ;get vertical coordinate
jsr DumpTwoSpr ;dump into both sprites
lda Misc_Rel_XPos ;get relative horizontal coordinate
stay Sprite_X_Position,y ;store as X coordinate for first sprite
clc
adcn ++$08 ;add eight pixels
stay Sprite_X_Position+4,y ;store as X coordinate for second sprite
ldan ++$02
stay Sprite_Attributes,y ;store attribute byte in both sprites
stay Sprite_Attributes+4,y
ldan ++$f7
stay Sprite_Tilenumber,y ;put tile numbers into both sprites
ldan ++$fb ;that resemble "200"
stay Sprite_Tilenumber+4,y
jmp ExJCGfx ;then jump to leave (why not an rts here instead?)
JumpingCoinTiles:
.db $60, $61, $62, $63
JCoinGfxHandler:
ldyx Misc_SprDataOffset,x ;get coin/floatey number's OAM data offset
ldax Misc_State,x ;get state of misc object
cmpn ++$02 ;if 2 or greater,
cmpcy
bcs DrawFloateyNumber_Coin ;branch to draw floatey number
ldax Misc_Y_Position,x ;store vertical coordinate as
stay Sprite_Y_Position,y ;Y coordinate for first sprite
clc
adcn ++$08 ;add eight pixels
stay Sprite_Y_Position+4,y ;store as Y coordinate for second sprite
lda Misc_Rel_XPos ;get relative horizontal coordinate
stay Sprite_X_Position,y
stay Sprite_X_Position+4,y ;store as X coordinate for first and second sprites
lda FrameCounter ;get frame counter
lsr ;divide by 2 to alter every other frame
andn ++%00000011 ;mask out d2-d1
tax ;use as graphical offset
ldax JumpingCoinTiles,x ;load tile number
iny ;increment OAM data offset to write tile numbers
jsr DumpTwoSpr ;do sub to dump tile number into both sprites
dey ;decrement to get old offset
ldan ++$02
stay Sprite_Attributes,y ;set attribute byte in first sprite
ldan ++$82
stay Sprite_Attributes+4,y ;set attribute byte with vertical flip in second sprite
ldx ObjectOffset ;get misc object offset
ExJCGfx: rts ;leave
;-------------------------------------------------------------------------------------
;$00-$01 - used to hold tiles for drawing the power-up, $00 also used to hold power-up type
;$02 - used to hold bottom row Y position
;$03 - used to hold flip control (not used here)
;$04 - used to hold sprite attributes
;$05 - used to hold X position
;$07 - counter
;tiles arranged in top left, right, bottom left, right order
PowerUpGfxTable:
.db $76, $77, $78, $79 ;regular mushroom
.db $d6, $d6, $d9, $d9 ;fire flower
.db $8d, $8d, $e4, $e4 ;star
.db $76, $77, $78, $79 ;1-up mushroom
PowerUpAttributes:
.db $02, $01, $02, $01
DrawPowerUp:
ldy Enemy_SprDataOffset+5 ;get power-up's sprite data offset
lda Enemy_Rel_YPos ;get relative vertical coordinate
clc
adcn ++$08 ;add eight pixels
sta SCRATCHPAD+$02 ;store result here
lda Enemy_Rel_XPos ;get relative horizontal coordinate
sta SCRATCHPAD+$05 ;store here
ldx PowerUpType ;get power-up type
ldax PowerUpAttributes,x ;get attribute data for power-up type
orai Enemy_SprAttrib+5 ;add background priority bit if set
sta SCRATCHPAD+$04 ;store attributes here
txa
pha ;save power-up type to the stack
asl
asl ;multiply by four to get proper offset
tax ;use as X
ldan ++$01
if Z80OPT
;ld (SCRATCHPAD+$07),a ;set counter here to draw two rows of sprite object
ld (SCRATCHPAD+$03),a ;init d1 of flip control
ld hl,PowerUpGfxTable
add hl,bc
call DrawSpriteObject
call DrawSpriteObject
else
sta SCRATCHPAD+$07 ;set counter here to draw two rows of sprite object
sta SCRATCHPAD+$03 ;init d1 of flip control
PUpDrawLoop0
ldax PowerUpGfxTable,x ;load left tile of power-up object
sta SCRATCHPAD+$00
ldax PowerUpGfxTable+1,x ;load right tile
jsr DrawOneSpriteRow ;branch to draw one row of our power-up object
deci SCRATCHPAD+$07 ;decrement counter
bpl PUpDrawLoop0 ;branch until two rows are drawn
endif
ldy Enemy_SprDataOffset+5 ;get sprite data offset again
pla ;pull saved power-up type from the stack
checka
beq PUpOfs ;if regular mushroom, branch, do not change colors or flip
cmpn ++$03
beq PUpOfs ;if 1-up mushroom, branch, do not change colors or flip
sta SCRATCHPAD+$00 ;store power-up type here now
lda FrameCounter ;get frame counter
lsr ;divide by 2 to change colors every two frames
andn ++%00000011 ;mask out all but d1 and d0 (previously d2 and d1)
orai Enemy_SprAttrib+5 ;add background priority bit if any set
stay Sprite_Attributes,y ;set as new palette bits for top left and
stay Sprite_Attributes+4,y ;top right sprites for fire flower and star
ldx SCRATCHPAD+$00
dex ;check power-up type for fire flower
beq FlipPUpRightSide ;if found, skip this part
stay Sprite_Attributes+8,y ;otherwise set new palette bits for bottom left
stay Sprite_Attributes+12,y ;and bottom right sprites as well for star only
FlipPUpRightSide:
lday Sprite_Attributes+4,y
oran ++%01000000 ;set horizontal flip bit for top right sprite
stay Sprite_Attributes+4,y
lday Sprite_Attributes+12,y
oran ++%01000000 ;set horizontal flip bit for bottom right sprite
stay Sprite_Attributes+12,y ;note these are only done for fire flower and star power-ups
PUpOfs: jmp SprObjectOffscrChk ;jump to check to see if power-up is offscreen at all, then leave
;-------------------------------------------------------------------------------------
;$00-$01 - used in DrawEnemyObjRow to hold sprite tile numbers
;$02 - used to store Y position
;$03 - used to store moving direction, used to flip enemies horizontally
;$04 - used to store enemy's sprite attributes
;$05 - used to store X position
;$eb - used to hold sprite data offset
;$ec - used to hold either altered enemy state or special value used in gfx handler as condition
;$ed - used to hold enemy state from buffer
;$ef - used to hold enemy code used in gfx handler (may or may not resemble Enemy_ID values)
;tiles arranged in top left, right, middle left, right, bottom left, right order
EnemyGraphicsTable:
.db $fc, $fc, $aa, $ab, $ac, $ad ;buzzy beetle frame 1
.db $fc, $fc, $ae, $af, $b0, $b1 ; frame 2
.db $fc, $a5, $a6, $a7, $a8, $a9 ;koopa troopa frame 1
.db $fc, $a0, $a1, $a2, $a3, $a4 ; frame 2
.db $69, $a5, $6a, $a7, $a8, $a9 ;koopa paratroopa frame 1
.db $6b, $a0, $6c, $a2, $a3, $a4 ; frame 2
.db $fc, $fc, $96, $97, $98, $99 ;spiny frame 1
.db $fc, $fc, $9a, $9b, $9c, $9d ; frame 2
.db $fc, $fc, $8f, $8e, $8e, $8f ;spiny's egg frame 1
.db $fc, $fc, $95, $94, $94, $95 ; frame 2
.db $fc, $fc, $dc, $dc, $df, $df ;bloober frame 1
.db $dc, $dc, $dd, $dd, $de, $de ; frame 2
.db $fc, $fc, $b2, $b3, $b4, $b5 ;cheep-cheep frame 1
.db $fc, $fc, $b6, $b3, $b7, $b5 ; frame 2
.db $fc, $fc, $70, $71, $72, $73 ;goomba
.db $fc, $fc, $6e, $6e, $6f, $6f ;koopa shell frame 1 (upside-down)
.db $fc, $fc, $6d, $6d, $6f, $6f ; frame 2
.db $fc, $fc, $6f, $6f, $6e, $6e ;koopa shell frame 1 (rightsideup)
.db $fc, $fc, $6f, $6f, $6d, $6d ; frame 2
.db $fc, $fc, $f4, $f4, $f5, $f5 ;buzzy beetle shell frame 1 (rightsideup)
.db $fc, $fc, $f4, $f4, $f5, $f5 ; frame 2
.db $fc, $fc, $f5, $f5, $f4, $f4 ;buzzy beetle shell frame 1 (upside-down)
.db $fc, $fc, $f5, $f5, $f4, $f4 ; frame 2
.db $fc, $fc, $fc, $fc, $ef, $ef ;defeated goomba
.db $b9, $b8, $bb, $ba, $bc, $bc ;lakitu frame 1
.db $fc, $fc, $bd, $bd, $bc, $bc ; frame 2
.db $7a, $7b, $da, $db, $d8, $d8 ;princess
.db $cd, $cd, $ce, $ce, $cf, $cf ;mushroom retainer
.db $7d, $7c, $d1, $8c, $d3, $d2 ;hammer bro frame 1
.db $7d, $7c, $89, $88, $8b, $8a ; frame 2
.db $d5, $d4, $e3, $e2, $d3, $d2 ; frame 3
.db $d5, $d4, $e3, $e2, $8b, $8a ; frame 4
.db $e5, $e5, $e6, $e6, $eb, $eb ;piranha plant frame 1
.db $ec, $ec, $ed, $ed, $ee, $ee ; frame 2
.db $fc, $fc, $d0, $d0, $d7, $d7 ;podoboo
.db $bf, $be, $c1, $c0, $c2, $fc ;bowser front frame 1
.db $c4, $c3, $c6, $c5, $c8, $c7 ;bowser rear frame 1
.db $bf, $be, $ca, $c9, $c2, $fc ; front frame 2
.db $c4, $c3, $c6, $c5, $cc, $cb ; rear frame 2
.db $fc, $fc, $e8, $e7, $ea, $e9 ;bullet bill
.db $f2, $f2, $f3, $f3, $f2, $f2 ;jumpspring frame 1
.db $f1, $f1, $f1, $f1, $fc, $fc ; frame 2
.db $f0, $f0, $fc, $fc, $fc, $fc ; frame 3
EnemyGfxTableOffsets:
.db $0c, $0c, $00, $0c, $0c, $a8, $54, $3c
.db $ea, $18, $48, $48, $cc, $c0, $18, $18
.db $18, $90, $24, $ff, $48, $9c, $d2, $d8
.db $f0, $f6, $fc
EnemyAttributeData:
.db $01, $02, $03, $02, $01, $01, $03, $03
.db $03, $01, $01, $02, $02, $21, $01, $02
.db $01, $01, $02, $ff, $02, $02, $01, $01
.db $02, $02, $02
EnemyAnimTimingBMask:
.db $08, $18
JumpspringFrameOffsets:
.db $18, $19, $1a, $19, $18
EnemyGfxHandler:
;TODO тьхёЄю ¤Єюую фхЁхтр тхЄтыхэшщ ёфхырЄ№ яхЁхїюф яю Єшяє юс·хъЄр яю ЄрсышЎх, фры№°х тхЄтшЄ№
ldax Enemy_Y_Position,x ;get enemy object vertical position
sta SCRATCHPAD+$02
lda Enemy_Rel_XPos ;get enemy object horizontal position
sta SCRATCHPAD+$05 ;relative to screen
ldyx Enemy_SprDataOffset,x
sty SCRATCHPAD+$eb ;get sprite data offset
ldan ++$00
sta VerticalFlipFlag ;initialize vertical flip flag by default
ldax Enemy_MovingDir,x
sta SCRATCHPAD+$03 ;get enemy object moving direction
ldax Enemy_SprAttrib,x
sta SCRATCHPAD+$04 ;get enemy object sprite attributes
ldax Enemy_ID,x
cmpn ++PiranhaPlant ;is enemy object piranha plant?
bne CheckForRetainerObj ;if not, branch
ldyx PiranhaPlant_Y_Speed,x
checky
bmi CheckForRetainerObj ;if piranha plant moving upwards, branch
ldyx EnemyFrameTimer,x
checky
beq CheckForRetainerObj ;if timer for movement expired, branch
rts ;if all conditions fail, leave
CheckForRetainerObj:
ldax Enemy_State,x ;store enemy state
sta SCRATCHPAD+$ed
andn ++%00011111 ;nullify all but 5 LSB and use as Y
tay
ldax Enemy_ID,x ;check for mushroom retainer/princess object
cmpn ++RetainerObject
bne CheckForBulletBillCV ;if not found, branch
ldyn ++$00 ;if found, nullify saved state in Y
ldan ++$01 ;set value that will not be used
sta SCRATCHPAD+$03
ldan ++$15 ;set value $15 as code for mushroom retainer/princess object
CheckForBulletBillCV:
cmpn ++BulletBill_CannonVar ;otherwise check for bullet bill object
bne CheckForJumpspring ;if not found, branch again
deci SCRATCHPAD+$02 ;decrement saved vertical position
ldan ++$03
ldyx EnemyFrameTimer,x ;get timer for enemy object
checky
beq SBBAt ;if expired, do not set priority bit
oran ++%00100000 ;otherwise do so
SBBAt: sta SCRATCHPAD+$04 ;set new sprite attributes
ldyn ++$00 ;nullify saved enemy state both in Y and in
sty SCRATCHPAD+$ed ;memory location here
ldan ++$08 ;set specific value to unconditionally branch once
CheckForJumpspring:
cmpn ++JumpspringObject ;check for jumpspring object
bne CheckForPodoboo
ldyn ++$03 ;set enemy state -2 MSB here for jumpspring object
ldx JumpspringAnimCtrl ;get current frame number for jumpspring object
ldax JumpspringFrameOffsets,x ;load data using frame number as offset
CheckForPodoboo:
sta SCRATCHPAD+$ef ;store saved enemy object value here
sty SCRATCHPAD+$ec ;and Y here (enemy state -2 MSB if not changed)
ldx ObjectOffset ;get enemy object offset
cmpn ++$0c ;check for podoboo object
bne CheckBowserGfxFlag ;branch if not found
ldax Enemy_Y_Speed,x ;if moving upwards, branch
checka
bmi CheckBowserGfxFlag
inci VerticalFlipFlag ;otherwise, set flag for vertical flip
CheckBowserGfxFlag:
lda BowserGfxFlag ;if not drawing bowser at all, skip to something else
checka
beq CheckForGoomba
ldyn ++$16 ;if set to 1, draw bowser's front
cmpn ++$01
beq SBwsrGfxOfs
iny ;otherwise draw bowser's rear
SBwsrGfxOfs: sty SCRATCHPAD+$ef
CheckForGoomba:
;фюё■фр 485t
ldy SCRATCHPAD+$ef ;check value for goomba object
cpyn ++Goomba
bne CheckBowserFront ;branch if not found
ldax Enemy_State,x
cmpn ++$02 ;check for defeated state
cmpcy
bcc GmbaAnim ;if not defeated, go ahead and animate
ldxn ++$04 ;if defeated, write new value here
stx SCRATCHPAD+$ec
GmbaAnim: andn ++%00100000 ;check for d5 set in enemy object state
orai TimerControl ;or timer disable flag set
bne CheckBowserFront ;if either condition true, do not animate goomba
lda FrameCounter
andn ++%00001000 ;check for every eighth frame
bne CheckBowserFront
lda SCRATCHPAD+$03
eorn ++%00000011 ;invert bits to flip horizontally every eight frames
sta SCRATCHPAD+$03 ;leave alone otherwise
CheckBowserFront:
lday EnemyAttributeData,y ;load sprite attribute using enemy object
orai SCRATCHPAD+$04 ;as offset, and add to bits already loaded
sta SCRATCHPAD+$04
lday EnemyGfxTableOffsets,y ;load value based on enemy object as offset
tax ;save as X
ldy SCRATCHPAD+$ec ;get previously saved value
lda BowserGfxFlag
checka
beq CheckForSpiny ;if not drawing bowser object at all, skip all of this
cmpn ++$01
bne CheckBowserRear ;if not drawing front part, branch to draw the rear part
lda BowserBodyControls ;check bowser's body control bits
checka
bpl ChkFrontSte ;branch if d7 not set (control's bowser's mouth)
ldxn ++$de ;otherwise load offset for second frame
ChkFrontSte: lda SCRATCHPAD+$ed ;check saved enemy state
andn ++%00100000 ;if bowser not defeated, do not set flag
beq DrawBowser
FlipBowserOver:
stx VerticalFlipFlag ;set vertical flip flag to nonzero
DrawBowser:
jmp DrawEnemyObject ;draw bowser's graphics now
CheckBowserRear:
lda BowserBodyControls ;check bowser's body control bits
andn ++$01
beq ChkRearSte ;branch if d0 not set (control's bowser's feet)
ldxn ++$e4 ;otherwise load offset for second frame
ChkRearSte: lda SCRATCHPAD+$ed ;check saved enemy state
andn ++%00100000 ;if bowser not defeated, do not set flag
beq DrawBowser
lda SCRATCHPAD+$02 ;subtract 16 pixels from
secsub ;saved vertical coordinate
sbcn ++$10
sta SCRATCHPAD+$02
jmp FlipBowserOver ;jump to set vertical flip flag
CheckForSpiny:
cpxn ++$24 ;check if value loaded is for spiny
bne CheckForLakitu ;if not found, branch
cpyn ++$05 ;if enemy state set to $05, do this,
bne NotEgg ;otherwise branch
ldxn ++$30 ;set to spiny egg offset
ldan ++$02
sta SCRATCHPAD+$03 ;set enemy direction to reverse sprites horizontally
ldan ++$05
sta SCRATCHPAD+$ec ;set enemy state
NotEgg: jmp CheckForHammerBro ;skip a big chunk of this if we found spiny but not in egg
CheckForLakitu:
cpxn ++$90 ;check value for lakitu's offset loaded
bne CheckUpsideDownShell ;branch if not loaded
lda SCRATCHPAD+$ed
andn ++%00100000 ;check for d5 set in enemy state
bne NoLAFr ;branch if set
lda FrenzyEnemyTimer
cmpn ++$10 ;check timer to see if we've reached a certain range
cmpcy
bcs NoLAFr ;branch if not
ldxn ++$96 ;if d6 not set and timer in range, load alt frame for lakitu
NoLAFr: jmp CheckDefeatedState ;skip this next part if we found lakitu but alt frame not needed
CheckUpsideDownShell:
lda SCRATCHPAD+$ef ;check for enemy object => $04
cmpn ++$04
cmpcy
bcs CheckRightSideUpShell ;branch if true
cpyn ++$02
cmpcy
bcc CheckRightSideUpShell ;branch if enemy state < $02
ldxn ++$5a ;set for upside-down koopa shell by default
ldy SCRATCHPAD+$ef
cpyn ++BuzzyBeetle ;check for buzzy beetle object
bne CheckRightSideUpShell
ldxn ++$7e ;set for upside-down buzzy beetle shell if found
inci SCRATCHPAD+$02 ;increment vertical position by one pixel
CheckRightSideUpShell:
lda SCRATCHPAD+$ec ;check for value set here
cmpn ++$04 ;if enemy state < $02, do not change to shell, if
bne CheckForHammerBro ;enemy state => $02 but not = $04, leave shell upside-down
ldxn ++$72 ;set right-side up buzzy beetle shell by default
inci SCRATCHPAD+$02 ;increment saved vertical position by one pixel
ldy SCRATCHPAD+$ef
cpyn ++BuzzyBeetle ;check for buzzy beetle object
beq CheckForDefdGoomba ;branch if found
ldxn ++$66 ;change to right-side up koopa shell if not found
inci SCRATCHPAD+$02 ;and increment saved vertical position again
CheckForDefdGoomba:
cpyn ++Goomba ;check for goomba object (necessary if previously
bne CheckForHammerBro ;failed buzzy beetle object test)
ldxn ++$54 ;load for regular goomba
lda SCRATCHPAD+$ed ;note that this only gets performed if enemy state => $02
andn ++%00100000 ;check saved enemy state for d5 set
bne CheckForHammerBro ;branch if set
ldxn ++$8a ;load offset for defeated goomba
deci SCRATCHPAD+$02 ;set different value and decrement saved vertical position
CheckForHammerBro:
ldy ObjectOffset
lda SCRATCHPAD+$ef ;check for hammer bro object
cmpn ++HammerBro
bne CheckForBloober ;branch if not found
lda SCRATCHPAD+$ed
checka
beq CheckToAnimateEnemy ;branch if not in normal enemy state
andn ++%00001000
beq CheckDefeatedState ;if d3 not set, branch further away
ldxn ++$b4 ;otherwise load offset for different frame
checkx
bne CheckToAnimateEnemy ;unconditional branch
CheckForBloober:
cpxn ++$48 ;check for cheep-cheep offset loaded
beq CheckToAnimateEnemy ;branch if found
lday EnemyIntervalTimer,y
cmpn ++$05
cmpcy
bcs CheckDefeatedState ;branch if some timer is above a certain point
cpxn ++$3c ;check for bloober offset loaded
bne CheckToAnimateEnemy ;branch if not found this time
cmpn ++$01
beq CheckDefeatedState ;branch if timer is set to certain point
inci SCRATCHPAD+$02 ;increment saved vertical coordinate three pixels
inci SCRATCHPAD+$02
inci SCRATCHPAD+$02
jmp CheckAnimationStop ;and do something else
CheckToAnimateEnemy:
lda SCRATCHPAD+$ef ;check for specific enemy objects
cmpn ++Goomba
beq CheckDefeatedState ;branch if goomba
cmpn ++$08
beq CheckDefeatedState ;branch if bullet bill (note both variants use $08 here)
cmpn ++Podoboo
beq CheckDefeatedState ;branch if podoboo
cmpn ++$18 ;branch if => $18
cmpcy
bcs CheckDefeatedState
ldyn ++$00
cmpn ++$15 ;check for mushroom retainer/princess object
;jr $
bne CheckForSecondFrame ;which uses different code here, branch if not found
iny ;residual instruction
lda WorldNumber ;are we on world 8?
cmpn ++World8
cmpcy
if ALWAYSPRINCESS
scf
endif
bcs CheckDefeatedState ;if so, leave the offset alone (use princess)
ldxn ++$a2 ;otherwise, set for mushroom retainer object instead
ldan ++$03 ;set alternate state here
sta SCRATCHPAD+$ec
checka
bne CheckDefeatedState ;unconditional branch
CheckForSecondFrame:
lda FrameCounter ;load frame counter
andy EnemyAnimTimingBMask,y ;mask it (partly residual, one byte not ever used)
bne CheckDefeatedState ;branch if timing is off
CheckAnimationStop:
lda SCRATCHPAD+$ed ;check saved enemy state
andn ++%10100000 ;for d7 or d5, or check for timers stopped
orai TimerControl
bne CheckDefeatedState ;if either condition true, branch
txa
clc
adcn ++$06 ;add $06 to current enemy offset
tax ;to animate various enemy objects
CheckDefeatedState:
lda SCRATCHPAD+$ed ;check saved enemy state
andn ++%00100000 ;for d5 set
beq DrawEnemyObject ;branch if not set
lda SCRATCHPAD+$ef
cmpn ++$04 ;check for saved enemy object => $04
cmpcy
bcc DrawEnemyObject ;branch if less
ldyn ++$01
sty VerticalFlipFlag ;set vertical flip flag
dey
sty SCRATCHPAD+$ec ;init saved value here
DrawEnemyObject:
;фюё■фр 1118t (юЄ эрўрыр яЁюЎхфєЁ√)
ldy SCRATCHPAD+$eb ;load sprite data offset
if Z80OPT
ld hl,EnemyGraphicsTable
add hl,bc
call DrawSpriteObject ;360t
call DrawSpriteObject ;360t
call DrawSpriteObject ;360t
else
jsr DrawEnemyObjRow ;draw six tiles of data
jsr DrawEnemyObjRow ;into sprite data
jsr DrawEnemyObjRow
endif
;фю т√їюфр х∙╕ 1030t (эхтшфшь√щ Goomba)/604t (тшфшь√щ)
ldx ObjectOffset ;get enemy object offset
ldyx Enemy_SprDataOffset,x ;get sprite data offset
lda SCRATCHPAD+$ef
cmpn ++$08 ;get saved enemy object and check
bne CheckForVerticalFlip ;for bullet bill, branch if not found
SkipToOffScrChk:
jmp SprObjectOffscrChk ;jump if found
CheckForVerticalFlip:
lda VerticalFlipFlag ;check if vertical flip flag is set here
checka
beq CheckForESymmetry ;branch if not
lday Sprite_Attributes,y ;get attributes of first sprite we dealt with
oran ++%10000000 ;set bit for vertical flip
iny
iny ;increment two bytes so that we store the vertical flip
jsr DumpSixSpr ;in attribute bytes of enemy obj sprite data
dey
dey ;now go back to the Y coordinate offset
tya
tax ;give offset to X
lda SCRATCHPAD+$ef
cmpn ++HammerBro ;check saved enemy object for hammer bro
beq FlipEnemyVertically
cmpn ++Lakitu ;check saved enemy object for lakitu
beq FlipEnemyVertically ;branch for hammer bro or lakitu
cmpn ++$15
cmpcy
bcs FlipEnemyVertically ;also branch if enemy object => $15
txa
clc
adcn ++$08 ;if not selected objects or => $15, set
tax ;offset in X for next row
FlipEnemyVertically:
ldax Sprite_Tilenumber,x ;load first or second row tiles
pha ;and save tiles to the stack
ldax Sprite_Tilenumber+4,x
pha
lday Sprite_Tilenumber+16,y ;exchange third row tiles
stax Sprite_Tilenumber,x ;with first or second row tiles
lday Sprite_Tilenumber+20,y
stax Sprite_Tilenumber+4,x
pla ;pull first or second row tiles from stack
stay Sprite_Tilenumber+20,y ;and save in third row
pla
stay Sprite_Tilenumber+16,y
CheckForESymmetry:
lda BowserGfxFlag ;are we drawing bowser at all?
checka
bne SkipToOffScrChk ;branch if so
lda SCRATCHPAD+$ef
ldx SCRATCHPAD+$ec ;get alternate enemy state
cmpn ++$05 ;check for hammer bro object
bne ContES
jmp SprObjectOffscrChk ;jump if found
ContES: cmpn ++Bloober ;check for bloober object
beq MirrorEnemyGfx
cmpn ++PiranhaPlant ;check for piranha plant object
beq MirrorEnemyGfx
cmpn ++Podoboo ;check for podoboo object
beq MirrorEnemyGfx ;branch if either of three are found
cmpn ++Spiny ;check for spiny object
bne ESRtnr ;branch closer if not found
cpxn ++$05 ;check spiny's state
bne CheckToMirrorLakitu ;branch if not an egg, otherwise
ESRtnr: cmpn ++$15 ;check for princess/mushroom retainer object
bne SpnySC
ldan ++$42 ;set horizontal flip on bottom right sprite
stay Sprite_Attributes+20,y ;note that palette bits were already set earlier
SpnySC: cpxn ++$02 ;if alternate enemy state set to 1 or 0, branch
cmpcy
bcc CheckToMirrorLakitu
MirrorEnemyGfx:
lda BowserGfxFlag ;if enemy object is bowser, skip all of this
checka
bne CheckToMirrorLakitu
lday Sprite_Attributes,y ;load attribute bits of first sprite
andn ++%10100011
stay Sprite_Attributes,y ;save vertical flip, priority, and palette bits
stay Sprite_Attributes+8,y ;in left sprite column of enemy object OAM data
stay Sprite_Attributes+16,y
oran ++%01000000 ;set horizontal flip
cpxn ++$05 ;check for state used by spiny's egg
bne EggExc ;if alternate state not set to $05, branch
oran ++%10000000 ;otherwise set vertical flip
EggExc: stay Sprite_Attributes+4,y ;set bits of right sprite column
stay Sprite_Attributes+12,y ;of enemy object sprite data
stay Sprite_Attributes+20,y
cpxn ++$04 ;check alternate enemy state
bne CheckToMirrorLakitu ;branch if not $04
lday Sprite_Attributes+8,y ;get second row left sprite attributes
oran ++%10000000
stay Sprite_Attributes+8,y ;store bits with vertical flip in
stay Sprite_Attributes+16,y ;second and third row left sprites
oran ++%01000000
stay Sprite_Attributes+12,y ;store with horizontal and vertical flip in
stay Sprite_Attributes+20,y ;second and third row right sprites
CheckToMirrorLakitu:
lda SCRATCHPAD+$ef ;check for lakitu enemy object
cmpn ++Lakitu
bne CheckToMirrorJSpring ;branch if not found
lda VerticalFlipFlag
checka
bne NVFLak ;branch if vertical flip flag not set
lday Sprite_Attributes+16,y ;save vertical flip and palette bits
andn ++%10000001 ;in third row left sprite
stay Sprite_Attributes+16,y
lday Sprite_Attributes+20,y ;set horizontal flip and palette bits
oran ++%01000001 ;in third row right sprite
stay Sprite_Attributes+20,y
ldx FrenzyEnemyTimer ;check timer
cpxn ++$10
cmpcy
bcs SprObjectOffscrChk ;branch if timer has not reached a certain range
stay Sprite_Attributes+12,y ;otherwise set same for second row right sprite
andn ++%10000001
stay Sprite_Attributes+8,y ;preserve vertical flip and palette bits for left sprite
if Z80
or a
endif
bcc SprObjectOffscrChk ;unconditional branch
NVFLak: lday Sprite_Attributes,y ;get first row left sprite attributes
andn ++%10000001
stay Sprite_Attributes,y ;save vertical flip and palette bits
lday Sprite_Attributes+4,y ;get first row right sprite attributes
oran ++%01000001 ;set horizontal flip and palette bits
stay Sprite_Attributes+4,y ;note that vertical flip is left as-is
CheckToMirrorJSpring:
lda SCRATCHPAD+$ef ;check for jumpspring object (any frame)
cmpn ++$18
cmpcy
bcc SprObjectOffscrChk ;branch if not jumpspring object at all
ldan ++$82
stay Sprite_Attributes+8,y ;set vertical flip and palette bits of
stay Sprite_Attributes+16,y ;second and third row left sprites
oran ++%01000000
stay Sprite_Attributes+12,y ;set, in addition to those, horizontal flip
stay Sprite_Attributes+20,y ;for second and third row right sprites
SprObjectOffscrChk:
;юЄё■фр фю т√їюфр х∙╕ 238t (тшфшь√щ Goomba)
ldx ObjectOffset ;get enemy buffer offset
lda Enemy_OffscreenBits ;check offscreen information
lsr
lsr ;shift three times to the right
lsr ;which puts d2 into carry
pha ;save to stack
bcc LcChk ;branch if not set
ldan ++$04 ;set for right column sprites
jsr MoveESprColOffscreen ;and move them offscreen
LcChk: pla ;get from stack
lsr ;move d3 to carry
pha ;save to stack
bcc Row3C ;branch if not set
ldan ++$00 ;set for left column sprites,
jsr MoveESprColOffscreen ;move them offscreen
Row3C: pla ;get from stack again
lsr ;move d5 to carry this time
lsr
pha ;save to stack again
bcc Row23C ;branch if carry not set
ldan ++$10 ;set for third row of sprites
jsr MoveESprRowOffscreen ;and move them offscreen
Row23C: pla ;get from stack
lsr ;move d6 into carry
pha ;save to stack
bcc AllRowC
ldan ++$08 ;set for second and third rows
jsr MoveESprRowOffscreen ;move them offscreen
AllRowC: pla ;get from stack once more
lsr ;move d7 into carry
bcc ExEGHandler
jsr MoveESprRowOffscreen ;move all sprites offscreen (A should be 0 by now)
ldax Enemy_ID,x
cmpn ++Podoboo ;check enemy identifier for podoboo
beq ExEGHandler ;skip this part if found, we do not want to erase podoboo!
ldax Enemy_Y_HighPos,x ;check high byte of vertical position
cmpn ++$02 ;if not yet past the bottom of the screen, branch
bne ExEGHandler
jsr EraseEnemyObject ;what it says
ExEGHandler:
rts
if Z80OPT
else
DrawEnemyObjRow:
ldax EnemyGraphicsTable,x ;load two tiles of enemy graphics
sta SCRATCHPAD+$00
ldax EnemyGraphicsTable+1,x
DrawOneSpriteRow:
sta SCRATCHPAD+$01
jmp DrawSpriteObject ;draw them
endif
MoveESprRowOffscreen:
clc ;add A to enemy object OAM data offset
adcx Enemy_SprDataOffset,x
tay ;use as offset
ldan ++$f8
jmp DumpTwoSpr ;move first row of sprites offscreen
MoveESprColOffscreen:
clc ;add A to enemy object OAM data offset
adcx Enemy_SprDataOffset,x
tay ;use as offset
jsr MoveColOffscreen ;move first and second row sprites in column offscreen
stay Sprite_Data+16,y ;move third row sprite in column offscreen
rts
;-------------------------------------------------------------------------------------
;$00-$01 - tile numbers
;$02 - relative Y position
;$03 - horizontal flip flag (not used here)
;$04 - attributes
;$05 - relative X position
DefaultBlockObjTiles:
.db $85, $85, $86, $86 ;brick w/ line (these are sprite tiles, not BG!)
DrawBlock:
lda Block_Rel_YPos ;get relative vertical coordinate of block object
sta SCRATCHPAD+$02 ;store here
lda Block_Rel_XPos ;get relative horizontal coordinate of block object
sta SCRATCHPAD+$05 ;store here
ldan ++$03
sta SCRATCHPAD+$04 ;set attribute byte here
lsr
sta SCRATCHPAD+$03 ;set horizontal flip bit here (will not be used) ???
ldyx Block_SprDataOffset,x ;get sprite data offset
if Z80OPT
ld hl,DefaultBlockObjTiles
;add hl,bc
call DrawSpriteObject
call DrawSpriteObject
else
ldxn ++$00 ;reset X for use as offset to tile data
DBlkLoop:
ldax DefaultBlockObjTiles,x ;get left tile number
sta SCRATCHPAD+$00 ;set here
ldax DefaultBlockObjTiles+1,x ;get right tile number
jsr DrawOneSpriteRow ;do sub to write tile numbers to first row of sprites
cpxn ++$04 ;check incremented offset
bne DBlkLoop ;and loop back until all four sprites are done
endif
ldx ObjectOffset ;get block object offset
ldyx Block_SprDataOffset,x ;get sprite data offset
lda AreaType
cmpn ++$01 ;check for ground level type area
beq ChkRep ;if found, branch to next part
ldan ++$86
stay Sprite_Tilenumber,y ;otherwise remove brick tiles with lines
stay Sprite_Tilenumber+4,y ;and replace then with lineless brick tiles
ChkRep: ldax Block_Metatile,x ;check replacement metatile
cmpn ++$c4 ;if not used block metatile, then
bne BlkOffscr ;branch ahead to use current graphics
ldan ++$87 ;set A for used block tile
iny ;increment Y to write to tile bytes
jsr DumpFourSpr ;do sub to dump into all four sprites
dey ;return Y to original offset
ldan ++$03 ;set palette bits
ldx AreaType
dex ;check for ground level type area again
beq SetBFlip ;if found, use current palette bits
lsr ;otherwise set to $01
SetBFlip: ldx ObjectOffset ;put block object offset back in X
stay Sprite_Attributes,y ;store attribute byte as-is in first sprite
oran ++%01000000
stay Sprite_Attributes+4,y ;set horizontal flip bit for second sprite
oran ++%10000000
stay Sprite_Attributes+12,y ;set both flip bits for fourth sprite
andn ++%10000011
stay Sprite_Attributes+8,y ;set vertical flip bit for third sprite
BlkOffscr: lda Block_OffscreenBits ;get offscreen bits for block object
pha ;save to stack
andn ++%00000100 ;check to see if d2 in offscreen bits are set
beq PullOfsB ;if not set, branch, otherwise move sprites offscreen
ldan ++$f8 ;move offscreen two OAMs
stay Sprite_Y_Position+4,y ;on the right side
stay Sprite_Y_Position+12,y
PullOfsB: pla ;pull offscreen bits from stack
ChkLeftCo: andn ++%00001000 ;check to see if d3 in offscreen bits are set
beq ExDBlk ;if not set, branch, otherwise move sprites offscreen
MoveColOffscreen:
ldan ++$f8 ;move offscreen two OAMs
stay Sprite_Y_Position,y ;on the left side (or two rows of enemy on either side
stay Sprite_Y_Position+8,y ;if branched here from enemy graphics handler)
ExDBlk: rts
;-------------------------------------------------------------------------------------
;$00 - used to hold palette bits for attribute byte or relative X position
DrawBrickChunks:
ldan ++$02 ;set palette bits here
sta SCRATCHPAD+$00
ldan ++$75 ;set tile number for ball (something residual, likely)
ldy GameEngineSubroutine
cpyn ++$05 ;if end-of-level routine running,
beq DChunks ;use palette and tile number assigned
ldan ++$03 ;otherwise set different palette bits
sta SCRATCHPAD+$00
ldan ++$84 ;and set tile number for brick chunks
DChunks: ldyx Block_SprDataOffset,x ;get OAM data offset
iny ;increment to start with tile bytes in OAM
jsr DumpFourSpr ;do sub to dump tile number into all four sprites
lda FrameCounter ;get frame counter
asl
asl
asl ;move low nybble to high
asl
andn ++$c0 ;get what was originally d3-d2 of low nybble
orai SCRATCHPAD+$00 ;add palette bits
iny ;increment offset for attribute bytes
jsr DumpFourSpr ;do sub to dump attribute data into all four sprites
dey
dey ;decrement offset to Y coordinate
lda Block_Rel_YPos ;get first block object's relative vertical coordinate
jsr DumpTwoSpr ;do sub to dump current Y coordinate into two sprites
lda Block_Rel_XPos ;get first block object's relative horizontal coordinate
stay Sprite_X_Position,y ;save into X coordinate of first sprite
ldax Block_Orig_XPos,x ;get original horizontal coordinate
secsub
sbci ScreenLeft_X_Pos ;subtract coordinate of left side from original coordinate
sta SCRATCHPAD+$00 ;store result as relative horizontal coordinate of original
secsub
sbci Block_Rel_XPos ;get difference of relative positions of original - current
cmpcy
adci SCRATCHPAD+$00 ;add original relative position to result
adcn ++$06 ;plus 6 pixels to position second brick chunk correctly
stay Sprite_X_Position+4,y ;save into X coordinate of second sprite
lda Block_Rel_YPos+1 ;get second block object's relative vertical coordinate
stay Sprite_Y_Position+8,y
stay Sprite_Y_Position+12,y ;dump into Y coordinates of third and fourth sprites
lda Block_Rel_XPos+1 ;get second block object's relative horizontal coordinate
stay Sprite_X_Position+8,y ;save into X coordinate of third sprite
lda SCRATCHPAD+$00 ;use original relative horizontal position
secsub
sbci Block_Rel_XPos+1 ;get difference of relative positions of original - current
cmpcy
adci SCRATCHPAD+$00 ;add original relative position to result
adcn ++$06 ;plus 6 pixels to position fourth brick chunk correctly
stay Sprite_X_Position+12,y ;save into X coordinate of fourth sprite
lda Block_OffscreenBits ;get offscreen bits for block object
jsr ChkLeftCo ;do sub to move left half of sprites offscreen if necessary
lda Block_OffscreenBits ;get offscreen bits again
asl ;shift d7 into carry
bcc ChnkOfs ;if d7 not set, branch to last part
ldan ++$f8
jsr DumpTwoSpr ;otherwise move top sprites offscreen
ChnkOfs: lda SCRATCHPAD+$00 ;if relative position on left side of screen,
checka
bpl ExBCDr ;go ahead and leave
lday Sprite_X_Position,y ;otherwise compare left-side X coordinate
cmpy Sprite_X_Position+4,y ;to right-side X coordinate
cmpcy
bcc ExBCDr ;branch to leave if less
ldan ++$f8 ;otherwise move right half of sprites offscreen
stay Sprite_Y_Position+4,y
stay Sprite_Y_Position+12,y
ExBCDr: rts ;leave
;-------------------------------------------------------------------------------------
DrawFireball:
ldyx FBall_SprDataOffset,x ;get fireball's sprite data offset
lda Fireball_Rel_YPos ;get relative vertical coordinate
stay Sprite_Y_Position,y ;store as sprite Y coordinate
lda Fireball_Rel_XPos ;get relative horizontal coordinate
stay Sprite_X_Position,y ;store as sprite X coordinate, then do shared code
DrawFirebar:
lda FrameCounter ;get frame counter
lsr ;divide by four
lsr
pha ;save result to stack
andn ++$01 ;mask out all but last bit
eorn ++$64 ;set either tile $64 or $65 as fireball tile
stay Sprite_Tilenumber,y ;thus tile changes every four frames
pla ;get from stack
lsr ;divide by four again
lsr
ldan ++$02 ;load value $02 to set palette in attrib byte
bcc FireA ;if last bit shifted out was not set, skip this
oran ++%11000000 ;otherwise flip both ways every eight frames
FireA: stay Sprite_Attributes,y ;store attribute byte and leave
rts
;-------------------------------------------------------------------------------------
ExplosionTiles:
.db $68, $67, $66
DrawExplosion_Fireball:
ldyx Alt_SprDataOffset,x ;get OAM data offset of alternate sort for fireball's explosion
ldax Fireball_State,x ;load fireball state
incx Fireball_State,x ;increment state for next frame
lsr ;divide by 2
andn ++%00000111 ;mask out all but d3-d1
cmpn ++$03 ;check to see if time to kill fireball
cmpcy
bcs KillFireBall ;branch if so, otherwise continue to draw explosion
DrawExplosion_Fireworks:
tax ;use whatever's in A for offset
ldax ExplosionTiles,x ;get tile number using offset
iny ;increment Y (contains sprite data offset)
jsr DumpFourSpr ;and dump into tile number part of sprite data
dey ;decrement Y so we have the proper offset again
ldx ObjectOffset ;return enemy object buffer offset to X
lda Fireball_Rel_YPos ;get relative vertical coordinate
secsub ;subtract four pixels vertically
sbcn ++$04 ;for first and third sprites
stay Sprite_Y_Position,y
stay Sprite_Y_Position+8,y
clc ;add eight pixels vertically
adcn ++$08 ;for second and fourth sprites
stay Sprite_Y_Position+4,y
stay Sprite_Y_Position+12,y
lda Fireball_Rel_XPos ;get relative horizontal coordinate
secsub ;subtract four pixels horizontally
sbcn ++$04 ;for first and second sprites
stay Sprite_X_Position,y
stay Sprite_X_Position+4,y
clc ;add eight pixels horizontally
adcn ++$08 ;for third and fourth sprites
stay Sprite_X_Position+8,y
stay Sprite_X_Position+12,y
ldan ++$02 ;set palette attributes for all sprites, but
stay Sprite_Attributes,y ;set no flip at all for first sprite
ldan ++$82
stay Sprite_Attributes+4,y ;set vertical flip for second sprite
ldan ++$42
stay Sprite_Attributes+8,y ;set horizontal flip for third sprite
ldan ++$c2
stay Sprite_Attributes+12,y ;set both flips for fourth sprite
rts ;we are done
KillFireBall:
ldan ++$00 ;clear fireball state to kill it
stax Fireball_State,x
rts
;-------------------------------------------------------------------------------------
DrawSmallPlatform:
ldyx Enemy_SprDataOffset,x ;get OAM data offset
ldan ++$5b ;load tile number for small platforms
iny ;increment offset for tile numbers
jsr DumpSixSpr ;dump tile number into all six sprites
iny ;increment offset for attributes
ldan ++$02 ;load palette controls
jsr DumpSixSpr ;dump attributes into all six sprites
dey ;decrement for original offset
dey
lda Enemy_Rel_XPos ;get relative horizontal coordinate
stay Sprite_X_Position,y
stay Sprite_X_Position+12,y ;dump as X coordinate into first and fourth sprites
clc
adcn ++$08 ;add eight pixels
stay Sprite_X_Position+4,y ;dump into second and fifth sprites
stay Sprite_X_Position+16,y
clc
adcn ++$08 ;add eight more pixels
stay Sprite_X_Position+8,y ;dump into third and sixth sprites
stay Sprite_X_Position+20,y
ldax Enemy_Y_Position,x ;get vertical coordinate
tax
pha ;save to stack
cpxn ++$20 ;if vertical coordinate below status bar,
cmpcy
bcs TopSP ;do not mess with it
ldan ++$f8 ;otherwise move first three sprites offscreen
TopSP: jsr DumpThreeSpr ;dump vertical coordinate into Y coordinates
pla ;pull from stack
clc
adcn ++$80 ;add 128 pixels
tax
cpxn ++$20 ;if below status bar (taking wrap into account)
cmpcy
bcs BotSP ;then do not change altered coordinate
ldan ++$f8 ;otherwise move last three sprites offscreen
BotSP: stay Sprite_Y_Position+12,y ;dump vertical coordinate + 128 pixels
stay Sprite_Y_Position+16,y ;into Y coordinates
stay Sprite_Y_Position+20,y
lda Enemy_OffscreenBits ;get offscreen bits
pha ;save to stack
andn ++%00001000 ;check d3
beq SOfs
ldan ++$f8 ;if d3 was set, move first and
stay Sprite_Y_Position,y ;fourth sprites offscreen
stay Sprite_Y_Position+12,y
SOfs: pla ;move out and back into stack
pha
andn ++%00000100 ;check d2
beq SOfs2
ldan ++$f8 ;if d2 was set, move second and
stay Sprite_Y_Position+4,y ;fifth sprites offscreen
stay Sprite_Y_Position+16,y
SOfs2: pla ;get from stack
andn ++%00000010 ;check d1
beq ExSPl
ldan ++$f8 ;if d1 was set, move third and
stay Sprite_Y_Position+8,y ;sixth sprites offscreen
stay Sprite_Y_Position+20,y
ExSPl: ldx ObjectOffset ;get enemy object offset and leave
rts
;-------------------------------------------------------------------------------------
DrawBubble:
ldy Player_Y_HighPos ;if player's vertical high position
dey ;not within screen, skip all of this
bne ExDBub
lda Bubble_OffscreenBits ;check air bubble's offscreen bits
andn ++%00001000
bne ExDBub ;if bit set, branch to leave
ldyx Bubble_SprDataOffset,x ;get air bubble's OAM data offset
lda Bubble_Rel_XPos ;get relative horizontal coordinate
stay Sprite_X_Position,y ;store as X coordinate here
lda Bubble_Rel_YPos ;get relative vertical coordinate
stay Sprite_Y_Position,y ;store as Y coordinate here
ldan ++$74
stay Sprite_Tilenumber,y ;put air bubble tile into OAM data
ldan ++$02
stay Sprite_Attributes,y ;set attribute byte
ExDBub: rts ;leave
;-------------------------------------------------------------------------------------
;$00 - used to store player's vertical offscreen bits
PlayerGfxTblOffsets:
.db $20, $28, $c8, $18, $00, $40, $50, $58
.db $80, $88, $b8, $78, $60, $a0, $b0, $b8
;tiles arranged in order, 2 tiles per row, top to bottom
PlayerGraphicsTable:
;big player table
.db $00, $01, $02, $03, $04, $05, $06, $07 ;walking frame 1
.db $08, $09, $0a, $0b, $0c, $0d, $0e, $0f ; frame 2
.db $10, $11, $12, $13, $14, $15, $16, $17 ; frame 3
.db $18, $19, $1a, $1b, $1c, $1d, $1e, $1f ;skidding
.db $20, $21, $22, $23, $24, $25, $26, $27 ;jumping
.db $08, $09, $28, $29, $2a, $2b, $2c, $2d ;swimming frame 1
.db $08, $09, $0a, $0b, $0c, $30, $2c, $2d ; frame 2
.db $08, $09, $0a, $0b, $2e, $2f, $2c, $2d ; frame 3
.db $08, $09, $28, $29, $2a, $2b, $5c, $5d ;climbing frame 1
.db $08, $09, $0a, $0b, $0c, $0d, $5e, $5f ; frame 2
.db $fc, $fc, $08, $09, $58, $59, $5a, $5a ;crouching
.db $08, $09, $28, $29, $2a, $2b, $0e, $0f ;fireball throwing
;small player table
.db $fc, $fc, $fc, $fc, $32, $33, $34, $35 ;walking frame 1
.db $fc, $fc, $fc, $fc, $36, $37, $38, $39 ; frame 2
.db $fc, $fc, $fc, $fc, $3a, $37, $3b, $3c ; frame 3
.db $fc, $fc, $fc, $fc, $3d, $3e, $3f, $40 ;skidding
.db $fc, $fc, $fc, $fc, $32, $41, $42, $43 ;jumping
.db $fc, $fc, $fc, $fc, $32, $33, $44, $45 ;swimming frame 1
.db $fc, $fc, $fc, $fc, $32, $33, $44, $47 ; frame 2
.db $fc, $fc, $fc, $fc, $32, $33, $48, $49 ; frame 3
.db $fc, $fc, $fc, $fc, $32, $33, $90, $91 ;climbing frame 1
.db $fc, $fc, $fc, $fc, $3a, $37, $92, $93 ; frame 2
.db $fc, $fc, $fc, $fc, $9e, $9e, $9f, $9f ;killed
;used by both player sizes
.db $fc, $fc, $fc, $fc, $3a, $37, $4f, $4f ;small player standing
.db $fc, $fc, $00, $01, $4c, $4d, $4e, $4e ;intermediate grow frame
.db $00, $01, $4c, $4d, $4a, $4a, $4b, $4b ;big player standing
SwimKickTileNum:
.db $31, $46
PlayerGfxHandler:
lda InjuryTimer ;if player's injured invincibility timer
checka
beq CntPl ;not set, skip checkpoint and continue code
lda FrameCounter
lsr ;otherwise check frame counter and branch
bcs ExPGH ;to leave on every other frame (when d0 is set)
CntPl: lda GameEngineSubroutine ;if executing specific game engine routine,
cmpn ++$0b ;branch ahead to some other part
beq PlayerKilled
lda PlayerChangeSizeFlag ;if grow/shrink flag set
checka
bne DoChangeSize ;then branch to some other code
ldy SwimmingFlag ;if swimming flag set, branch to
checky
beq FindPlayerAction ;different part, do not return
lda Player_State
cmpn ++$00 ;if player status normal,
beq FindPlayerAction ;branch and do not return
jsr FindPlayerAction ;otherwise jump and return
lda FrameCounter
andn ++%00000100 ;check frame counter for d2 set (8 frames every
bne ExPGH ;eighth frame), and branch if set to leave
tax ;initialize X to zero
ldy Player_SprDataOffset ;get player sprite data offset
lda PlayerFacingDir ;get player's facing direction
lsr
bcs SwimKT ;if player facing to the right, use current offset
iny
iny ;otherwise move to next OAM data
iny
iny
SwimKT: lda PlayerSize ;check player's size
checka
beq BigKTS ;if big, use first tile
lday Sprite_Tilenumber+24,y ;check tile number of seventh/eighth sprite
cmpi SwimTileRepOffset ;against tile number in player graphics table
beq ExPGH ;if spr7/spr8 tile number = value, branch to leave
inx ;otherwise increment X for second tile
BigKTS: ldax SwimKickTileNum,x ;overwrite tile number in sprite 7/8
stay Sprite_Tilenumber+24,y ;to animate player's feet when swimming
ExPGH: rts ;then leave
FindPlayerAction:
jsr ProcessPlayerAction ;find proper offset to graphics table by player's actions
jmp PlayerGfxProcessing ;draw player, then process for fireball throwing
DoChangeSize:
jsr HandleChangeSize ;find proper offset to graphics table for grow/shrink
jmp PlayerGfxProcessing ;draw player, then process for fireball throwing
PlayerKilled:
ldyn ++$0e ;load offset for player killed
lday PlayerGfxTblOffsets,y ;get offset to graphics table
PlayerGfxProcessing:
sta PlayerGfxOffset ;store offset to graphics table here
ldan ++$04
jsr RenderPlayerSub ;draw player based on offset loaded
jsr ChkForPlayerAttrib ;set horizontal flip bits as necessary
lda FireballThrowingTimer
checka
beq PlayerOffscreenChk ;if fireball throw timer not set, skip to the end
ldyn ++$00 ;set value to initialize by default
lda PlayerAnimTimer ;get animation frame timer
cmpi FireballThrowingTimer ;compare to fireball throw timer
sty FireballThrowingTimer ;initialize fireball throw timer
cmpcy
bcs PlayerOffscreenChk ;if animation frame timer => fireball throw timer skip to end
sta FireballThrowingTimer ;otherwise store animation timer into fireball throw timer
ldyn ++$07 ;load offset for throwing
lday PlayerGfxTblOffsets,y ;get offset to graphics table
sta PlayerGfxOffset ;store it for use later
ldyn ++$04 ;set to update four sprite rows by default
lda Player_X_Speed
orai Left_Right_Buttons ;check for horizontal speed or left/right button press
beq SUpdR ;if no speed or button press, branch using set value in Y
dey ;otherwise set to update only three sprite rows
SUpdR: tya ;save in A for use
jsr RenderPlayerSub ;in sub, draw player object again
PlayerOffscreenChk:
lda Player_OffscreenBits ;get player's offscreen bits
lsr
lsr ;move vertical bits to low nybble
lsr
lsr
sta SCRATCHPAD+$00 ;store here
ldxn ++$03 ;check all four rows of player sprites
lda Player_SprDataOffset ;get player's sprite data offset
clc
adcn ++$18 ;add 24 bytes to start at bottom row
tay ;set as offset here
PROfsLoop: ldan ++$f8 ;load offscreen Y coordinate just in case
lsri SCRATCHPAD+$00 ;shift bit into carry
bcc NPROffscr ;if bit not set, skip, do not move sprites
jsr DumpTwoSpr ;otherwise dump offscreen Y coordinate into sprite data
NPROffscr: tya
secsub ;subtract eight bytes to do
sbcn ++$08 ;next row up
tay
dex ;decrement row counter
bpl PROfsLoop ;do this until all sprite rows are checked
rts ;then we are done!
;-------------------------------------------------------------------------------------
IntermediatePlayerData:
.db $58, $01, $00, $60, $ff, $04
DrawPlayer_Intermediate:
if Z80OPT
ld hl,IntermediatePlayerData
ld de,SCRATCHPAD+$02
ld c,6
ldir
ld d,b;0
ld e,++$04 ;load sprite data offset
ld hl,PlayerGraphicsTable+$b8 ;load offset for small standing
;add hl,bc
call DrawPlayerLoop ;draw player accordingly
else
ldxn ++$05 ;store data into zero page memory
PIntLoop: ldax IntermediatePlayerData,x ;load data to display player as he always
stax SCRATCHPAD+$02,x ;appears on world/lives display
dex
bpl PIntLoop ;do this until all data is loaded
ldxn ++$b8 ;load offset for small standing
ldyn ++$04 ;load sprite data offset
jsr DrawPlayerLoop ;draw player accordingly
endif
lda Sprite_Attributes+36 ;get empty sprite attributes
oran ++%01000000 ;set horizontal flip bit for bottom-right sprite
sta Sprite_Attributes+32 ;store and leave
rts
;-------------------------------------------------------------------------------------
;$00-$01 - used to hold tile numbers, $00 also used to hold upper extent of animation frames
;$02 - vertical position
;$03 - facing direction, used as horizontal flip control
;$04 - attributes
;$05 - horizontal position
;$07 - number of rows to draw
;these also used in IntermediatePlayerData
RenderPlayerSub:
sta SCRATCHPAD+$07 ;store number of rows of sprites to draw
lda Player_Rel_XPos
sta Player_Pos_ForScroll ;store player's relative horizontal position
sta SCRATCHPAD+$05 ;store it here also
lda Player_Rel_YPos
sta SCRATCHPAD+$02 ;store player's vertical position
lda PlayerFacingDir
sta SCRATCHPAD+$03 ;store player's facing direction
lda Player_SprAttrib
sta SCRATCHPAD+$04 ;store player's sprite attributes
ldx PlayerGfxOffset ;load graphics table offset
ldy Player_SprDataOffset ;get player's sprite data offset
if Z80OPT
ld hl,PlayerGraphicsTable
add hl,bc
DrawPlayerLoop:
ld a,(SCRATCHPAD+$07)
ld b,a
DrawPlayerLoop0
call DrawSpriteObject
djnz DrawPlayerLoop0 ;do this until all rows are drawn
else
DrawPlayerLoop:
DrawPlayerLoop0
ldax PlayerGraphicsTable,x ;load player's left side
sta SCRATCHPAD+$00
ldax PlayerGraphicsTable+1,x ;now load right side
jsr DrawOneSpriteRow
deci SCRATCHPAD+$07 ;decrement rows of sprites to draw
bne DrawPlayerLoop0 ;do this until all rows are drawn
endif
rts
ProcessPlayerAction:
lda Player_State ;get player's state
cmpn ++$03
beq ActionClimbing ;if climbing, branch here
cmpn ++$02
beq ActionFalling ;if falling, branch here
cmpn ++$01
bne ProcOnGroundActs ;if not jumping, branch here
lda SwimmingFlag
checka
bne ActionSwimming ;if swimming flag set, branch elsewhere
ldyn ++$06 ;load offset for crouching
lda CrouchingFlag ;get crouching flag
checka
bne NonAnimatedActs ;if set, branch to get offset for graphics table
ldyn ++$00 ;otherwise load offset for jumping
jmp NonAnimatedActs ;go to get offset to graphics table
ProcOnGroundActs:
ldyn ++$06 ;load offset for crouching
lda CrouchingFlag ;get crouching flag
checka
bne NonAnimatedActs ;if set, branch to get offset for graphics table
ldyn ++$02 ;load offset for standing
lda Player_X_Speed ;check player's horizontal speed
orai Left_Right_Buttons ;and left/right controller bits
beq NonAnimatedActs ;if no speed or buttons pressed, use standing offset
lda Player_XSpeedAbsolute ;load walking/running speed
cmpn ++$09
cmpcy
bcc ActionWalkRun ;if less than a certain amount, branch, too slow to skid
lda Player_MovingDir ;otherwise check to see if moving direction
andi PlayerFacingDir ;and facing direction are the same
bne ActionWalkRun ;if moving direction = facing direction, branch, don't skid
iny ;otherwise increment to skid offset ($03)
NonAnimatedActs:
jsr GetGfxOffsetAdder ;do a sub here to get offset adder for graphics table
ldan ++$00
sta PlayerAnimCtrl ;initialize animation frame control
lday PlayerGfxTblOffsets,y ;load offset to graphics table using size as offset
rts
ActionFalling:
ldyn ++$04 ;load offset for walking/running
jsr GetGfxOffsetAdder ;get offset to graphics table
jmp GetCurrentAnimOffset ;execute instructions for falling state
ActionWalkRun:
ldyn ++$04 ;load offset for walking/running
jsr GetGfxOffsetAdder ;get offset to graphics table
jmp FourFrameExtent ;execute instructions for normal state
ActionClimbing:
ldyn ++$05 ;load offset for climbing
lda Player_Y_Speed ;check player's vertical speed
checka
beq NonAnimatedActs ;if no speed, branch, use offset as-is
jsr GetGfxOffsetAdder ;otherwise get offset for graphics table
jmp ThreeFrameExtent ;then skip ahead to more code
ActionSwimming:
ldyn ++$01 ;load offset for swimming
jsr GetGfxOffsetAdder
lda JumpSwimTimer ;check jump/swim timer
orai PlayerAnimCtrl ;and animation frame control
bne FourFrameExtent ;if any one of these set, branch ahead
lda A_B_Buttons
asl ;check for A button pressed
bcs FourFrameExtent ;branch to same place if A button pressed
GetCurrentAnimOffset:
lda PlayerAnimCtrl ;get animation frame control
jmp GetOffsetFromAnimCtrl ;jump to get proper offset to graphics table
FourFrameExtent:
ldan ++$03 ;load upper extent for frame control
jmp AnimationControl ;jump to get offset and animate player object
ThreeFrameExtent:
ldan ++$02 ;load upper extent for frame control for climbing
AnimationControl:
sta SCRATCHPAD+$00 ;store upper extent here
jsr GetCurrentAnimOffset ;get proper offset to graphics table
pha ;save offset to stack
lda PlayerAnimTimer ;load animation frame timer
checka
bne ExAnimC ;branch if not expired
lda PlayerAnimTimerSet ;get animation frame timer amount
sta PlayerAnimTimer ;and set timer accordingly
lda PlayerAnimCtrl
clc ;add one to animation frame control
adcn ++$01
cmpi SCRATCHPAD+$00 ;compare to upper extent
cmpcy
bcc SetAnimC ;if frame control + 1 < upper extent, use as next
ldan ++$00 ;otherwise initialize frame control
SetAnimC: sta PlayerAnimCtrl ;store as new animation frame control
ExAnimC: pla ;get offset to graphics table from stack and leave
rts
GetGfxOffsetAdder:
lda PlayerSize ;get player's size
checka
beq SzOfs ;if player big, use current offset as-is
tya ;for big player
clc ;otherwise add eight bytes to offset
adcn ++$08 ;for small player
tay
SzOfs: rts ;go back
ChangeSizeOffsetAdder:
.db $00, $01, $00, $01, $00, $01, $02, $00, $01, $02
.db $02, $00, $02, $00, $02, $00, $02, $00, $02, $00
HandleChangeSize:
ldy PlayerAnimCtrl ;get animation frame control
lda FrameCounter
andn ++%00000011 ;get frame counter and execute this code every
bne GorSLog ;fourth frame, otherwise branch ahead
iny ;increment frame control
cpyn ++$0a ;check for preset upper extent
cmpcy
bcc CSzNext ;if not there yet, skip ahead to use
ldyn ++$00 ;otherwise initialize both grow/shrink flag
sty PlayerChangeSizeFlag ;and animation frame control
CSzNext: sty PlayerAnimCtrl ;store proper frame control
GorSLog: lda PlayerSize ;get player's size
checka
bne ShrinkPlayer ;if player small, skip ahead to next part
lday ChangeSizeOffsetAdder,y ;get offset adder based on frame control as offset
ldyn ++$0f ;load offset for player growing
GetOffsetFromAnimCtrl:
asl ;multiply animation frame control
asl ;by eight to get proper amount
asl ;to add to our offset
adcy PlayerGfxTblOffsets,y ;add to offset to graphics table
rts ;and return with result in A
ShrinkPlayer:
tya ;add ten bytes to frame control as offset
clc
adcn ++$0a ;this thing apparently uses two of the swimming frames
tax ;to draw the player shrinking
ldyn ++$09 ;load offset for small player swimming
ldax ChangeSizeOffsetAdder,x ;get what would normally be offset adder
checka
bne ShrPlF ;and branch to use offset if nonzero
ldyn ++$01 ;otherwise load offset for big player swimming
ShrPlF: lday PlayerGfxTblOffsets,y ;get offset to graphics table based on offset loaded
rts ;and leave
ChkForPlayerAttrib:
ldy Player_SprDataOffset ;get sprite data offset
lda GameEngineSubroutine
cmpn ++$0b ;if executing specific game engine routine,
beq KilledAtt ;branch to change third and fourth row OAM attributes
lda PlayerGfxOffset ;get graphics table offset
cmpn ++$50
beq C_S_IGAtt ;if crouch offset, either standing offset,
cmpn ++$b8 ;or intermediate growing offset,
beq C_S_IGAtt ;go ahead and execute code to change
cmpn ++$c0 ;fourth row OAM attributes only
beq C_S_IGAtt
cmpn ++$c8
bne ExPlyrAt ;if none of these, branch to leave
KilledAtt: lday Sprite_Attributes+16,y
andn ++%00111111 ;mask out horizontal and vertical flip bits
stay Sprite_Attributes+16,y ;for third row sprites and save
lday Sprite_Attributes+20,y
andn ++%00111111
oran ++%01000000 ;set horizontal flip bit for second
stay Sprite_Attributes+20,y ;sprite in the third row
C_S_IGAtt: lday Sprite_Attributes+24,y
andn ++%00111111 ;mask out horizontal and vertical flip bits
stay Sprite_Attributes+24,y ;for fourth row sprites and save
lday Sprite_Attributes+28,y
andn ++%00111111
oran ++%01000000 ;set horizontal flip bit for second
stay Sprite_Attributes+28,y ;sprite in the fourth row
ExPlyrAt: rts ;leave
;-------------------------------------------------------------------------------------
;$00 - used in adding to get proper offset
RelativePlayerPosition:
ldxn ++$00 ;set offsets for relative cooordinates
ldyn ++$00 ;routine to correspond to player object
jmp RelWOfs ;get the coordinates
RelativeBubblePosition:
ldyn ++$01 ;set for air bubble offsets
jsr GetProperObjOffset ;modify X to get proper air bubble offset
ldyn ++$03
jmp RelWOfs ;get the coordinates
RelativeFireballPosition:
ldyn ++$00 ;set for fireball offsets
jsr GetProperObjOffset ;modify X to get proper fireball offset
ldyn ++$02
RelWOfs: jsr GetObjRelativePosition ;get the coordinates
ldx ObjectOffset ;return original offset
rts ;leave
RelativeMiscPosition:
ldyn ++$02 ;set for misc object offsets
jsr GetProperObjOffset ;modify X to get proper misc object offset
ldyn ++$06
jmp RelWOfs ;get the coordinates
RelativeEnemyPosition:
ldan ++$01 ;get coordinates of enemy object
ldyn ++$01 ;relative to the screen
jmp VariableObjOfsRelPos
RelativeBlockPosition:
ldan ++$09 ;get coordinates of one block object
ldyn ++$04 ;relative to the screen
jsr VariableObjOfsRelPos
inx ;adjust offset for other block object if any
inx
ldan ++$09
iny ;adjust other and get coordinates for other one
VariableObjOfsRelPos:
stx SCRATCHPAD+$00 ;store value to add to A here
clc
adci SCRATCHPAD+$00 ;add A to value stored
tax ;use as enemy offset
jsr GetObjRelativePosition
ldx ObjectOffset ;reload old object offset and leave
rts
GetObjRelativePosition:
ldax SprObject_Y_Position,x ;load vertical coordinate low
stay SprObject_Rel_YPos,y ;store here
ldax SprObject_X_Position,x ;load horizontal coordinate
secsub ;subtract left edge coordinate
sbci ScreenLeft_X_Pos
stay SprObject_Rel_XPos,y ;store result here
rts
;-------------------------------------------------------------------------------------
;$00 - used as temp variable to hold offscreen bits
GetPlayerOffscreenBits:
ldxn ++$00 ;set offsets for player-specific variables
ldyn ++$00 ;and get offscreen information about player
jmp GetOffScreenBitsSet
GetFireballOffscreenBits:
ldyn ++$00 ;set for fireball offsets
jsr GetProperObjOffset ;modify X to get proper fireball offset
ldyn ++$02 ;set other offset for fireball's offscreen bits
jmp GetOffScreenBitsSet ;and get offscreen information about fireball
GetBubbleOffscreenBits:
ldyn ++$01 ;set for air bubble offsets
jsr GetProperObjOffset ;modify X to get proper air bubble offset
ldyn ++$03 ;set other offset for airbubble's offscreen bits
jmp GetOffScreenBitsSet ;and get offscreen information about air bubble
GetMiscOffscreenBits:
ldyn ++$02 ;set for misc object offsets
jsr GetProperObjOffset ;modify X to get proper misc object offset
ldyn ++$06 ;set other offset for misc object's offscreen bits
jmp GetOffScreenBitsSet ;and get offscreen information about misc object
ObjOffsetData:
.db $07, $16, $0d
GetProperObjOffset:
txa ;move offset to A
clc
adcy ObjOffsetData,y ;add amount of bytes to offset depending on setting in Y
tax ;put back in X and leave
rts
GetEnemyOffscreenBits:
if Z80OPT ;???эх яюьюурхЄ т 1-2
inc c
call RunOffscrBitsSubs ;a=$00, $08, $0c, $0e, $0f, $07, $03, $01, $00
add a,a ;move low nybble to high nybble
add a,a
add a,a
add a,a
ld hl,SCRATCHPAD+$00
or (hl) ;mask together with previously saved low nybble
ld (SprObject_OffscrBits+1),a
ld hl,ObjectOffset
ld c,(hl)
ret
else
ldan ++$01 ;set A to add 1 byte in order to get enemy offset
ldyn ++$01 ;set Y to put offscreen bits in Enemy_OffscreenBits
jmp SetOffscrBitsOffset
endif
GetBlockOffscreenBits:
ldan ++$09 ;set A to add 9 bytes in order to get block obj offset
ldyn ++$04 ;set Y to put offscreen bits in Block_OffscreenBits
SetOffscrBitsOffset:
stx SCRATCHPAD+$00
clc ;add contents of X to A to get
adci SCRATCHPAD+$00 ;appropriate offset, then give back to X
tax
GetOffScreenBitsSet:
tya ;save offscreen bits offset to stack for now
pha
jsr RunOffscrBitsSubs ;a=$00, $08, $0c, $0e, $0f, $07, $03, $01, $00
asl ;move low nybble to high nybble
asl
asl
asl
orai SCRATCHPAD+$00 ;mask together with previously saved low nybble
sta SCRATCHPAD+$00 ;store both here
pla ;get offscreen bits offset from stack
tay
lda SCRATCHPAD+$00 ;get value here and store elsewhere
stay SprObject_OffscrBits,y
ldx ObjectOffset
rts
RunOffscrBitsSubs:
;2 calls
;x эр т√їюфх эх трцхэ, трцэю SCRATCHPAD+$00 [ш +$07(???)]
if Z80OPT ;???
call GetXOffscreenBits ;do subroutine here
rra ;move high nybble to low
rra
rra
rra ;TODO єсЁрЄ№ т GetXOffscreenBits
and 0x0f
ld (SCRATCHPAD+$00),a ;store here
;test top of screen
;ld c,0
xor a ;load coordinate for edge of vertical unit ;.db $ff, $00
sub (ix+SprObject_Y_Position-SprObject_X_Position) ;subtract from vertical coordinate of object
ld l,a;sta SCRATCHPAD+$07 ;store here
ld a,++$01 ;subtract one from vertical high byte of object
sbc a,(ix+SprObject_Y_HighPos-SprObject_X_Position)
; jp m,YLdBData ;if under top of the screen or beyond bottom, branch
jp m,YLdBbottom ;if under top of the screen or beyond bottom
ld c,4 ;if not, load alternate offset value here (y+DefaultYOnscreenOfs+1) ;.db $04, $00, $04
jp nz,YLdBData ;if one vertical unit or more above the screen, branch ;ЄєЄ Єюўэю т√їюф
;с√ыю dyhigh==0
ld a,l;lda SCRATCHPAD+$07 ;get pixel difference
cp 32 ;cmpi SCRATCHPAD+$06 ;compare to preset value
jr nc,YLdBData ;if pixel difference HIGH = preset value, branch ;шёяюы№чєхЄ c=x=4 ;ЄєЄ Єюўэю т√їюф
rra ;divide by eight
rra
rra ;т ьырф°шї сшЄрї: 0..3
and 3 ;andn ++$07 ;mask out all but 3 LSB ;с√ыю эх 2 сшЄр, яюЄюьє ўЄю ¤Єю с√ыр юс∙р яЁюЎхфєЁр фы X ш Y
ld c,a;tax ;use as offset
YLdBData:
ld hl,YOffscreenBitsData ;.db $00, $08, $0c, $0e, $0f, $07, $03, $01, $00 ;чрўхь яюёыхфэшщ 0?
add hl,bc
ld a,(hl) ;get offscreen data bits using offset
or a
ret nz ;if bits not zero, branch to leave
;test bottom of screen
YLdBbottom:
;ld c,4
ld a,-1 ;load coordinate for edge of vertical unit ;.db $ff, $00
sub (ix+SprObject_Y_Position-SprObject_X_Position) ;subtract from vertical coordinate of object
ld l,a;sta SCRATCHPAD+$07 ;store here
ld a,++$01 ;subtract one from vertical high byte of object
sbc a,(ix+SprObject_Y_HighPos-SprObject_X_Position)
; jp m,YLdBDatabottom ;if under top of the screen or beyond bottom, branch
ld a,15
ret m ;if under top of the screen or beyond bottom
;ld c,0 ;if not, load alternate offset value here (y+DefaultYOnscreenOfs+1) ;.db $04, $00, $04
ld c,8-4 ;фы єёъюЁхэш ўхЁхч ёфтшу ЄрсышЎ√
jp nz,YLdBDatabottom ;if one vertical unit or more above the screen, branch
;с√ыю dyHSB==0
ld a,l;lda SCRATCHPAD+$07 ;get pixel difference
cp 32 ;cmpi SCRATCHPAD+$06 ;compare to preset value
jr nc,YLdBDatabottom ;if pixel difference HIGH = preset value, branch ;шёяюы№чєхЄ c=x=0
rra ;divide by eight
rra
rra ;т ьырф°шї сшЄрї: 0..3
and 3 ;andn ++$07 ;mask out all but 3 LSB ;с√ыю эх 2 сшЄр, яюЄюьє ўЄю ¤Єю с√ыр юс∙р яЁюЎхфєЁр фы X ш Y
;add a,4
ld c,a;tax ;use as offset
YLdBDatabottom:
ld hl,YOffscreenBitsData +4 ;.db $00, $08, $0c, $0e, $0f, $07, $03, $01, $00 ;чрўхь яюёыхфэшщ 0? шёяюы№чєхь хую фы єёъюЁхэш
add hl,bc
ld a,(hl) ;get offscreen data bits using offset
ret
else ;~Z80
jsr GetXOffscreenBits ;do subroutine here
lsr ;move high nybble to low
lsr
lsr
lsr
sta SCRATCHPAD+$00 ;store here
jmp GetYOffscreenBits
endif
;--------------------------------
;(these apply to these three subsections)
;$04 - used to store proper offset
;$05 - used as adder in DividePDiff
;$06 - used to store preset value used to compare to pixel difference in $07
;$07 - used to store difference between coordinates of object and screen edges
;TODO ърцфюх чэрўхэшх 8 Ёрч, ўЄюс√ эх ёфтшурЄ№
XOffscreenBitsData:
.db $7f, $3f, $1f, $0f, $07, $03, $01, $00
.db $80, $c0, $e0, $f0, $f8, $fc, $fe, $ff
DefaultXOnscreenOfs:
.db $07, $0f, $07
GetXOffscreenBits:
;TODO >>4?
if Z80OPT
ld ix,SprObject_X_Position
add ix,bc
;right side of screen
ld a,(ScreenEdge_X_Pos+1) ;get pixel coordinate of edge ;т яхЁхьхээ√ї!!!
;ld c,15
sub (ix) ;get difference between pixel coordinate of edge and pixel coordinate of object position
ld l,a ;sta SCRATCHPAD+$07 ;store here
ld a,(ScreenEdge_PageLoc+1) ;get page location of edge ;т яхЁхьхээ√ї!!!
sbc a,(ix+SprObject_PageLoc-SprObject_X_Position) ;subtract from page location of object position
;jp m,XLdBData ;if beyond right edge or in front of left edge, branch ;ЄєЄ Єюўэю т√їюф
;ld a,0xff
; ret m ;if beyond right edge or in front of left edge, branch
jp m,XLdBDataleftq ;if beyond right edge or in front of left edge, branch
;ld c,7 ;ldxy DefaultXOnscreenOfs+1,y ;if not, load alternate offset value here ;.db $07, $0f, $07
jp nz,XLdBleft;XLdBData ;if one page or more to the left of either edge, branch
;с√ыю dxHSB==0
ld a,l ;lda SCRATCHPAD+$07 ;get pixel difference
cp 56 ;cmpi SCRATCHPAD+$06 ;compare to preset value
jr nc,XLdBleft;XLdBData ;if pixel difference >= preset value, branch
rra ;divide by eight
rra
rra ;т ьырф°шї сшЄрї: 0..6
and ++$07 ;mask out all but 3 LSB
ld c,a;tax ;use as offset
;XLdBData:
ld hl,XOffscreenBitsData
add hl,bc
ld a,(hl) ;.db $7f, $3f, $1f, $0f, $07, $03, $01, $00, $80, $c0, $e0, $f0, $f8, $fc, $fe, $ff
or a ;if bits not zero, branch to leave
ret nz
;left side of screen
XLdBleft
ld a,(ScreenEdge_X_Pos) ;get pixel coordinate of edge ;т яхЁхьхээ√ї!!!
ld c,7
sub (ix) ;get difference between pixel coordinate of edge and pixel coordinate of object position
ld l,a ;sta SCRATCHPAD+$07 ;store here
ld a,(ScreenEdge_PageLoc) ;get page location of edge ;т яхЁхьхээ√ї!!!
sbc a,(ix+SprObject_PageLoc-SprObject_X_Position) ;subtract from page location of object position
jp m,XLdBDataleft ;if beyond right edge or in front of left edge, branch
;ld c,15 ;ldxy DefaultXOnscreenOfs+1,y ;if not, load alternate offset value here ;.db $07, $0f, $07
jp nz,XLdBDataleftq;XLdBDataleft ;if one page or more to the left of either edge, branch ;ЄєЄ Єюўэю т√їюф
;с√ыю dxHSB==0
ld a,l ;lda SCRATCHPAD+$07 ;get pixel difference
cp 56 ;cmpi SCRATCHPAD+$06 ;compare to preset value
jr nc,XLdBDataleftq;XLdBDataleft ;if pixel difference >= preset value, branch ;шёяюы№чєхЄ x=c=15 ;ЄєЄ Єюўэю т√їюф
rra ;divide by eight
rra
rra ;т ьырф°шї сшЄрї: 0..6
and ++$07 ;mask out all but 3 LSB
add a,8
ld c,a;tax ;use as offset
XLdBDataleft:
ld hl,XOffscreenBitsData
add hl,bc
ld a,(hl) ;.db $7f, $3f, $1f, $0f, $07, $03, $01, $00, $80, $c0, $e0, $f0, $f8, $fc, $fe, $ff
ret
XLdBDataleftq
ld a,0xff
ret
else ;~Z80
;keep x!
stx SCRATCHPAD+$04 ;save position in buffer to here
ldyn ++$01 ;start with right side of screen
XOfsLoop: lday ScreenEdge_X_Pos,y ;get pixel coordinate of edge
secsub ;get difference between pixel coordinate of edge
sbcx SprObject_X_Position,x ;and pixel coordinate of object position
sta SCRATCHPAD+$07 ;store here
ldaykeepcy ScreenEdge_PageLoc,y ;get page location of edge
sbcx SprObject_PageLoc,x ;subtract from page location of object position
ldxy DefaultXOnscreenOfs,y ;load offset value here
cmpn ++$00
bmi XLdBData ;if beyond right edge or in front of left edge, branch
ldxy DefaultXOnscreenOfs+1,y ;if not, load alternate offset value here
cmpn ++$01
bpl XLdBData ;if one page or more to the left of either edge, branch
ldan ++$38 ;if no branching, load value here and store
sta SCRATCHPAD+$06
ldan ++$08 ;load some other value and execute subroutine
jsr DividePDiff
XLdBData: ldax XOffscreenBitsData,x ;get bits here
ldx SCRATCHPAD+$04 ;reobtain position in buffer
cmpn ++$00 ;if bits not zero, branch to leave
bne ExXOfsBS
dey ;otherwise, do left side of screen now
bpl XOfsLoop ;branch if not already done with left side ;2 яЁюїюфр
ExXOfsBS: rts
endif
;--------------------------------
;TODO ърцфюх чэрўхэшх 8 Ёрч, ўЄюс√ эх ёфтшурЄ№
YOffscreenBitsData:
.db $00, $08, $0c, $0e
.db $0f, $07, $03, $01
.db $00 ;чрўхь юэ с√ы эєцхэ? (т Z80 ЄхяхЁ№ эєцхэ)
DefaultYOnscreenOfs:
.db $04, $00, $04
HighPosUnitData:
.db $ff, $00
if Z80OPT
else
GetYOffscreenBits:
stx SCRATCHPAD+$04 ;save position in buffer to here
ldyn ++$01 ;start with top of screen
YOfsLoop0: lday HighPosUnitData,y ;load coordinate for edge of vertical unit
secsub
sbcx SprObject_Y_Position,x ;subtract from vertical coordinate of object
sta SCRATCHPAD+$07 ;store here
ldan ++$01 ;subtract one from vertical high byte of object
sbcx SprObject_Y_HighPos,x
ldxy DefaultYOnscreenOfs,y ;load offset value here
cmpn ++$00
bmi YLdBData ;if under top of the screen or beyond bottom, branch
ldxy DefaultYOnscreenOfs+1,y ;if not, load alternate offset value here
cmpn ++$01
bpl YLdBData ;if one vertical unit or more above the screen, branch
ldan ++$20 ;if no branching, load value here and store
sta SCRATCHPAD+$06
ldan ++$04 ;load some other value and execute subroutine
jsr DividePDiff
YLdBData: ldax YOffscreenBitsData,x ;get offscreen data bits using offset
ldx SCRATCHPAD+$04 ;reobtain position in buffer
cmpn ++$00
bne ExYOfsBS ;if bits not zero, branch to leave
dey ;otherwise, do bottom of the screen now
bpl YOfsLoop0
ExYOfsBS: rts
endif
;--------------------------------
DividePDiff:
;a = 4 (Y) / 8 (X)
;y = эюьхЁ яЁюїюфр т ъююЁфшэрЄх (0 = ыхтю/эшч, 1 = яЁртю/тхЁї)
;out: x
if Z80OPT
ld ly,a;sta SCRATCHPAD+$05 ;store current value in A here
ld hl,(SCRATCHPAD+$06)
ld a,h ;lda SCRATCHPAD+$07 ;get pixel difference
cp l ;cmpi SCRATCHPAD+$06 ;compare to preset value
ret nc ;if pixel difference >= preset value, branch
rra ;divide by eight
rra
rra
and ++$07 ;mask out all but 3 LSB
inc e ;right side of the screen or top?
dec e
jr nz,SetOscrO ;if so, branch, use difference / 8 as offset
add a,ly; SCRATCHPAD+$05 ;if not, add value to difference / 8
SetOscrO: ld c,a ;use as offset
ret
else
sta SCRATCHPAD+$05 ;store current value in A here
lda SCRATCHPAD+$07 ;get pixel difference
cmpi SCRATCHPAD+$06 ;compare to preset value
cmpcy
bcs ExDivPD ;if pixel difference >= preset value, branch
lsr ;divide by eight
lsr
lsr
andn ++$07 ;mask out all but 3 LSB
cpyn ++$01 ;right side of the screen or top?
cmpcy
bcs SetOscrO ;if so, branch, use difference / 8 as offset
adci SCRATCHPAD+$05 ;if not, add value to difference / 8
SetOscrO: tax ;use as offset
ExDivPD: rts ;leave
endif
;-------------------------------------------------------------------------------------
;$00-$01 - tile numbers (hl, out: hl+=2)
;$02 - Y coordinate
;$03 - flip control
;$04 - sprite attributes
;$05 - X coordinate
;y = Sprite_Data offset
;out: [x+=2], y+=8
DrawSpriteObject:
if Z80OPT
ld ix,Sprite_Data
add ix,de
lda SCRATCHPAD+$03 ;get saved flip control bits
rra
rra ;move d1 into carry
;ld hl,(SCRATCHPAD+$00)
ld a,(hl)
jr nc,NoHFlip ;if d1 not set, branch
ld (ix+Sprite_Tilenumber-Sprite_Data+4),a ;store first tile into second sprite
inc hl
ld a,(hl)
ld (ix+Sprite_Tilenumber-Sprite_Data),a ;and second into first sprite
ld a,(SCRATCHPAD+$04)
or 0x40 ;activate horizontal flip OAM attribute
jp SetHFAt ;and unconditionally branch
NoHFlip: ld (ix+Sprite_Tilenumber-Sprite_Data),a ;store first tile into first sprite
inc hl
ld a,(hl)
ld (ix+Sprite_Tilenumber-Sprite_Data+4),a ;and second into second sprite
ld a,(SCRATCHPAD+$04) ;схч +0x40 OAM attribute
SetHFAt:
ld (ix+Sprite_Attributes-Sprite_Data),a ;store sprite attributes
ld (ix+Sprite_Attributes-Sprite_Data+4),a
ld a,(SCRATCHPAD+$02) ;now the y coordinates
ld (ix+Sprite_Y_Position-Sprite_Data),a ;note because they are
ld (ix+Sprite_Y_Position-Sprite_Data+4),a ;side by side, they are the same
add a,8 ;add eight pixels to the next y coordinate
ld (SCRATCHPAD+$02),a
ld a,(SCRATCHPAD+$05)
ld (ix+Sprite_X_Position-Sprite_Data),a ;store x coordinate, then
add a,8 ;add 8 pixels and store another to
ld (ix+Sprite_X_Position-Sprite_Data+4),a ;put them side by side
ld a,e ;add eight to the offset in Y to
add a,8 ;move to the next two sprites
ld e,a
inc hl
else
lda SCRATCHPAD+$03 ;get saved flip control bits
lsr
lsr ;move d1 into carry
lda SCRATCHPAD+$00
bcc NoHFlip ;if d1 not set, branch
stay Sprite_Tilenumber+4,y ;store first tile into second sprite
lda SCRATCHPAD+$01 ;and second into first sprite
stay Sprite_Tilenumber,y
ldan ++$40 ;activate horizontal flip OAM attribute
checka
bne SetHFAt ;and unconditionally branch
NoHFlip: stay Sprite_Tilenumber,y ;store first tile into first sprite
lda SCRATCHPAD+$01 ;and second into second sprite
stay Sprite_Tilenumber+4,y
ldan ++$00 ;clear bit for horizontal flip
SetHFAt: orai SCRATCHPAD+$04 ;add other OAM attributes if necessary
stay Sprite_Attributes,y ;store sprite attributes
stay Sprite_Attributes+4,y
lda SCRATCHPAD+$02 ;now the y coordinates
stay Sprite_Y_Position,y ;note because they are
stay Sprite_Y_Position+4,y ;side by side, they are the same
lda SCRATCHPAD+$05
stay Sprite_X_Position,y ;store x coordinate, then
clc ;add 8 pixels and store another to
adcn ++$08 ;put them side by side
stay Sprite_X_Position+4,y
lda SCRATCHPAD+$02 ;add eight pixels to the next y
clc ;coordinate
adcn ++$08
sta SCRATCHPAD+$02
tya ;add eight to the offset in Y to
clc ;move to the next two sprites
adcn ++$08
tay
inx ;increment offset to return it to the
inx ;routine that called this subroutine
endif
rts
;-------------------------------------------------------------------------------------
if Z80==0
;unused space (т тхЁёшш Super Mario Bros..nes)
;р т тхЁёшш Super Mario Bros (JU) (PRG 1).nes ЄєЄ 78, ee, cb, f2, ff, ff - чрўхь??? фЁєушї Ёрчышўшщ ьхцфє ¤Єшьш тхЁёш ьш эхЄ!!!
.db $ff, $ff, $ff, $ff, $ff, $ff
endif
;-------------------------------------------------------------------------------------
if MUSICONINT==0
include "smbsound.asm"
include "smbmusic.asm"
endif
;-------------------------------------------------------------------------------------
;INTERRUPT VECTORS
if Z80==0
;т ъюэЎх ярь Єш яЁюуЁрьь√: 82 80 00 80 f0 ff
.dw NonMaskableInterrupt
.dw Start
.dw $fff0 ;unused (т Xevious Єюцх т ъюэЎх ff, юёЄры№э√х чэрўхэш фЁєушх, т Bomberman1 т ъюэЎх 7 фЁєушї срщЄ)
endif
SwimTileRepOffset = PlayerGraphicsTable + $9e
MusicHeaderOffsetData = MusicHeaderData - 1
MHD = MusicHeaderData