-
Star
(1,583)
You must be signed in to star a gist -
Fork
(185)
You must be signed in to fork a gist
-
-
Save 1wErt3r/4048722 to your computer and use it in GitHub Desktop.
;SMBDIS.ASM - A COMPREHENSIVE SUPER MARIO BROS. DISASSEMBLY | |
;by doppelganger ([email protected]) | |
;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. | |
;------------------------------------------------------------------------------------- | |
;DEFINES | |
;NES specific hardware defines | |
PPU_CTRL_REG1 = $2000 | |
PPU_CTRL_REG2 = $2001 | |
PPU_STATUS = $2002 | |
PPU_SPR_ADDR = $2003 | |
PPU_SPR_DATA = $2004 | |
PPU_SCROLL_REG = $2005 | |
PPU_ADDRESS = $2006 | |
PPU_DATA = $2007 | |
SND_REGISTER = $4000 | |
SND_SQUARE1_REG = $4000 | |
SND_SQUARE2_REG = $4004 | |
SND_TRIANGLE_REG = $4008 | |
SND_NOISE_REG = $400c | |
SND_DELTA_REG = $4010 | |
SND_MASTERCTRL_REG = $4015 | |
SPR_DMA = $4014 | |
JOYPAD_PORT = $4016 | |
JOYPAD_PORT1 = $4016 | |
JOYPAD_PORT2 = $4017 | |
; GAME SPECIFIC DEFINES | |
ObjectOffset = $08 | |
FrameCounter = $09 | |
SavedJoypadBits = $06fc | |
SavedJoypad1Bits = $06fc | |
SavedJoypad2Bits = $06fd | |
JoypadBitMask = $074a | |
JoypadOverride = $0758 | |
A_B_Buttons = $0a | |
PreviousA_B_Buttons = $0d | |
Up_Down_Buttons = $0b | |
Left_Right_Buttons = $0c | |
GameEngineSubroutine = $0e | |
Mirror_PPU_CTRL_REG1 = $0778 | |
Mirror_PPU_CTRL_REG2 = $0779 | |
OperMode = $0770 | |
OperMode_Task = $0772 | |
ScreenRoutineTask = $073c | |
GamePauseStatus = $0776 | |
GamePauseTimer = $0777 | |
DemoAction = $0717 | |
DemoActionTimer = $0718 | |
TimerControl = $0747 | |
IntervalTimerControl = $077f | |
Timers = $0780 | |
SelectTimer = $0780 | |
PlayerAnimTimer = $0781 | |
JumpSwimTimer = $0782 | |
RunningTimer = $0783 | |
BlockBounceTimer = $0784 | |
SideCollisionTimer = $0785 | |
JumpspringTimer = $0786 | |
GameTimerCtrlTimer = $0787 | |
ClimbSideTimer = $0789 | |
EnemyFrameTimer = $078a | |
FrenzyEnemyTimer = $078f | |
BowserFireBreathTimer = $0790 | |
StompTimer = $0791 | |
AirBubbleTimer = $0792 | |
ScrollIntervalTimer = $0795 | |
EnemyIntervalTimer = $0796 | |
BrickCoinTimer = $079d | |
InjuryTimer = $079e | |
StarInvincibleTimer = $079f | |
ScreenTimer = $07a0 | |
WorldEndTimer = $07a1 | |
DemoTimer = $07a2 | |
Sprite_Data = $0200 | |
Sprite_Y_Position = $0200 | |
Sprite_Tilenumber = $0201 | |
Sprite_Attributes = $0202 | |
Sprite_X_Position = $0203 | |
ScreenEdge_PageLoc = $071a | |
ScreenEdge_X_Pos = $071c | |
ScreenLeft_PageLoc = $071a | |
ScreenRight_PageLoc = $071b | |
ScreenLeft_X_Pos = $071c | |
ScreenRight_X_Pos = $071d | |
PlayerFacingDir = $33 | |
DestinationPageLoc = $34 | |
VictoryWalkControl = $35 | |
ScrollFractional = $0768 | |
PrimaryMsgCounter = $0719 | |
SecondaryMsgCounter = $0749 | |
HorizontalScroll = $073f | |
VerticalScroll = $0740 | |
ScrollLock = $0723 | |
ScrollThirtyTwo = $073d | |
Player_X_Scroll = $06ff | |
Player_Pos_ForScroll = $0755 | |
ScrollAmount = $0775 | |
AreaData = $e7 | |
AreaDataLow = $e7 | |
AreaDataHigh = $e8 | |
EnemyData = $e9 | |
EnemyDataLow = $e9 | |
EnemyDataHigh = $ea | |
AreaParserTaskNum = $071f | |
ColumnSets = $071e | |
CurrentPageLoc = $0725 | |
CurrentColumnPos = $0726 | |
BackloadingFlag = $0728 | |
BehindAreaParserFlag = $0729 | |
AreaObjectPageLoc = $072a | |
AreaObjectPageSel = $072b | |
AreaDataOffset = $072c | |
AreaObjOffsetBuffer = $072d | |
AreaObjectLength = $0730 | |
StaircaseControl = $0734 | |
AreaObjectHeight = $0735 | |
MushroomLedgeHalfLen = $0736 | |
EnemyDataOffset = $0739 | |
EnemyObjectPageLoc = $073a | |
EnemyObjectPageSel = $073b | |
MetatileBuffer = $06a1 | |
BlockBufferColumnPos = $06a0 | |
CurrentNTAddr_Low = $0721 | |
CurrentNTAddr_High = $0720 | |
AttributeBuffer = $03f9 | |
LoopCommand = $0745 | |
DisplayDigits = $07d7 | |
TopScoreDisplay = $07d7 | |
ScoreAndCoinDisplay = $07dd | |
PlayerScoreDisplay = $07dd | |
GameTimerDisplay = $07f8 | |
DigitModifier = $0134 | |
VerticalFlipFlag = $0109 | |
FloateyNum_Control = $0110 | |
ShellChainCounter = $0125 | |
FloateyNum_Timer = $012c | |
FloateyNum_X_Pos = $0117 | |
FloateyNum_Y_Pos = $011e | |
FlagpoleFNum_Y_Pos = $010d | |
FlagpoleFNum_YMFDummy = $010e | |
FlagpoleScore = $010f | |
FlagpoleCollisionYPos = $070f | |
StompChainCounter = $0484 | |
VRAM_Buffer1_Offset = $0300 | |
VRAM_Buffer1 = $0301 | |
VRAM_Buffer2_Offset = $0340 | |
VRAM_Buffer2 = $0341 | |
VRAM_Buffer_AddrCtrl = $0773 | |
Sprite0HitDetectFlag = $0722 | |
DisableScreenFlag = $0774 | |
DisableIntermediate = $0769 | |
ColorRotateOffset = $06d4 | |
TerrainControl = $0727 | |
AreaStyle = $0733 | |
ForegroundScenery = $0741 | |
BackgroundScenery = $0742 | |
CloudTypeOverride = $0743 | |
BackgroundColorCtrl = $0744 | |
AreaType = $074e | |
AreaAddrsLOffset = $074f | |
AreaPointer = $0750 | |
PlayerEntranceCtrl = $0710 | |
GameTimerSetting = $0715 | |
AltEntranceControl = $0752 | |
EntrancePage = $0751 | |
NumberOfPlayers = $077a | |
WarpZoneControl = $06d6 | |
ChangeAreaTimer = $06de | |
MultiLoopCorrectCntr = $06d9 | |
MultiLoopPassCntr = $06da | |
FetchNewGameTimerFlag = $0757 | |
GameTimerExpiredFlag = $0759 | |
PrimaryHardMode = $076a | |
SecondaryHardMode = $06cc | |
WorldSelectNumber = $076b | |
WorldSelectEnableFlag = $07fc | |
ContinueWorld = $07fd | |
CurrentPlayer = $0753 | |
PlayerSize = $0754 | |
PlayerStatus = $0756 | |
OnscreenPlayerInfo = $075a | |
NumberofLives = $075a ;used by current player | |
HalfwayPage = $075b | |
LevelNumber = $075c ;the actual dash number | |
Hidden1UpFlag = $075d | |
CoinTally = $075e | |
WorldNumber = $075f | |
AreaNumber = $0760 ;internal number used to find areas | |
CoinTallyFor1Ups = $0748 | |
OffscreenPlayerInfo = $0761 | |
OffScr_NumberofLives = $0761 ;used by offscreen player | |
OffScr_HalfwayPage = $0762 | |
OffScr_LevelNumber = $0763 | |
OffScr_Hidden1UpFlag = $0764 | |
OffScr_CoinTally = $0765 | |
OffScr_WorldNumber = $0766 | |
OffScr_AreaNumber = $0767 | |
BalPlatformAlignment = $03a0 | |
Platform_X_Scroll = $03a1 | |
PlatformCollisionFlag = $03a2 | |
YPlatformTopYPos = $0401 | |
YPlatformCenterYPos = $58 | |
BrickCoinTimerFlag = $06bc | |
StarFlagTaskControl = $0746 | |
PseudoRandomBitReg = $07a7 | |
WarmBootValidation = $07ff | |
SprShuffleAmtOffset = $06e0 | |
SprShuffleAmt = $06e1 | |
SprDataOffset = $06e4 | |
Player_SprDataOffset = $06e4 | |
Enemy_SprDataOffset = $06e5 | |
Block_SprDataOffset = $06ec | |
Alt_SprDataOffset = $06ec | |
Bubble_SprDataOffset = $06ee | |
FBall_SprDataOffset = $06f1 | |
Misc_SprDataOffset = $06f3 | |
SprDataOffset_Ctrl = $03ee | |
Player_State = $1d | |
Enemy_State = $1e | |
Fireball_State = $24 | |
Block_State = $26 | |
Misc_State = $2a | |
Player_MovingDir = $45 | |
Enemy_MovingDir = $46 | |
SprObject_X_Speed = $57 | |
Player_X_Speed = $57 | |
Enemy_X_Speed = $58 | |
Fireball_X_Speed = $5e | |
Block_X_Speed = $60 | |
Misc_X_Speed = $64 | |
Jumpspring_FixedYPos = $58 | |
JumpspringAnimCtrl = $070e | |
JumpspringForce = $06db | |
SprObject_PageLoc = $6d | |
Player_PageLoc = $6d | |
Enemy_PageLoc = $6e | |
Fireball_PageLoc = $74 | |
Block_PageLoc = $76 | |
Misc_PageLoc = $7a | |
Bubble_PageLoc = $83 | |
SprObject_X_Position = $86 | |
Player_X_Position = $86 | |
Enemy_X_Position = $87 | |
Fireball_X_Position = $8d | |
Block_X_Position = $8f | |
Misc_X_Position = $93 | |
Bubble_X_Position = $9c | |
SprObject_Y_Speed = $9f | |
Player_Y_Speed = $9f | |
Enemy_Y_Speed = $a0 | |
Fireball_Y_Speed = $a6 | |
Block_Y_Speed = $a8 | |
Misc_Y_Speed = $ac | |
SprObject_Y_HighPos = $b5 | |
Player_Y_HighPos = $b5 | |
Enemy_Y_HighPos = $b6 | |
Fireball_Y_HighPos = $bc | |
Block_Y_HighPos = $be | |
Misc_Y_HighPos = $c2 | |
Bubble_Y_HighPos = $cb | |
SprObject_Y_Position = $ce | |
Player_Y_Position = $ce | |
Enemy_Y_Position = $cf | |
Fireball_Y_Position = $d5 | |
Block_Y_Position = $d7 | |
Misc_Y_Position = $db | |
Bubble_Y_Position = $e4 | |
SprObject_Rel_XPos = $03ad | |
Player_Rel_XPos = $03ad | |
Enemy_Rel_XPos = $03ae | |
Fireball_Rel_XPos = $03af | |
Bubble_Rel_XPos = $03b0 | |
Block_Rel_XPos = $03b1 | |
Misc_Rel_XPos = $03b3 | |
SprObject_Rel_YPos = $03b8 | |
Player_Rel_YPos = $03b8 | |
Enemy_Rel_YPos = $03b9 | |
Fireball_Rel_YPos = $03ba | |
Bubble_Rel_YPos = $03bb | |
Block_Rel_YPos = $03bc | |
Misc_Rel_YPos = $03be | |
SprObject_SprAttrib = $03c4 | |
Player_SprAttrib = $03c4 | |
Enemy_SprAttrib = $03c5 | |
SprObject_X_MoveForce = $0400 | |
Enemy_X_MoveForce = $0401 | |
SprObject_YMF_Dummy = $0416 | |
Player_YMF_Dummy = $0416 | |
Enemy_YMF_Dummy = $0417 | |
Bubble_YMF_Dummy = $042c | |
SprObject_Y_MoveForce = $0433 | |
Player_Y_MoveForce = $0433 | |
Enemy_Y_MoveForce = $0434 | |
Block_Y_MoveForce = $043c | |
DisableCollisionDet = $0716 | |
Player_CollisionBits = $0490 | |
Enemy_CollisionBits = $0491 | |
SprObj_BoundBoxCtrl = $0499 | |
Player_BoundBoxCtrl = $0499 | |
Enemy_BoundBoxCtrl = $049a | |
Fireball_BoundBoxCtrl = $04a0 | |
Misc_BoundBoxCtrl = $04a2 | |
EnemyFrenzyBuffer = $06cb | |
EnemyFrenzyQueue = $06cd | |
Enemy_Flag = $0f | |
Enemy_ID = $16 | |
PlayerGfxOffset = $06d5 | |
Player_XSpeedAbsolute = $0700 | |
FrictionAdderHigh = $0701 | |
FrictionAdderLow = $0702 | |
RunningSpeed = $0703 | |
SwimmingFlag = $0704 | |
Player_X_MoveForce = $0705 | |
DiffToHaltJump = $0706 | |
JumpOrigin_Y_HighPos = $0707 | |
JumpOrigin_Y_Position = $0708 | |
VerticalForce = $0709 | |
VerticalForceDown = $070a | |
PlayerChangeSizeFlag = $070b | |
PlayerAnimTimerSet = $070c | |
PlayerAnimCtrl = $070d | |
DeathMusicLoaded = $0712 | |
FlagpoleSoundQueue = $0713 | |
CrouchingFlag = $0714 | |
MaximumLeftSpeed = $0450 | |
MaximumRightSpeed = $0456 | |
SprObject_OffscrBits = $03d0 | |
Player_OffscreenBits = $03d0 | |
Enemy_OffscreenBits = $03d1 | |
FBall_OffscreenBits = $03d2 | |
Bubble_OffscreenBits = $03d3 | |
Block_OffscreenBits = $03d4 | |
Misc_OffscreenBits = $03d6 | |
EnemyOffscrBitsMasked = $03d8 | |
Cannon_Offset = $046a | |
Cannon_PageLoc = $046b | |
Cannon_X_Position = $0471 | |
Cannon_Y_Position = $0477 | |
Cannon_Timer = $047d | |
Whirlpool_Offset = $046a | |
Whirlpool_PageLoc = $046b | |
Whirlpool_LeftExtent = $0471 | |
Whirlpool_Length = $0477 | |
Whirlpool_Flag = $047d | |
VineFlagOffset = $0398 | |
VineHeight = $0399 | |
VineObjOffset = $039a | |
VineStart_Y_Position = $039d | |
Block_Orig_YPos = $03e4 | |
Block_BBuf_Low = $03e6 | |
Block_Metatile = $03e8 | |
Block_PageLoc2 = $03ea | |
Block_RepFlag = $03ec | |
Block_ResidualCounter = $03f0 | |
Block_Orig_XPos = $03f1 | |
BoundingBox_UL_XPos = $04ac | |
BoundingBox_UL_YPos = $04ad | |
BoundingBox_DR_XPos = $04ae | |
BoundingBox_DR_YPos = $04af | |
BoundingBox_UL_Corner = $04ac | |
BoundingBox_LR_Corner = $04ae | |
EnemyBoundingBoxCoord = $04b0 | |
PowerUpType = $39 | |
FireballBouncingFlag = $3a | |
FireballCounter = $06ce | |
FireballThrowingTimer = $0711 | |
HammerEnemyOffset = $06ae | |
JumpCoinMiscOffset = $06b7 | |
Block_Buffer_1 = $0500 | |
Block_Buffer_2 = $05d0 | |
HammerThrowingTimer = $03a2 | |
HammerBroJumpTimer = $3c | |
Misc_Collision_Flag = $06be | |
RedPTroopaOrigXPos = $0401 | |
RedPTroopaCenterYPos = $58 | |
XMovePrimaryCounter = $a0 | |
XMoveSecondaryCounter = $58 | |
CheepCheepMoveMFlag = $58 | |
CheepCheepOrigYPos = $0434 | |
BitMFilter = $06dd | |
LakituReappearTimer = $06d1 | |
LakituMoveSpeed = $58 | |
LakituMoveDirection = $a0 | |
FirebarSpinState_Low = $58 | |
FirebarSpinState_High = $a0 | |
FirebarSpinSpeed = $0388 | |
FirebarSpinDirection = $34 | |
DuplicateObj_Offset = $06cf | |
NumberofGroupEnemies = $06d3 | |
BlooperMoveCounter = $a0 | |
BlooperMoveSpeed = $58 | |
BowserBodyControls = $0363 | |
BowserFeetCounter = $0364 | |
BowserMovementSpeed = $0365 | |
BowserOrigXPos = $0366 | |
BowserFlameTimerCtrl = $0367 | |
BowserFront_Offset = $0368 | |
BridgeCollapseOffset = $0369 | |
BowserGfxFlag = $036a | |
BowserHitPoints = $0483 | |
MaxRangeFromOrigin = $06dc | |
BowserFlamePRandomOfs = $0417 | |
PiranhaPlantUpYPos = $0417 | |
PiranhaPlantDownYPos = $0434 | |
PiranhaPlant_Y_Speed = $58 | |
PiranhaPlant_MoveFlag = $a0 | |
FireworksCounter = $06d7 | |
ExplosionGfxCounter = $58 | |
ExplosionTimerCounter = $a0 | |
;sound related defines | |
Squ2_NoteLenBuffer = $07b3 | |
Squ2_NoteLenCounter = $07b4 | |
Squ2_EnvelopeDataCtrl = $07b5 | |
Squ1_NoteLenCounter = $07b6 | |
Squ1_EnvelopeDataCtrl = $07b7 | |
Tri_NoteLenBuffer = $07b8 | |
Tri_NoteLenCounter = $07b9 | |
Noise_BeatLenCounter = $07ba | |
Squ1_SfxLenCounter = $07bb | |
Squ2_SfxLenCounter = $07bd | |
Sfx_SecondaryCounter = $07be | |
Noise_SfxLenCounter = $07bf | |
PauseSoundQueue = $fa | |
Square1SoundQueue = $ff | |
Square2SoundQueue = $fe | |
NoiseSoundQueue = $fd | |
AreaMusicQueue = $fb | |
EventMusicQueue = $fc | |
Square1SoundBuffer = $f1 | |
Square2SoundBuffer = $f2 | |
NoiseSoundBuffer = $f3 | |
AreaMusicBuffer = $f4 | |
EventMusicBuffer = $07b1 | |
PauseSoundBuffer = $07b2 | |
MusicData = $f5 | |
MusicDataLow = $f5 | |
MusicDataHigh = $f6 | |
MusicOffset_Square2 = $f7 | |
MusicOffset_Square1 = $f8 | |
MusicOffset_Triangle = $f9 | |
MusicOffset_Noise = $07b0 | |
NoteLenLookupTblOfs = $f0 | |
DAC_Counter = $07c0 | |
NoiseDataLoopbackOfs = $07c1 | |
NoteLengthTblAdder = $07c4 | |
AreaMusicBuffer_Alt = $07c5 | |
PauseModeFlag = $07c6 | |
GroundMusicHeaderOfs = $07c7 | |
AltRegContentFlag = $07ca | |
;------------------------------------------------------------------------------------- | |
;CONSTANTS | |
;sound effects constants | |
Sfx_SmallJump = %10000000 | |
Sfx_Flagpole = %01000000 | |
Sfx_Fireball = %00100000 | |
Sfx_PipeDown_Injury = %00010000 | |
Sfx_EnemySmack = %00001000 | |
Sfx_EnemyStomp = %00000100 | |
Sfx_Bump = %00000010 | |
Sfx_BigJump = %00000001 | |
Sfx_BowserFall = %10000000 | |
Sfx_ExtraLife = %01000000 | |
Sfx_PowerUpGrab = %00100000 | |
Sfx_TimerTick = %00010000 | |
Sfx_Blast = %00001000 | |
Sfx_GrowVine = %00000100 | |
Sfx_GrowPowerUp = %00000010 | |
Sfx_CoinGrab = %00000001 | |
Sfx_BowserFlame = %00000010 | |
Sfx_BrickShatter = %00000001 | |
;music constants | |
Silence = %10000000 | |
StarPowerMusic = %01000000 | |
PipeIntroMusic = %00100000 | |
CloudMusic = %00010000 | |
CastleMusic = %00001000 | |
UndergroundMusic = %00000100 | |
WaterMusic = %00000010 | |
GroundMusic = %00000001 | |
TimeRunningOutMusic = %01000000 | |
EndOfLevelMusic = %00100000 | |
AltGameOverMusic = %00010000 | |
EndOfCastleMusic = %00001000 | |
VictoryMusic = %00000100 | |
GameOverMusic = %00000010 | |
DeathMusic = %00000001 | |
;enemy object constants | |
GreenKoopa = $00 | |
BuzzyBeetle = $02 | |
RedKoopa = $03 | |
HammerBro = $05 | |
Goomba = $06 | |
Bloober = $07 | |
BulletBill_FrenzyVar = $08 | |
GreyCheepCheep = $0a | |
RedCheepCheep = $0b | |
Podoboo = $0c | |
PiranhaPlant = $0d | |
GreenParatroopaJump = $0e | |
RedParatroopa = $0f | |
GreenParatroopaFly = $10 | |
Lakitu = $11 | |
Spiny = $12 | |
FlyCheepCheepFrenzy = $14 | |
FlyingCheepCheep = $14 | |
BowserFlame = $15 | |
Fireworks = $16 | |
BBill_CCheep_Frenzy = $17 | |
Stop_Frenzy = $18 | |
Bowser = $2d | |
PowerUpObject = $2e | |
VineObject = $2f | |
FlagpoleFlagObject = $30 | |
StarFlagObject = $31 | |
JumpspringObject = $32 | |
BulletBill_CannonVar = $33 | |
RetainerObject = $35 | |
TallEnemy = $09 | |
;other constants | |
World1 = 0 | |
World2 = 1 | |
World3 = 2 | |
World4 = 3 | |
World5 = 4 | |
World6 = 5 | |
World7 = 6 | |
World8 = 7 | |
Level1 = 0 | |
Level2 = 1 | |
Level3 = 2 | |
Level4 = 3 | |
WarmBootOffset = <$07d6 | |
ColdBootOffset = <$07fe | |
TitleScreenDataOffset = $1ec0 | |
SoundMemory = $07b0 | |
SwimTileRepOffset = PlayerGraphicsTable + $9e | |
MusicHeaderOffsetData = MusicHeaderData - 1 | |
MHD = MusicHeaderData | |
A_Button = %10000000 | |
B_Button = %01000000 | |
Select_Button = %00100000 | |
Start_Button = %00010000 | |
Up_Dir = %00001000 | |
Down_Dir = %00000100 | |
Left_Dir = %00000010 | |
Right_Dir = %00000001 | |
TitleScreenModeValue = 0 | |
GameModeValue = 1 | |
VictoryModeValue = 2 | |
GameOverModeValue = 3 | |
;------------------------------------------------------------------------------------- | |
;DIRECTIVES | |
.index 8 | |
.mem 8 | |
.org $8000 | |
;------------------------------------------------------------------------------------- | |
Start: | |
sei ;pretty standard 6502 type init here | |
cld | |
lda #%00010000 ;init PPU control register 1 | |
sta PPU_CTRL_REG1 | |
ldx #$ff ;reset stack pointer | |
txs | |
VBlank1: lda PPU_STATUS ;wait two frames | |
bpl VBlank1 | |
VBlank2: lda PPU_STATUS | |
bpl VBlank2 | |
ldy #ColdBootOffset ;load default cold boot pointer | |
ldx #$05 ;this is where we check for a warm boot | |
WBootCheck: lda TopScoreDisplay,x ;check each score digit in the top score | |
cmp #10 ;to see if we have a valid digit | |
bcs ColdBoot ;if not, give up and proceed with cold boot | |
dex | |
bpl WBootCheck | |
lda WarmBootValidation ;second checkpoint, check to see if | |
cmp #$a5 ;another location has a specific value | |
bne ColdBoot | |
ldy #WarmBootOffset ;if passed both, load warm boot pointer | |
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 | |
lda #$a5 ;set warm boot flag | |
sta WarmBootValidation | |
sta PseudoRandomBitReg ;set seed for pseudorandom register | |
lda #%00001111 | |
sta SND_MASTERCTRL_REG ;enable all sound channels except dmc | |
lda #%00000110 | |
sta PPU_CTRL_REG2 ;turn off clipping for OAM and background | |
jsr MoveAllSpritesOffscreen | |
jsr InitializeNameTables ;initialize both name tables | |
inc DisableScreenFlag ;set flag to disable screen output | |
lda Mirror_PPU_CTRL_REG1 | |
ora #%10000000 ;enable NMIs | |
jsr WritePPUReg1 | |
EndlessLoop: jmp EndlessLoop ;endless loop, need I say more? | |
;------------------------------------------------------------------------------------- | |
;$00 - vram buffer address table low, also used for pseudorandom bit | |
;$01 - vram buffer address table high | |
VRAM_AddrTable_Low: | |
.db <VRAM_Buffer1, <WaterPaletteData, <GroundPaletteData | |
.db <UndergroundPaletteData, <CastlePaletteData, <VRAM_Buffer1_Offset | |
.db <VRAM_Buffer2, <VRAM_Buffer2, <BowserPaletteData | |
.db <DaySnowPaletteData, <NightSnowPaletteData, <MushroomPaletteData | |
.db <MarioThanksMessage, <LuigiThanksMessage, <MushroomRetainerSaved | |
.db <PrincessSaved1, <PrincessSaved2, <WorldSelectMessage1 | |
.db <WorldSelectMessage2 | |
VRAM_AddrTable_High: | |
.db >VRAM_Buffer1, >WaterPaletteData, >GroundPaletteData | |
.db >UndergroundPaletteData, >CastlePaletteData, >VRAM_Buffer1_Offset | |
.db >VRAM_Buffer2, >VRAM_Buffer2, >BowserPaletteData | |
.db >DaySnowPaletteData, >NightSnowPaletteData, >MushroomPaletteData | |
.db >MarioThanksMessage, >LuigiThanksMessage, >MushroomRetainerSaved | |
.db >PrincessSaved1, >PrincessSaved2, >WorldSelectMessage1 | |
.db >WorldSelectMessage2 | |
VRAM_Buffer_Offset: | |
.db <VRAM_Buffer1_Offset, <VRAM_Buffer2_Offset | |
NonMaskableInterrupt: | |
lda Mirror_PPU_CTRL_REG1 ;disable NMIs in mirror reg | |
and #%01111111 ;save all other bits | |
sta Mirror_PPU_CTRL_REG1 | |
and #%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 | |
and #%11100110 | |
ldy DisableScreenFlag ;get screen disable flag | |
bne ScreenOff ;if set, used bits as-is | |
lda Mirror_PPU_CTRL_REG2 ;otherwise reenable bits and save them | |
ora #%00011110 | |
ScreenOff: sta Mirror_PPU_CTRL_REG2 ;save bits for later but not in register at the moment | |
and #%11100111 ;disable screen for now | |
sta PPU_CTRL_REG2 | |
ldx PPU_STATUS ;reset flip-flop and reset scroll registers to zero | |
lda #$00 | |
jsr InitScroll | |
sta PPU_SPR_ADDR ;reset spr-ram address register | |
lda #$02 ;perform spr-ram DMA access on $0200-$02ff | |
sta SPR_DMA | |
ldx VRAM_Buffer_AddrCtrl ;load control for pointer to buffer contents | |
lda VRAM_AddrTable_Low,x ;set indirect at $00 to pointer | |
sta $00 | |
lda VRAM_AddrTable_High,x | |
sta $01 | |
jsr UpdateScreen ;update screen with buffer contents | |
ldy #$00 | |
ldx VRAM_Buffer_AddrCtrl ;check for usage of $0341 | |
cpx #$06 | |
bne InitBuffer | |
iny ;get offset based on usage | |
InitBuffer: ldx VRAM_Buffer_Offset,y | |
lda #$00 ;clear buffer header at last location | |
sta VRAM_Buffer1_Offset,x | |
sta VRAM_Buffer1,x | |
sta VRAM_Buffer_AddrCtrl ;reinit address control to $0301 | |
lda Mirror_PPU_CTRL_REG2 ;copy mirror of $2001 to register | |
sta PPU_CTRL_REG2 | |
jsr SoundEngine ;play sound | |
jsr ReadJoypads ;read joypads | |
jsr PauseRoutine ;handle pause | |
jsr UpdateTopScore | |
lda GamePauseStatus ;check for pause status | |
lsr | |
bcs PauseSkip | |
lda TimerControl ;if master timer control not set, decrement | |
beq DecTimers ;all frame and interval timers | |
dec TimerControl | |
bne NoDecTimers | |
DecTimers: ldx #$14 ;load end offset for end of frame timers | |
dec IntervalTimerControl ;decrement interval timer control, | |
bpl DecTimersLoop ;if not expired, only frame timers will decrement | |
lda #$14 | |
sta IntervalTimerControl ;if control for interval timers expired, | |
ldx #$23 ;interval timers will decrement along with frame timers | |
DecTimersLoop: lda Timers,x ;check current timer | |
beq SkipExpTimer ;if current timer expired, branch to skip, | |
dec Timers,x ;otherwise decrement the current timer | |
SkipExpTimer: dex ;move onto next timer | |
bpl DecTimersLoop ;do this until all timers are dealt with | |
NoDecTimers: inc FrameCounter ;increment frame counter | |
PauseSkip: ldx #$00 | |
ldy #$07 | |
lda PseudoRandomBitReg ;get first memory location of LSFR bytes | |
and #%00000010 ;mask out all but d1 | |
sta $00 ;save here | |
lda PseudoRandomBitReg+1 ;get second memory location | |
and #%00000010 ;mask out all but d1 | |
eor $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: ror PseudoRandomBitReg,x ;rotate carry into d7, and rotate last bit into carry | |
inx ;increment to next byte | |
dey ;decrement for loop | |
bne RotPRandomBit | |
lda Sprite0HitDetectFlag ;check for flag here | |
beq SkipSprite0 | |
Sprite0Clr: lda PPU_STATUS ;wait for sprite 0 flag to clear, which will | |
and #%01000000 ;not happen until vblank has ended | |
bne Sprite0Clr | |
lda GamePauseStatus ;if in pause mode, do not bother with sprites at all | |
lsr | |
bcs Sprite0Hit | |
jsr MoveSpritesOffscreen | |
jsr SpriteShuffler | |
Sprite0Hit: lda PPU_STATUS ;do sprite #0 hit detection | |
and #%01000000 | |
beq Sprite0Hit | |
ldy #$14 ;small delay, to wait until we hit horizontal blank time | |
HBlankDelay: dey | |
bne HBlankDelay | |
SkipSprite0: lda HorizontalScroll ;set scroll registers from variables | |
sta PPU_SCROLL_REG | |
lda VerticalScroll | |
sta PPU_SCROLL_REG | |
lda Mirror_PPU_CTRL_REG1 ;load saved mirror of $2000 | |
pha | |
sta PPU_CTRL_REG1 | |
lda GamePauseStatus ;if in pause mode, do not perform operation mode stuff | |
lsr | |
bcs SkipMainOper | |
jsr OperModeExecutionTree ;otherwise do one of many, many possible subroutines | |
SkipMainOper: lda PPU_STATUS ;reset flip-flop | |
pla | |
ora #%10000000 ;reactivate NMIs | |
sta PPU_CTRL_REG1 | |
rti ;we are done until the next frame! | |
;------------------------------------------------------------------------------------- | |
PauseRoutine: | |
lda OperMode ;are we in victory mode? | |
cmp #VictoryModeValue ;if so, go ahead | |
beq ChkPauseTimer | |
cmp #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? | |
cmp #$03 | |
bne ExitPause ;if not, leave | |
ChkPauseTimer: lda GamePauseTimer ;check if pause timer is still counting down | |
beq ChkStart | |
dec GamePauseTimer ;if so, decrement and leave | |
rts | |
ChkStart: lda SavedJoypad1Bits ;check to see if start is pressed | |
and #Start_Button ;on controller 1 | |
beq ClrPauseTimer | |
lda GamePauseStatus ;check to see if timer flag is set | |
and #%10000000 ;and if so, do not reset timer (residual, | |
bne ExitPause ;joypad reading routine makes this unnecessary) | |
lda #$2b ;set pause timer | |
sta GamePauseTimer | |
lda GamePauseStatus | |
tay | |
iny ;set pause sfx queue for next pause mode | |
sty PauseSoundQueue | |
eor #%00000001 ;invert d0 and set d7 | |
ora #%10000000 | |
bne SetPause ;unconditional branch | |
ClrPauseTimer: lda GamePauseStatus ;clear timer flag if timer is at zero and start button | |
and #%01111111 ;is not pressed | |
SetPause: sta GamePauseStatus | |
ExitPause: rts | |
;------------------------------------------------------------------------------------- | |
;$00 - used for preset value | |
SpriteShuffler: | |
ldy AreaType ;load level type, likely residual code | |
lda #$28 ;load preset value which will put it at | |
sta $00 ;sprite #10 | |
ldx #$0e ;start at the end of OAM data offsets | |
ShuffleLoop: lda SprDataOffset,x ;check for offset value against | |
cmp $00 ;the preset value | |
bcc NextSprOffset ;if less, skip this part | |
ldy SprShuffleAmtOffset ;get current offset to preset value we want to add | |
clc | |
adc SprShuffleAmt,y ;get shuffle amount, add to current sprite offset | |
bcc StrSprOffset ;if not exceeded $ff, skip second add | |
clc | |
adc $00 ;otherwise add preset value $28 to offset | |
StrSprOffset: sta 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 | |
cpx #$03 ;check if offset + 1 goes to 3 | |
bne SetAmtOffset ;if offset + 1 not 3, store | |
ldx #$00 ;otherwise, init to 0 | |
SetAmtOffset: stx SprShuffleAmtOffset | |
ldx #$08 ;load offsets for values and storage | |
ldy #$02 | |
SetMiscOffset: lda SprDataOffset+5,y ;load one of three OAM data offsets | |
sta Misc_SprDataOffset-2,x ;store first one unmodified, but | |
clc ;add eight to the second and eight | |
adc #$08 ;more to the third one | |
sta Misc_SprDataOffset-1,x ;note that due to the way X is set up, | |
clc ;this code loads into the misc sprite offsets | |
adc #$08 | |
sta Misc_SprDataOffset,x | |
dex | |
dex | |
dex | |
dey | |
bpl SetMiscOffset ;do this until all misc spr offsets are loaded | |
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: | |
ldy #$00 ;this routine moves all sprites off the screen | |
.db $2c ;BIT instruction opcode | |
MoveSpritesOffscreen: | |
ldy #$04 ;this routine moves all but sprite 0 | |
lda #$f8 ;off the screen | |
SprInitLoop: sta 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: | |
ldy #$00 | |
lda SavedJoypad1Bits ;check to see if either player pressed | |
ora SavedJoypad2Bits ;only the start button (either joypad) | |
cmp #Start_Button | |
beq StartGame | |
cmp #A_Button+Start_Button ;check to see if A + start was pressed | |
bne ChkSelect ;if not, branch to check select button | |
StartGame: jmp ChkContinue ;if either start or A + start, execute here | |
ChkSelect: cmp #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 | |
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 | |
jmp RunDemo ;otherwise, run game engine for demo | |
ChkWorldSel: ldx WorldSelectEnableFlag ;check to see if world selection has been enabled | |
beq NullJoypad | |
cmp #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 | |
beq ResetTitle ;if demo timer expired, branch to reset title screen mode | |
lda #$18 ;otherwise reset demo timer | |
sta DemoTimer | |
lda SelectTimer ;check select/B button timer | |
bne NullJoypad ;if not expired, branch | |
lda #$10 ;otherwise reset select button timer | |
sta SelectTimer | |
cpy #$01 ;was the B button pressed earlier? if so, branch | |
beq IncWorldSel ;note this will not be run if world selection is disabled | |
lda NumberOfPlayers ;if no, must have been the select button, therefore | |
eor #%00000001 ;change number of players and draw icon accordingly | |
sta NumberOfPlayers | |
jsr DrawMushroomIcon | |
jmp NullJoypad | |
IncWorldSel: ldx WorldSelectNumber ;increment world select number | |
inx | |
txa | |
and #%00000111 ;mask out higher bits | |
sta WorldSelectNumber ;store as current world select number | |
jsr GoContinue | |
UpdateShroom: lda WSelectBufferTemplate,x ;write template for world select in vram buffer | |
sta VRAM_Buffer1-1,x ;do this until all bytes are written | |
inx | |
cpx #$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: lda #$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 | |
cmp #$06 | |
bne ExitMenu ;if not, do not do all the resetting below | |
ResetTitle: lda #$00 ;reset game modes, disable | |
sta OperMode ;sprite 0 check and disable | |
sta OperMode_Task ;screen output | |
sta Sprite0HitDetectFlag | |
inc DisableScreenFlag | |
rts | |
ChkContinue: ldy DemoTimer ;if timer for demo has expired, reset modes | |
beq ResetTitle | |
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 | |
inc Hidden1UpFlag ;set 1-up box flag for both players | |
inc OffScr_Hidden1UpFlag | |
inc FetchNewGameTimerFlag ;set fetch new game timer flag | |
inc OperMode ;set next game mode | |
lda WorldSelectEnableFlag ;if world select flag is on, then primary | |
sta PrimaryHardMode ;hard mode must be on as well | |
lda #$00 | |
sta OperMode_Task ;set game mode here, and clear demo timer | |
sta DemoTimer | |
ldx #$17 | |
lda #$00 | |
InitScores: sta 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 | |
ldx #$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: | |
ldy #$07 ;read eight bytes to be read by transfer routine | |
IconDataRead: lda MushroomIconData,y ;note that the default position is set for a | |
sta VRAM_Buffer1-1,y ;1-player game | |
dey | |
bpl IconDataRead | |
lda NumberOfPlayers ;check number of players | |
beq ExitIcon ;if set to 1-player game, we're done | |
lda #$24 ;otherwise, load blank tile in 1-player position | |
sta VRAM_Buffer1+3 | |
lda #$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 | |
bne DoAction ;if timer still counting down, skip | |
inx | |
inc DemoAction ;if expired, increment action, X, and | |
sec ;set carry by default for demo over | |
lda DemoTimingData-1,x ;get next timer | |
sta DemoActionTimer ;store as current timer | |
beq DemoOver ;if timer already at zero, skip | |
DoAction: lda DemoActionData-1,x ;get and perform action (current or next) | |
sta SavedJoypad1Bits | |
dec 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 | |
beq AutoPlayer ;if on bridge collapse, skip enemy processing | |
ldx #$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 | |
lda #EndOfCastleMusic | |
sta EventMusicQueue ;play win castle music | |
jmp IncModeTask_B ;jump to set next major task in victory mode | |
;------------------------------------------------------------------------------------- | |
PlayerVictoryWalk: | |
ldy #$00 ;set value here to not walk player by default | |
sty VictoryWalkControl | |
lda Player_PageLoc ;get player's page location | |
cmp 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 | |
cmp #$60 ;compare with preset horizontal position | |
bcs DontWalk ;if still on other page, branch ahead | |
PerformWalk: inc 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 | |
cmp 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 | |
adc #$80 | |
sta ScrollFractional ;save fractional movement amount | |
lda #$01 ;set 1 pixel per frame | |
adc #$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 | |
inc VictoryWalkControl ;increment value to stay in this routine | |
ExitVWalk: lda VictoryWalkControl ;load value set here | |
beq IncModeTask_A ;if zero, branch to change modes | |
rts ;otherwise leave | |
;------------------------------------------------------------------------------------- | |
PrintVictoryMessages: | |
lda SecondaryMsgCounter ;load secondary message counter | |
bne IncMsgCounter ;if set, branch to increment message counters | |
lda PrimaryMsgCounter ;otherwise load primary message counter | |
beq ThankPlayer ;if set to zero, branch to print first message | |
cmp #$09 ;if at 9 or above, branch elsewhere (this comparison | |
bcs IncMsgCounter ;is residual code, counter never reaches 9) | |
ldy WorldNumber ;check world number | |
cpy #World8 | |
bne MRetainerMsg ;if not at world 8, skip to next part | |
cmp #$03 ;check primary message counter again | |
bcc IncMsgCounter ;if not at 3 yet (world 8 only), branch to increment | |
sbc #$01 ;otherwise subtract one | |
jmp ThankPlayer ;and skip to next part | |
MRetainerMsg: cmp #$02 ;check primary message counter | |
bcc IncMsgCounter ;if not at 2 yet (world 1-7 only), branch | |
ThankPlayer: tay ;put primary message counter into Y | |
bne SecondPartMsg ;if counter nonzero, skip this part, do not print first message | |
lda CurrentPlayer ;otherwise get player currently on the screen | |
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 | |
cmp #World8 ;check world number | |
beq EvalForMusic ;if at world 8, branch to next part | |
dey ;otherwise decrement Y for world 1-7's message | |
cpy #$04 ;if counter at 4 (world 1-7 only) | |
bcs SetEndTimer ;branch to set victory end timer | |
cpy #$03 ;if counter at 3 (world 1-7 only) | |
bcs IncMsgCounter ;branch to keep counting | |
EvalForMusic: cpy #$03 ;if counter not yet at 3 (world 8 only), branch | |
bne PrintMsg ;to print message only (note world 1-7 will only | |
lda #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, | |
adc #$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 | |
adc #$04 ;add four to secondary message counter | |
sta SecondaryMsgCounter | |
lda PrimaryMsgCounter | |
adc #$00 ;add carry to primary message counter | |
sta PrimaryMsgCounter | |
cmp #$07 ;check primary counter one more time | |
SetEndTimer: bcc ExitMsgs ;if not reached value yet, branch to leave | |
lda #$06 | |
sta WorldEndTimer ;otherwise set world end timer | |
IncModeTask_A: inc OperMode_Task ;move onto next task in mode | |
ExitMsgs: rts ;leave | |
;------------------------------------------------------------------------------------- | |
PlayerEndWorld: | |
lda WorldEndTimer ;check to see if world end timer expired | |
bne EndExitOne ;branch to leave if not | |
ldy WorldNumber ;check world number | |
cpy #World8 ;if on world 8, player is done with game, | |
bcs EndChkBButton ;thus branch to read controller | |
lda #$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 | |
inc WorldNumber ;increment world number to move onto the next world | |
jsr LoadAreaPointer ;get area address offset for the next area | |
inc FetchNewGameTimerFlag ;set flag to load game timer from header | |
lda #GameModeValue | |
sta OperMode ;set mode of operation to game mode | |
EndExitOne: rts ;and leave | |
EndChkBButton: lda SavedJoypad1Bits | |
ora SavedJoypad2Bits ;check to see if B button was pressed on | |
and #B_Button ;either controller | |
beq EndExitTwo ;branch to leave if not | |
lda #$01 ;otherwise set world selection flag | |
sta WorldSelectEnableFlag | |
lda #$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: | |
lda FloateyNum_Control,x ;load control for floatey number | |
beq EndExitOne ;if zero, branch to leave | |
cmp #$0b ;if less than $0b, branch | |
bcc ChkNumTimer | |
lda #$0b ;otherwise set to $0b, thus keeping | |
sta FloateyNum_Control,x ;it in range | |
ChkNumTimer: tay ;use as Y | |
lda FloateyNum_Timer,x ;check value here | |
bne DecNumTimer ;if nonzero, branch ahead | |
sta FloateyNum_Control,x ;initialize floatey number control and leave | |
rts | |
DecNumTimer: dec FloateyNum_Timer,x ;decrement value here | |
cmp #$2b ;if not reached a certain point, branch | |
bne ChkTallEnemy | |
cpy #$0b ;check offset for $0b | |
bne LoadNumTiles ;branch ahead if not found | |
inc NumberofLives ;give player one extra life (1-up) | |
lda #Sfx_ExtraLife | |
sta Square2SoundQueue ;and play the 1-up sound | |
LoadNumTiles: lda ScoreUpdateData,y ;load point value here | |
lsr ;move high nybble to low | |
lsr | |
lsr | |
lsr | |
tax ;use as X offset, essentially the digit | |
lda ScoreUpdateData,y ;load again and this time | |
and #%00001111 ;mask out the high nybble | |
sta DigitModifier,x ;store as amount to add to the digit | |
jsr AddToScore ;update the score accordingly | |
ChkTallEnemy: ldy Enemy_SprDataOffset,x ;get OAM data offset for enemy object | |
lda Enemy_ID,x ;get enemy object identifier | |
cmp #Spiny | |
beq FloateyPart ;branch if spiny | |
cmp #PiranhaPlant | |
beq FloateyPart ;branch if piranha plant | |
cmp #HammerBro | |
beq GetAltOffset ;branch elsewhere if hammer bro | |
cmp #GreyCheepCheep | |
beq FloateyPart ;branch if cheep-cheep of either color | |
cmp #RedCheepCheep | |
beq FloateyPart | |
cmp #TallEnemy | |
bcs GetAltOffset ;branch elsewhere if enemy object => $09 | |
lda Enemy_State,x | |
cmp #$02 ;if enemy state defeated or otherwise | |
bcs FloateyPart ;$02 or greater, branch beyond this part | |
GetAltOffset: ldx SprDataOffset_Ctrl ;load some kind of control bit | |
ldy Alt_SprDataOffset,x ;get alternate OAM data offset | |
ldx ObjectOffset ;get enemy object offset again | |
FloateyPart: lda FloateyNum_Y_Pos,x ;get vertical coordinate for | |
cmp #$18 ;floatey number, if coordinate in the | |
bcc SetupNumSpr ;status bar, branch | |
sbc #$01 | |
sta FloateyNum_Y_Pos,x ;otherwise subtract one and store as new | |
SetupNumSpr: lda FloateyNum_Y_Pos,x ;get vertical coordinate | |
sbc #$08 ;subtract eight and dump into the | |
jsr DumpTwoSpr ;left and right sprite's Y coordinates | |
lda FloateyNum_X_Pos,x ;get horizontal coordinate | |
sta Sprite_X_Position,y ;store into X coordinate of left sprite | |
clc | |
adc #$08 ;add eight pixels and store into X | |
sta Sprite_X_Position+4,y ;coordinate of right sprite | |
lda #$02 | |
sta Sprite_Attributes,y ;set palette control in attribute bytes | |
sta Sprite_Attributes+4,y ;of left and right sprites | |
lda FloateyNum_Control,x | |
asl ;multiply our floatey number control by 2 | |
tax ;and use as offset for look-up table | |
lda FloateyNumTileData,x | |
sta Sprite_Tilenumber,y ;display first half of number of points | |
lda FloateyNumTileData+1,x | |
sta 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 | |
.dw SetupIntermediate | |
.dw WriteTopStatusLine | |
.dw WriteBottomStatusLine | |
.dw DisplayTimeUp | |
.dw ResetSpritesAndScreenTimer | |
.dw DisplayIntermediate | |
.dw ResetSpritesAndScreenTimer | |
.dw AreaParserTaskControl | |
.dw GetAreaPalette | |
.dw GetBackgroundColor | |
.dw GetAlternatePalette1 | |
.dw DrawTitleScreen | |
.dw ClearBuffersDrawIcon | |
.dw WriteTopScore | |
;------------------------------------------------------------------------------------- | |
InitScreen: | |
jsr MoveAllSpritesOffscreen ;initialize all sprites including sprite #0 | |
jsr InitializeNameTables ;and erase both name and attribute tables | |
lda OperMode | |
beq NextSubtask ;if mode still 0, do not load | |
ldx #$03 ;into buffer pointer | |
jmp SetVRAMAddr_A | |
;------------------------------------------------------------------------------------- | |
SetupIntermediate: | |
lda BackgroundColorCtrl ;save current background color control | |
pha ;and player status to stack | |
lda PlayerStatus | |
pha | |
lda #$00 ;set background color to black | |
sta PlayerStatus ;and player status to not fiery | |
lda #$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 | |
;------------------------------------------------------------------------------------- | |
AreaPalette: | |
.db $01, $02, $03, $04 | |
GetAreaPalette: | |
ldy AreaType ;select appropriate palette to load | |
ldx AreaPalette,y ;based on area type | |
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: | |
.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 | |
PlayerColors: | |
.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) | |
GetBackgroundColor: | |
ldy BackgroundColorCtrl ;check background color control | |
beq NoBGColor ;if not set, increment task and fetch palette | |
lda BGColorCtrl_Addr-4,y ;put appropriate palette into vram | |
sta VRAM_Buffer_AddrCtrl ;note that if set to 5-7, $0301 will not be read | |
NoBGColor: inc ScreenRoutineTask ;increment to next subtask and plod on through | |
GetPlayerColors: | |
ldx VRAM_Buffer1_Offset ;get current buffer offset | |
ldy #$00 | |
lda CurrentPlayer ;check which player is on the screen | |
beq ChkFiery | |
ldy #$04 ;load offset for luigi | |
ChkFiery: lda PlayerStatus ;check player status | |
cmp #$02 | |
bne StartClrGet ;if fiery, load alternate offset for fiery player | |
ldy #$08 | |
StartClrGet: lda #$03 ;do four colors | |
sta $00 | |
ClrGetLoop: lda PlayerColors,y ;fetch player colors and store them | |
sta VRAM_Buffer1+3,x ;in the buffer | |
iny | |
inx | |
dec $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 | |
bne SetBGColor ;therefore use it as offset to background color | |
ldy AreaType ;otherwise use area type bits from area offset as offset | |
SetBGColor: lda BackgroundColors,y ;to background color instead | |
sta VRAM_Buffer1+3,x | |
lda #$3f ;set for sprite palette address | |
sta VRAM_Buffer1,x ;save to buffer | |
lda #$10 | |
sta VRAM_Buffer1+1,x | |
lda #$04 ;write length byte to buffer | |
sta VRAM_Buffer1+2,x | |
lda #$00 ;now the null terminator | |
sta VRAM_Buffer1+7,x | |
txa ;move the buffer pointer ahead 7 bytes | |
clc ;in case we want to write anything else later | |
adc #$07 | |
SetVRAMOffset: sta VRAM_Buffer1_Offset ;store as new vram buffer offset | |
rts | |
;------------------------------------------------------------------------------------- | |
GetAlternatePalette1: | |
lda AreaStyle ;check for mushroom level style | |
cmp #$01 | |
bne NoAltPal | |
lda #$0b ;if found, load appropriate palette | |
SetVRAMAddr_B: sta VRAM_Buffer_AddrCtrl | |
NoAltPal: jmp IncSubtask ;now onto the next task | |
;------------------------------------------------------------------------------------- | |
WriteTopStatusLine: | |
lda #$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 | |
lda #$20 ;write address for world-area number on screen | |
sta VRAM_Buffer1,x | |
lda #$73 | |
sta VRAM_Buffer1+1,x | |
lda #$03 ;write length for it | |
sta VRAM_Buffer1+2,x | |
ldy WorldNumber ;first the world number | |
iny | |
tya | |
sta VRAM_Buffer1+3,x | |
lda #$28 ;next the dash | |
sta VRAM_Buffer1+4,x | |
ldy LevelNumber ;next the level number | |
iny ;increment for proper number display | |
tya | |
sta VRAM_Buffer1+5,x | |
lda #$00 ;put null terminator on | |
sta VRAM_Buffer1+6,x | |
txa ;move the buffer offset up by 6 bytes | |
clc | |
adc #$06 | |
sta VRAM_Buffer1_Offset | |
jmp IncSubtask | |
;------------------------------------------------------------------------------------- | |
DisplayTimeUp: | |
lda GameTimerExpiredFlag ;if game timer not expired, increment task | |
beq NoTimeUp ;control 2 tasks forward, otherwise, stay here | |
lda #$00 | |
sta GameTimerExpiredFlag ;reset timer expiration flag | |
lda #$02 ;output time-up screen to buffer | |
jmp OutputInter | |
NoTimeUp: inc ScreenRoutineTask ;increment control task 2 tasks forward | |
jmp IncSubtask | |
;------------------------------------------------------------------------------------- | |
DisplayIntermediate: | |
lda OperMode ;check primary mode of operation | |
beq NoInter ;if in title screen mode, skip this | |
cmp #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 | |
bne NoInter ;and branch if found | |
ldy AreaType ;check if we are on castle level | |
cpy #$03 ;and if so, branch (possibly residual) | |
beq PlayerInter | |
lda DisableIntermediate ;if this flag is set, skip intermediate lives display | |
bne NoInter ;and jump to specific task, otherwise | |
PlayerInter: jsr DrawPlayer_Intermediate ;put player in appropriate place for | |
lda #$01 ;lives display, then output lives display to buffer | |
OutputInter: jsr WriteGameText | |
jsr ResetScreenTimer | |
lda #$00 | |
sta DisableScreenFlag ;reenable screen output | |
rts | |
GameOverInter: lda #$12 ;set screen timer | |
sta ScreenTimer | |
lda #$03 ;output game over screen to buffer | |
jsr WriteGameText | |
jmp IncModeTask_B | |
NoInter: lda #$08 ;set for specific task and leave | |
sta ScreenRoutineTask | |
rts | |
;------------------------------------------------------------------------------------- | |
AreaParserTaskControl: | |
inc DisableScreenFlag ;turn off screen | |
TaskLoop: jsr AreaParserTaskHandler ;render column set of current area | |
lda AreaParserTaskNum ;check number of tasks | |
bne TaskLoop ;if tasks still not all done, do another one | |
dec ColumnSets ;do we need to render more column sets? | |
bpl OutputCol | |
inc ScreenRoutineTask ;if not, move on to the next task | |
OutputCol: lda #$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: | |
lda OperMode ;are we in title screen mode? | |
bne IncModeTask_B ;if not, exit | |
lda #>TitleScreenDataOffset ;load address $1ec0 into | |
sta PPU_ADDRESS ;the vram address register | |
lda #<TitleScreenDataOffset | |
sta PPU_ADDRESS | |
lda #$03 ;put address $0300 into | |
sta $01 ;the indirect at $00 | |
ldy #$00 | |
sty $00 | |
lda PPU_DATA ;do one garbage read | |
OutputTScr: lda PPU_DATA ;get title screen from chr-rom | |
sta ($00),y ;store 256 bytes into buffer | |
iny | |
bne ChkHiByte ;if not past 256 bytes, do not increment | |
inc $01 ;otherwise increment high byte of indirect | |
ChkHiByte: lda $01 ;check high byte? | |
cmp #$04 ;at $0400? | |
bne OutputTScr ;if not, loop back and do another | |
cpy #$3a ;check if offset points past end of data | |
bcc OutputTScr ;if not, loop back and do another | |
lda #$05 ;set buffer transfer control to $0300, | |
jmp SetVRAMAddr_B ;increment task and exit | |
;------------------------------------------------------------------------------------- | |
ClearBuffersDrawIcon: | |
lda OperMode ;check game mode | |
bne IncModeTask_B ;if not title screen mode, leave | |
ldx #$00 ;otherwise, clear buffer space | |
TScrClear: sta VRAM_Buffer1-1,x | |
sta VRAM_Buffer1-1+$100,x | |
dex | |
bne TScrClear | |
jsr DrawMushroomIcon ;draw player select icon | |
IncSubtask: inc ScreenRoutineTask ;move onto next task | |
rts | |
;------------------------------------------------------------------------------------- | |
WriteTopScore: | |
lda #$fa ;run display routine to display top score on title | |
jsr UpdateNumber | |
IncModeTask_B: inc 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 | |
cpy #$04 ;if set to do top status bar or world/lives display, | |
bcc LdGameText ;branch to use current offset as-is | |
cpy #$08 ;if set to do time-up or game over, | |
bcc Chk2Players ;branch to check players | |
ldy #$08 ;otherwise warp zone, therefore set offset | |
Chk2Players: lda NumberOfPlayers ;check for number of players | |
bne LdGameText ;if there are two, use current offset to also print name | |
iny ;otherwise increment offset by one to not print name | |
LdGameText: ldx GameTextOffsets,y ;get offset to message we want to print | |
ldy #$00 | |
GameTextLoop: lda GameText,x ;load message data | |
cmp #$ff ;check for terminator | |
beq EndGameText ;branch to end text if found | |
sta 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: lda #$00 ;put null terminator at end | |
sta VRAM_Buffer1,y | |
pla ;pull original text number from stack | |
tax | |
cmp #$04 ;are we printing warp zone? | |
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 | |
adc #$01 | |
cmp #10 ;more than 9 lives? | |
bcc PutLives | |
sbc #10 ;if so, subtract 10 and put a crown tile | |
ldy #$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 | |
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 | |
cpy #GameOverModeValue | |
beq ChkLuigi | |
eor #%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 | |
ldy #$04 | |
NameLoop: lda LuigiName,y ;otherwise, replace "MARIO" with "LUIGI" | |
sta VRAM_Buffer1+3,y | |
dey | |
bpl NameLoop ;do this until each letter is replaced | |
ExitChkName: rts | |
PrintWarpZoneNumbers: | |
sbc #$04 ;subtract 4 and then shift to the left | |
asl ;twice to get proper warp zone number | |
asl ;offset | |
tax | |
ldy #$00 | |
WarpNumLoop: lda WarpZoneNumbers,x ;print warp zone numbers into the | |
sta VRAM_Buffer1+27,y ;placeholders from earlier | |
inx | |
iny ;put a number in every fourth space | |
iny | |
iny | |
iny | |
cpy #$0c | |
bcc WarpNumLoop | |
lda #$2c ;load new buffer pointer at end of message | |
jmp SetVRAMOffset | |
;------------------------------------------------------------------------------------- | |
ResetSpritesAndScreenTimer: | |
lda ScreenTimer ;check if screen timer has expired | |
bne NoReset ;if not, branch to leave | |
jsr MoveAllSpritesOffscreen ;otherwise reset sprites now | |
ResetScreenTimer: | |
lda #$07 ;reset timer again | |
sta ScreenTimer | |
inc 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: | |
lda CurrentColumnPos ;store LSB of where we're at | |
and #$01 | |
sta $05 | |
ldy VRAM_Buffer2_Offset ;store vram buffer offset | |
sty $00 | |
lda CurrentNTAddr_Low ;get current name table address we're supposed to render | |
sta VRAM_Buffer2+1,y | |
lda CurrentNTAddr_High | |
sta VRAM_Buffer2,y | |
lda #$9a ;store length byte of 26 here with d7 set | |
sta VRAM_Buffer2+2,y ;to increment by 32 (in columns) | |
lda #$00 ;init attribute row | |
sta $04 | |
tax | |
DrawMTLoop: stx $01 ;store init value of 0 or incremented offset for buffer | |
lda MetatileBuffer,x ;get first metatile number, and mask out all but 2 MSB | |
and #%11000000 | |
sta $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 | |
lda MetatileGraphics_Low,y ;get address to graphics table from here | |
sta $06 | |
lda MetatileGraphics_High,y | |
sta $07 | |
lda MetatileBuffer,x ;get metatile number again | |
asl ;multiply by 4 and use as tile offset | |
asl | |
sta $02 | |
lda AreaParserTaskNum ;get current task number for level processing and | |
and #%00000001 ;mask out all but LSB, then invert LSB, multiply by 2 | |
eor #%00000001 ;to get the correct column position in the metatile, | |
asl ;then add to the tile offset so we can draw either side | |
adc $02 ;of the metatiles | |
tay | |
ldx $00 ;use vram buffer offset from before as X | |
lda ($06),y | |
sta VRAM_Buffer2+3,x ;get first tile number (top left or top right) and store | |
iny | |
lda ($06),y ;now get the second (bottom left or bottom right) and store | |
sta VRAM_Buffer2+4,x | |
ldy $04 ;get current attribute row | |
lda $05 ;get LSB of current column where we're at, and | |
bne RightCheck ;branch if set (clear = left attrib, set = right) | |
lda $01 ;get current row we're rendering | |
lsr ;branch if LSB set (clear = top left, set = bottom left) | |
bcs LLeft | |
rol $03 ;rotate attribute bits 3 to the left | |
rol $03 ;thus in d1-d0, for upper left square | |
rol $03 | |
jmp SetAttrib | |
RightCheck: lda $01 ;get LSB of current row we're rendering | |
lsr ;branch if set (clear = top right, set = bottom right) | |
bcs NextMTRow | |
lsr $03 ;shift attribute bits 4 to the right | |
lsr $03 ;thus in d3-d2, for upper right square | |
lsr $03 | |
lsr $03 | |
jmp SetAttrib | |
LLeft: lsr $03 ;shift attribute bits 2 to the right | |
lsr $03 ;thus in d5-d4 for lower left square | |
NextMTRow: inc $04 ;move onto next attribute row | |
SetAttrib: lda AttributeBuffer,y ;get previously saved bits from before | |
ora $03 ;if any, and put new bits, if any, onto | |
sta AttributeBuffer,y ;the old, and store | |
inc $00 ;increment vram buffer offset by 2 | |
inc $00 | |
ldx $01 ;get current gfx buffer row, and check for | |
inx ;the bottom of the screen | |
cpx #$0d | |
bcc DrawMTLoop ;if not there yet, loop back | |
ldy $00 ;get current vram buffer offset, increment by 3 | |
iny ;(for name table address and length bytes) | |
iny | |
iny | |
lda #$00 | |
sta VRAM_Buffer2,y ;put null terminator at end of data for name table | |
sty VRAM_Buffer2_Offset ;store new buffer offset | |
inc CurrentNTAddr_Low ;increment name table address low | |
lda CurrentNTAddr_Low ;check current low byte | |
and #%00011111 ;if no wraparound, just skip this part | |
bne ExitDrawM | |
lda #$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 | |
eor #%00000100 ;to move onto the next appropriate name table | |
sta CurrentNTAddr_High | |
ExitDrawM: jmp SetVRAMCtrl ;jump to set buffer to $0341 and leave | |
;------------------------------------------------------------------------------------- | |
;$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 | |
and #%00011111 ;to be written to, mask out all but 5 LSB, | |
sec ;subtract four | |
sbc #$04 | |
and #%00011111 ;mask out bits again and store | |
sta $01 | |
lda CurrentNTAddr_High ;get high byte and branch if borrow not set | |
bcs SetATHigh | |
eor #%00000100 ;otherwise invert d2 | |
SetATHigh: and #%00000100 ;mask out all other bits | |
ora #$23 ;add $2300 to the high byte and store | |
sta $00 | |
lda $01 ;get low byte - 4, divide by 4, add offset for | |
lsr ;attribute table and store | |
lsr | |
adc #$c0 ;we should now have the appropriate block of | |
sta $01 ;attribute table in our temp address | |
ldx #$00 | |
ldy VRAM_Buffer2_Offset ;get buffer offset | |
AttribLoop: lda $00 | |
sta VRAM_Buffer2,y ;store high byte of attribute table address | |
lda $01 | |
clc ;get low byte, add 8 because we want to start | |
adc #$08 ;below the status bar, and store | |
sta VRAM_Buffer2+1,y | |
sta $01 ;also store in temp again | |
lda AttributeBuffer,x ;fetch current attribute table byte and store | |
sta VRAM_Buffer2+3,y ;in the buffer | |
lda #$01 | |
sta VRAM_Buffer2+2,y ;store length of 1 in buffer | |
lsr | |
sta 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 | |
cpx #$07 ;if we're at the end yet | |
bcc AttribLoop | |
sta VRAM_Buffer2,y ;put null terminator at the end | |
sty VRAM_Buffer2_Offset ;store offset in case we want to do any more | |
SetVRAMCtrl: lda #$06 | |
sta VRAM_Buffer_AddrCtrl ;set buffer to $0341 and leave | |
rts | |
;------------------------------------------------------------------------------------- | |
;$00 - used as temporary counter in ColorRotation | |
ColorRotatePalette: | |
.db $27, $27, $27, $17, $07, $17 | |
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 | |
and #$07 ;mask out all but three LSB | |
bne ExitColorRot ;branch if not set to zero to do this every eighth frame | |
ldx VRAM_Buffer1_Offset ;check vram buffer offset | |
cpx #$31 | |
bcs ExitColorRot ;if offset over 48 bytes, branch to leave | |
tay ;otherwise use frame counter's 3 LSB as offset here | |
GetBlankPal: lda BlankPalette,y ;get blank palette for palette 3 | |
sta VRAM_Buffer1,x ;store it in the vram buffer | |
inx ;increment offsets | |
iny | |
cpy #$08 | |
bcc GetBlankPal ;do this until all bytes are copied | |
ldx VRAM_Buffer1_Offset ;get current vram buffer offset | |
lda #$03 | |
sta $00 ;set counter here | |
lda AreaType ;get area type | |
asl ;multiply by 4 to get proper offset | |
asl | |
tay ;save as offset here | |
GetAreaPal: lda Palette3Data,y ;fetch palette to be written based on area type | |
sta VRAM_Buffer1+3,x ;store it to overwrite blank palette in vram buffer | |
iny | |
inx | |
dec $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 | |
lda ColorRotatePalette,y | |
sta 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 | |
adc #$07 | |
sta VRAM_Buffer1_Offset | |
inc ColorRotateOffset ;increment color cycling offset | |
lda ColorRotateOffset | |
cmp #$06 ;check to see if it's still in range | |
bcc ExitColorRot ;if so, branch to leave | |
lda #$00 | |
sta ColorRotateOffset ;otherwise, init to keep it in range | |
ExitColorRot: rts ;leave | |
;------------------------------------------------------------------------------------- | |
;$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 | |
.db $26, $26, $26, $26 | |
RemoveCoin_Axe: | |
ldy #$41 ;set low byte so offset points to $0341 | |
lda #$03 ;load offset for default blank metatile | |
ldx AreaType ;check area type | |
bne WriteBlankMT ;if not water type, use offset | |
lda #$04 ;otherwise load offset for blank metatile used in water | |
WriteBlankMT: jsr PutBlockMetatile ;do a sub to write blank metatile to vram buffer | |
lda #$06 | |
sta VRAM_Buffer_AddrCtrl ;set vram address controller to $0341 and leave | |
rts | |
ReplaceBlockMetatile: | |
jsr WriteBlockMetatile ;write metatile to vram buffer to replace block object | |
inc Block_ResidualCounter ;increment unused counter (residual code) | |
dec Block_RepFlag,x ;decrement flag (residual code) | |
rts ;leave | |
DestroyBlockMetatile: | |
lda #$00 ;force blank metatile if branched/jumped to this point | |
WriteBlockMetatile: | |
ldy #$03 ;load offset for blank metatile | |
cmp #$00 ;check contents of A for blank metatile | |
beq UseBOffset ;branch if found (unconditional if branched from 8a6b) | |
ldy #$00 ;load offset for brick metatile w/ line | |
cmp #$58 | |
beq UseBOffset ;use offset if metatile is brick with coins (w/ line) | |
cmp #$51 | |
beq UseBOffset ;use offset if metatile is breakable brick w/ line | |
iny ;increment offset for brick metatile w/o line | |
cmp #$5d | |
beq UseBOffset ;use offset if metatile is brick with coins (w/o line) | |
cmp #$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 | |
adc #10 | |
jmp SetVRAMOffset ;branch to store as new vram buffer offset | |
PutBlockMetatile: | |
stx $00 ;store control bit from SprDataOffset_Ctrl | |
sty $01 ;store vram buffer offset for next byte | |
asl | |
asl ;multiply A by four and use as X | |
tax | |
ldy #$20 ;load high byte for name table 0 | |
lda $06 ;get low byte of block buffer pointer | |
cmp #$d0 ;check to see if we're on odd-page block buffer | |
bcc SaveHAdder ;if not, use current high byte | |
ldy #$24 ;otherwise load high byte for name table 1 | |
SaveHAdder: sty $03 ;save high byte here | |
and #$0f ;mask out high nybble of block buffer pointer | |
asl ;multiply by 2 to get appropriate name table low byte | |
sta $04 ;and then store it here | |
lda #$00 | |
sta $05 ;initialize temp high byte | |
lda $02 ;get vertical high nybble offset used in block buffer routine | |
clc | |
adc #$20 ;add 32 pixels for the status bar | |
asl | |
rol $05 ;shift and rotate d7 onto d0 and d6 into carry | |
asl | |
rol $05 ;shift and rotate d6 onto d0 and d5 into carry | |
adc $04 ;add low byte of name table and carry to vertical high nybble | |
sta $04 ;and store here | |
lda $05 ;get whatever was in d7 and d6 of vertical high nybble | |
adc #$00 ;add carry | |
clc | |
adc $03 ;then add high byte of name table | |
sta $05 ;store here | |
ldy $01 ;get vram buffer offset to be used | |
RemBridge: lda BlockGfxData,x ;write top left and top right | |
sta VRAM_Buffer1+2,y ;tile numbers into first spot | |
lda BlockGfxData+1,x | |
sta VRAM_Buffer1+3,y | |
lda BlockGfxData+2,x ;write bottom left and bottom | |
sta VRAM_Buffer1+7,y ;right tiles numbers into | |
lda BlockGfxData+3,x ;second spot | |
sta VRAM_Buffer1+8,y | |
lda $04 | |
sta VRAM_Buffer1,y ;write low byte of name table | |
clc ;into first slot as read | |
adc #$20 ;add 32 bytes to value | |
sta VRAM_Buffer1+5,y ;write low byte of name table | |
lda $05 ;plus 32 bytes into second slot | |
sta VRAM_Buffer1-1,y ;write high byte of name | |
sta VRAM_Buffer1+4,y ;table address to both slots | |
lda #$02 | |
sta VRAM_Buffer1+1,y ;put length of 2 in | |
sta VRAM_Buffer1+6,y ;both slots | |
lda #$00 | |
sta VRAM_Buffer1+9,y ;put null terminator at end | |
ldx $00 ;get offset control bit here | |
rts ;and leave | |
;------------------------------------------------------------------------------------- | |
;METATILE GRAPHICS TABLE | |
MetatileGraphics_Low: | |
.db <Palette0_MTiles, <Palette1_MTiles, <Palette2_MTiles, <Palette3_MTiles | |
MetatileGraphics_High: | |
.db >Palette0_MTiles, >Palette1_MTiles, >Palette2_MTiles, >Palette3_MTiles | |
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, $26 ;mountain left | |
.db $26, $26, $34, $26 ;mountain left bottom/middle center | |
.db $24, $31, $24, $32 ;mountain middle top | |
.db $33, $26, $24, $33 ;mountain right | |
.db $34, $26, $26, $26 ;mountain right bottom | |
.db $26, $26, $26, $26 ;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 $26, $26, $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 | |
.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 $26, $93, $26, $93 ;sideways pipe shaft bottom | |
.db $90, $94, $69, $69 ;sideways pipe joint bottom | |
.db $a4, $e9, $ea, $eb ;seaplant | |
.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 $a2, $a2, $a3, $a3 ;vertical rope | |
.db $99, $24, $99, $24 ;horizontal rope | |
.db $24, $a2, $3e, $3f ;left pulley | |
.db $5b, $5c, $24, $a3 ;right pulley | |
.db $24, $24, $24, $24 ;blank used for balance rope | |
.db $9d, $47, $9e, $47 ;castle top | |
.db $47, $47, $27, $27 ;castle window left | |
.db $47, $47, $47, $47 ;castle brick wall | |
.db $27, $27, $47, $47 ;castle window right | |
.db $a9, $47, $aa, $47 ;castle top w/ brick | |
.db $9b, $27, $9c, $27 ;entrance top | |
.db $27, $27, $27, $27 ;entrance bottom | |
.db $52, $52, $52, $52 ;green ledge stump | |
.db $80, $a0, $81, $a1 ;fence | |
.db $be, $be, $bf, $bf ;tree trunk | |
.db $75, $ba, $76, $bb ;mushroom stump top | |
.db $ba, $ba, $bb, $bb ;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 $86, $8a, $87, $8b ;water pipe top | |
.db $8e, $91, $8f, $92 ;water pipe bottom | |
.db $24, $2f, $24, $3d ;flag ball (residual object) | |
Palette2_MTiles: | |
.db $24, $24, $24, $35 ;cloud left | |
.db $36, $25, $37, $25 ;cloud middle | |
.db $24, $38, $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, $26, $41, $26 ;water/lava top | |
.db $26, $26, $26, $26 ;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 | |
.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 | |
JumpEngine: | |
asl ;shift bit from contents of A | |
tay | |
pla ;pull saved return address from stack | |
sta $04 ;save to indirect | |
pla | |
sta $05 | |
iny | |
lda ($04),y ;load pointer from indirect | |
sta $06 ;note that if an RTS is performed in next routine | |
iny ;it will return to the execution before the sub | |
lda ($04),y ;that called this routine | |
sta $07 | |
jmp ($06) ;jump to the address we loaded | |
;------------------------------------------------------------------------------------- | |
InitializeNameTables: | |
lda PPU_STATUS ;reset flip-flop | |
lda Mirror_PPU_CTRL_REG1 ;load mirror of ppu reg $2000 | |
ora #%00010000 ;set sprites for first 4k and background for second 4k | |
and #%11110000 ;clear rest of lower nybble, leave higher alone | |
jsr WritePPUReg1 | |
lda #$24 ;set vram address to start of name table 1 | |
jsr WriteNTAddr | |
lda #$20 ;and then set it to name table 0 | |
WriteNTAddr: sta PPU_ADDRESS | |
lda #$00 | |
sta PPU_ADDRESS | |
ldx #$04 ;clear name table with blank tile #24 | |
ldy #$c0 | |
lda #$24 | |
InitNTLoop: sta PPU_DATA ;count out exactly 768 tiles | |
dey | |
bne InitNTLoop | |
dex | |
bne InitNTLoop | |
ldy #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: sta PPU_DATA | |
dey | |
bne InitATLoop | |
sta HorizontalScroll ;reset scroll variables | |
sta VerticalScroll | |
jmp InitScroll ;initialize scroll registers to zero | |
;------------------------------------------------------------------------------------- | |
;$00 - temp joypad bit | |
ReadJoypads: | |
lda #$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: ldy #$08 | |
PortLoop: pha ;push previous bit onto stack | |
lda JOYPAD_PORT,x ;read current bit on joypad port | |
sta $00 ;check d1 and d0 of port output | |
lsr ;this is necessary on the old | |
ora $00 ;famicom systems in japan | |
lsr | |
pla ;read bits from stack | |
rol ;rotate bit from carry flag | |
dey | |
bne PortLoop ;count down bits left | |
sta SavedJoypadBits,x ;save controller status here always | |
pha | |
and #%00110000 ;check for select or start | |
and JoypadBitMask,x ;if neither saved state nor current state | |
beq Save8Bits ;have any of these two set, branch | |
pla | |
and #%11001111 ;otherwise store without select | |
sta SavedJoypadBits,x ;or start bits and leave | |
rts | |
Save8Bits: pla | |
sta JoypadBitMask,x ;save with all bits in another place and leave | |
rts | |
;------------------------------------------------------------------------------------- | |
;$00 - vram buffer address table low | |
;$01 - vram buffer address table high | |
WriteBufferToScreen: | |
sta PPU_ADDRESS ;store high byte of vram address | |
iny | |
lda ($00),y ;load next byte (second) | |
sta PPU_ADDRESS ;store low byte of vram address | |
iny | |
lda ($00),y ;load next byte (third) | |
asl ;shift to left and save in stack | |
pha | |
lda Mirror_PPU_CTRL_REG1 ;load mirror of $2000, | |
ora #%00000100 ;set ppu to increment by 32 by default | |
bcs SetupWrites ;if d7 of third byte was clear, ppu will | |
and #%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 | |
ora #%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: lda ($00),y ;load more data from buffer and write to vram | |
sta PPU_DATA | |
dex ;done writing? | |
bne OutputToVRAM | |
sec | |
tya | |
adc $00 ;add end length plus one to the indirect at $00 | |
sta $00 ;to allow this routine to read another set of updates | |
lda #$00 | |
adc $01 | |
sta $01 | |
lda #$3f ;sets vram address to $3f00 | |
sta PPU_ADDRESS | |
lda #$00 | |
sta PPU_ADDRESS | |
sta PPU_ADDRESS ;then reinitializes it for some reason | |
sta PPU_ADDRESS | |
UpdateScreen: ldx PPU_STATUS ;reset flip-flop | |
ldy #$00 ;load first byte from indirect as a pointer | |
lda ($00),y | |
bne WriteBufferToScreen ;if byte is zero we have no further updates to make here | |
InitScroll: sta PPU_SCROLL_REG ;store contents of A into scroll registers | |
sta PPU_SCROLL_REG ;and end whatever subroutine led us here | |
rts | |
;------------------------------------------------------------------------------------- | |
WritePPUReg1: | |
sta PPU_CTRL_REG1 ;write contents of A to PPU register 1 | |
sta Mirror_PPU_CTRL_REG1 ;and its mirror | |
rts | |
;------------------------------------------------------------------------------------- | |
;$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 $00 ;store player-specific offset | |
jsr OutputNumbers ;use first nybble to print the coin display | |
lda $00 ;move high nybble to low | |
lsr ;and print to score display | |
lsr | |
lsr | |
lsr | |
OutputNumbers: | |
clc ;add 1 to low nybble | |
adc #$01 | |
and #%00001111 ;mask out high nybble | |
cmp #$06 | |
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 | |
lda #$20 ;put at top of screen by default | |
cpy #$00 ;are we writing top score on title screen? | |
bne SetupNums | |
lda #$22 ;if so, put further down on the screen | |
SetupNums: sta VRAM_Buffer1,x | |
lda StatusBarData,y ;write low vram address and length of thing | |
sta VRAM_Buffer1+1,x ;we're printing to the buffer | |
lda StatusBarData+1,y | |
sta VRAM_Buffer1+2,x | |
sta $03 ;save length byte in counter | |
stx $02 ;and buffer pointer elsewhere for now | |
pla ;pull original incremented value from stack | |
tax | |
lda StatusBarOffset,x ;load offset to value we want to write | |
sec | |
sbc StatusBarData+1,y ;subtract from length byte we read before | |
tay ;use value as offset to display digits | |
ldx $02 | |
DigitPLoop: lda DisplayDigits,y ;write digits to the buffer | |
sta VRAM_Buffer1+3,x | |
inx | |
iny | |
dec $03 ;do this until all the digits are written | |
bne DigitPLoop | |
lda #$00 ;put null terminator at end | |
sta 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 | |
;------------------------------------------------------------------------------------- | |
DigitsMathRoutine: | |
lda OperMode ;check mode of operation | |
cmp #TitleScreenModeValue | |
beq EraseDMods ;if in title screen mode, branch to lock score | |
ldx #$05 | |
AddModLoop: lda DigitModifier,x ;load digit amount to increment | |
clc | |
adc DisplayDigits,y ;add to current digit | |
bmi BorrowOne ;if result is a negative number, branch to subtract | |
cmp #10 | |
bcs CarryOne ;if digit greater than $09, branch to add | |
StoreNewD: sta 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: lda #$00 ;store zero here | |
ldx #$06 ;start with the last digit | |
EraseMLoop: sta DigitModifier-1,x ;initialize the digit amounts to increment | |
dex | |
bpl EraseMLoop ;do this until they're all reset, then leave | |
rts | |
BorrowOne: dec DigitModifier-1,x ;decrement the previous digit, then put $09 in | |
lda #$09 ;the game timer digit we're currently on to "borrow | |
bne StoreNewD ;the one", then do an unconditional branch back | |
CarryOne: sec ;subtract ten from our digit to make it a | |
sbc #10 ;proper BCD number, then increment the digit | |
inc DigitModifier-1,x ;preceding current digit to "carry the one" properly | |
jmp StoreNewD ;go back to just after we branched here | |
;------------------------------------------------------------------------------------- | |
UpdateTopScore: | |
ldx #$05 ;start with mario's score | |
jsr TopScoreCheck | |
ldx #$0b ;now do luigi's score | |
TopScoreCheck: | |
ldy #$05 ;start with the lowest digit | |
sec | |
GetScoreDiff: lda PlayerScoreDisplay,x ;subtract each player digit from each high score digit | |
sbc 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 | |
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: lda PlayerScoreDisplay,x ;store player's score digits into high score memory area | |
sta TopScoreDisplay,y | |
inx | |
iny | |
cpy #$06 ;do this until we have stored them all | |
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: | |
ldy #$6f ;clear all memory as in initialization procedure, | |
jsr InitializeMemory ;but this time, clear only as far as $076f | |
ldy #$1f | |
ClrSndLoop: sta SoundMemory,y ;clear out memory used | |
dey ;by the sound engines | |
bpl ClrSndLoop | |
lda #$18 ;set demo timer | |
sta DemoTimer | |
jsr LoadAreaPointer | |
InitializeArea: | |
ldy #$4b ;clear all memory again, only as far as $074b | |
jsr InitializeMemory ;this is only necessary if branching from | |
ldx #$21 | |
lda #$00 | |
ClrTimersLoop: sta 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 | |
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 | |
ldy #$20 ;if on odd numbered page, use $2480 as start of rendering | |
and #%00000001 ;otherwise use $2080, this address used later as name table | |
beq SetInitNTHigh ;address for rendering of game area | |
ldy #$24 | |
SetInitNTHigh: sty CurrentNTAddr_High ;store name table address | |
ldy #$80 | |
sty CurrentNTAddr_Low | |
asl ;store LSB of page number in high nybble | |
asl ;of block buffer column position | |
asl | |
asl | |
sta BlockBufferColumnPos | |
dec AreaObjectLength ;set area object lengths for all empty | |
dec AreaObjectLength+1 | |
dec AreaObjectLength+2 | |
lda #$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 | |
bne SetSecHard ;if so, activate the secondary no matter where we're at | |
lda WorldNumber ;otherwise check world number | |
cmp #World5 ;if less than 5, do not activate secondary | |
bcc CheckHalfway | |
bne SetSecHard ;if not equal to, then world > 5, thus activate | |
lda LevelNumber ;otherwise, world 5, so check level number | |
cmp #Level3 ;if 1 or 2, do not set secondary hard mode flag | |
bcc CheckHalfway | |
SetSecHard: inc SecondaryHardMode ;set secondary hard mode flag for areas 5-3 and beyond | |
CheckHalfway: lda HalfwayPage | |
beq DoneInitArea | |
lda #$02 ;if halfway page set, overwrite start position from header | |
sta PlayerEntranceCtrl | |
DoneInitArea: lda #Silence ;silence music | |
sta AreaMusicQueue | |
lda #$01 ;disable screen output | |
sta DisableScreenFlag | |
inc OperMode_Task ;increment one of the modes | |
rts | |
;------------------------------------------------------------------------------------- | |
PrimaryGameSetup: | |
lda #$01 | |
sta FetchNewGameTimerFlag ;set flag to load game timer from header | |
sta PlayerSize ;set player's size to small | |
lda #$02 | |
sta NumberofLives ;give each player three lives | |
sta OffScr_NumberofLives | |
SecondaryGameSetup: | |
lda #$00 | |
sta DisableScreenFlag ;enable screen output | |
tay | |
ClearVRLoop: sta 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 | |
lda #$ff | |
sta BalPlatformAlignment ;initialize balance platform assignment flag | |
lda ScreenLeft_PageLoc ;get left side page location | |
lsr Mirror_PPU_CTRL_REG1 ;shift LSB of ppu register #1 mirror out | |
and #$01 ;mask out all but LSB of page location | |
ror ;rotate LSB of page location into carry then onto mirror | |
rol Mirror_PPU_CTRL_REG1 ;this is to set the proper PPU name table | |
jsr GetAreaMusic ;load proper music into queue | |
lda #$38 ;load sprite shuffle amounts to be used later | |
sta SprShuffleAmt+2 | |
lda #$48 | |
sta SprShuffleAmt+1 | |
lda #$58 | |
sta SprShuffleAmt | |
ldx #$0e ;load default OAM offsets into $06e4-$06f2 | |
ShufAmtLoop: lda DefaultSprOffsets,x | |
sta SprDataOffset,x | |
dex ;do this until they're all set | |
bpl ShufAmtLoop | |
ldy #$03 ;set up sprite #0 | |
ISpr0Loop: lda Sprite0Data,y | |
sta Sprite_Data,y | |
dey | |
bpl ISpr0Loop | |
jsr DoNothing2 ;these jsrs doesn't do anything useful | |
jsr DoNothing1 | |
inc Sprite0HitDetectFlag ;set sprite #0 check flag | |
inc OperMode_Task ;increment to next task | |
rts | |
;------------------------------------------------------------------------------------- | |
;$06 - RAM address low | |
;$07 - RAM address high | |
InitializeMemory: | |
ldx #$07 ;set initial high byte to $0700-$07ff | |
lda #$00 ;set initial low byte to start of page (at $00 of page) | |
sta $06 | |
InitPageLoop: stx $07 | |
InitByteLoop: cpx #$01 ;check to see if we're on the stack ($0100-$01ff) | |
bne InitByte ;if not, go ahead anyway | |
cpy #$60 ;otherwise, check to see if we're at $0160-$01ff | |
bcs SkipByte ;if so, skip write | |
InitByte: sta ($06),y ;otherwise, initialize byte with current low byte in Y | |
SkipByte: dey | |
cpy #$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 | |
rts | |
;------------------------------------------------------------------------------------- | |
MusicSelectData: | |
.db WaterMusic, GroundMusic, UndergroundMusic, CastleMusic | |
.db CloudMusic, PipeIntroMusic | |
GetAreaMusic: | |
lda OperMode ;if in title screen mode, leave | |
beq ExitGetM | |
lda AltEntranceControl ;check for specific alternate mode of entry | |
cmp #$02 ;if found, branch without checking starting position | |
beq ChkAreaType ;from area object data header | |
ldy #$05 ;select music for pipe intro scene by default | |
lda PlayerEntranceCtrl ;check value from level header for certain values | |
cmp #$06 | |
beq StoreMusic ;load music for pipe intro scene if header | |
cmp #$07 ;start position either value $06 or $07 | |
beq StoreMusic | |
ChkAreaType: ldy AreaType ;load area type as offset for music bit | |
lda CloudTypeOverride | |
beq StoreMusic ;check for cloud type override | |
ldy #$04 ;select music for cloud type level if found | |
StoreMusic: lda 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 | |
lda #$28 ;store value here | |
sta VerticalForceDown ;for fractional movement downwards if necessary | |
lda #$01 ;set high byte of player position and | |
sta PlayerFacingDir ;set facing direction so that player faces right | |
sta Player_Y_HighPos | |
lda #$00 ;set player state to on the ground by default | |
sta Player_State | |
dec Player_CollisionBits ;initialize player's collision bits | |
ldy #$00 ;initialize halfway page | |
sty HalfwayPage | |
lda AreaType ;check area type | |
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 | |
beq SetStPos | |
cpy #$01 | |
beq SetStPos | |
ldx AltYPosOffset-2,y ;if not 0 or 1, override $0710 with new offset in X | |
SetStPos: lda PlayerStarting_X_Pos,y ;load appropriate horizontal position | |
sta Player_X_Position ;and vertical positions for the player, using | |
lda 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 | |
lda 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 | |
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 | |
beq ChkOverR ;old game timer setting | |
lda 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 | |
lda #$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 | |
beq ChkSwimE | |
lda #$03 ;set player state to climbing | |
sta Player_State | |
ldx #$00 ;set offset for first slot, for block object | |
jsr InitBlock_XY_Pos | |
lda #$f0 ;set vertical coordinate for block object | |
sta Block_Y_Position | |
ldx #$05 ;set offset in X for last enemy object buffer slot | |
ldy #$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, | |
bne SetPESub ;skip this subroutine | |
jsr SetupBubble ;otherwise, execute sub to set up air bubbles | |
SetPESub: lda #$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: | |
inc DisableScreenFlag ;disable screen and sprite 0 check | |
lda #$00 | |
sta Sprite0HitDetectFlag | |
lda #Silence ;silence music | |
sta EventMusicQueue | |
dec NumberofLives ;take one life from player | |
bpl StillInGame ;if player still has lives, branch | |
lda #$00 | |
sta OperMode_Task ;initialize mode task, | |
lda #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 | |
and #$02 ;offset by one byte, otherwise | |
beq GetHalfway ;leave offset alone | |
inx | |
GetHalfway: ldy 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: and #%00001111 ;mask out all but lower nybble | |
cmp ScreenLeft_PageLoc | |
beq SetHalfway ;left side of screen must be at the halfway page, | |
bcc SetHalfway ;otherwise player must start at the | |
lda #$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: | |
lda #$00 ;reset screen routine task control for title screen, game, | |
sta ScreenRoutineTask ;and game over modes | |
sta Sprite0HitDetectFlag ;disable sprite 0 check | |
lda #GameOverMusic | |
sta EventMusicQueue ;put game over music in secondary queue | |
inc DisableScreenFlag ;disable screen output | |
inc OperMode_Task ;set secondary mode to 1 | |
rts | |
;------------------------------------------------------------------------------------- | |
RunGameOver: | |
lda #$00 ;reenable screen | |
sta DisableScreenFlag | |
lda SavedJoypad1Bits ;check controller for start pressed | |
and #Start_Button | |
bne TerminateGame | |
lda ScreenTimer ;if not pressed, wait for | |
bne GameIsOn ;screen timer to expire | |
TerminateGame: | |
lda #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 | |
lda #$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 | |
lda #$01 ;actual world and area numbers, then | |
sta PlayerSize ;reset player's size, status, and | |
inc FetchNewGameTimerFlag ;set game timer flag to reload | |
lda #$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 | |
lda #$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 | |
beq ExTrans | |
lda OffScr_NumberofLives ;does offscreen player have any lives left? | |
bmi ExTrans ;branch if not | |
lda CurrentPlayer ;invert bit to update | |
eor #%00000001 ;which player is on the screen | |
sta CurrentPlayer | |
ldx #$06 | |
TransLoop: lda OnscreenPlayerInfo,x ;transpose the information | |
pha ;of the onscreen player | |
lda OffscreenPlayerInfo,x ;with that of the offscreen player | |
sta OnscreenPlayerInfo,x | |
pla | |
sta OffscreenPlayerInfo,x | |
dex | |
bpl TransLoop | |
clc ;clear carry flag to get game going | |
ExTrans: rts | |
;------------------------------------------------------------------------------------- | |
DoNothing1: | |
lda #$ff ;this is residual code, this value is | |
sta $06c9 ;not used anywhere in the program | |
DoNothing2: | |
rts | |
;------------------------------------------------------------------------------------- | |
AreaParserTaskHandler: | |
ldy AreaParserTaskNum ;check number of tasks here | |
bne DoAPTasks ;if already set, go ahead | |
ldy #$08 | |
sty AreaParserTaskNum ;otherwise, set eight by default | |
DoAPTasks: dey | |
tya | |
jsr AreaParserTasks | |
dec AreaParserTaskNum ;if all tasks not complete do not | |
bne SkipATRender ;render attribute table yet | |
jsr RenderAttributeTables | |
SkipATRender: rts | |
AreaParserTasks: | |
jsr JumpEngine | |
.dw IncrementColumnPos | |
.dw RenderAreaGraphics | |
.dw RenderAreaGraphics | |
.dw AreaParserCore | |
.dw IncrementColumnPos | |
.dw RenderAreaGraphics | |
.dw RenderAreaGraphics | |
.dw AreaParserCore | |
;------------------------------------------------------------------------------------- | |
IncrementColumnPos: | |
inc CurrentColumnPos ;increment column where we're at | |
lda CurrentColumnPos | |
and #%00001111 ;mask out higher nybble | |
bne NoColWrap | |
sta CurrentColumnPos ;if no bits left set, wrap back to zero (0-f) | |
inc CurrentPageLoc ;and increment page number where we're at | |
NoColWrap: inc BlockBufferColumnPos ;increment column offset where we're at | |
lda BlockBufferColumnPos | |
and #%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: | |
.db $93, $00, $00, $11, $12, $12, $13, $00 ;clouds | |
.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 | |
beq RenderSceneryTerrain ;if not, go ahead and render background, foreground and terrain | |
jsr ProcessAreaData ;otherwise skip ahead and load level data | |
RenderSceneryTerrain: | |
ldx #$0c | |
lda #$00 | |
ClrMTBuf: sta MetatileBuffer,x ;clear out metatile buffer | |
dex | |
bpl ClrMTBuf | |
ldy BackgroundScenery ;do we need to render the background scenery? | |
beq RendFore ;if not, skip to check the foreground | |
lda CurrentPageLoc ;otherwise check for every third page | |
ThirdP: cmp #$03 | |
bmi RendBack ;if less than three we're there | |
sec | |
sbc #$03 ;if 3 or more, subtract 3 and | |
bpl ThirdP ;do an unconditional branch | |
RendBack: asl ;move results to higher nybble | |
asl | |
asl | |
asl | |
adc BSceneDataOffsets-1,y ;add to it offset loaded from here | |
adc CurrentColumnPos ;add to the result our current column position | |
tax | |
lda BackSceneryData,x ;load data from sum of offsets | |
beq RendFore ;if zero, no scenery for that part | |
pha | |
and #$0f ;save to stack and clear high nybble | |
sec | |
sbc #$01 ;subtract one (because low nybble is $01-$0c) | |
sta $00 ;save low nybble | |
asl ;multiply by three (shift to left and add result to old one) | |
adc $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) | |
lda #$03 ;use previously saved memory location for counter | |
sta $00 | |
SceLoop1: lda BackSceneryMetatiles,x ;load metatile data from offset of (lsb - 1) * 3 | |
sta MetatileBuffer,y ;store into buffer from offset of (msb / 16) | |
inx | |
iny | |
cpy #$0b ;if at this location, leave loop | |
beq RendFore | |
dec $00 ;decrement until counter expires, barring exception | |
bne SceLoop1 | |
RendFore: ldx ForegroundScenery ;check for foreground data needed or not | |
beq RendTerr ;if not, skip this part | |
ldy FSceneDataOffsets-1,x ;load offset from location offset by header value, then | |
ldx #$00 ;reinit X | |
SceLoop2: lda ForeSceneryData,y ;load data until counter expires | |
beq NoFore ;do not store if zero found | |
sta MetatileBuffer,x | |
NoFore: iny | |
inx | |
cpx #$0d ;store up to end of metatile buffer | |
bne SceLoop2 | |
RendTerr: ldy AreaType ;check world type for water level | |
bne TerMTile ;if not water level, skip this part | |
lda WorldNumber ;check world number, if not world number eight | |
cmp #World8 ;then skip this part | |
bne TerMTile | |
lda #$62 ;if set as water level and world number eight, | |
jmp StoreMT ;use castle wall metatile as terrain type | |
TerMTile: lda TerrainMetatiles,y ;otherwise get appropriate metatile for area type | |
ldy CloudTypeOverride ;check for cloud type override | |
beq StoreMT ;if not set, keep value otherwise | |
lda #$88 ;use cloud block terrain | |
StoreMT: sta $07 ;store value here | |
ldx #$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: lda TerrainRenderBits,y ;get one of the terrain rendering bit data | |
sta $00 | |
iny ;increment Y and use as offset next time around | |
sty $01 | |
lda CloudTypeOverride ;skip if value here is zero | |
beq NoCloud2 | |
cpx #$00 ;otherwise, check if we're doing the ceiling byte | |
beq NoCloud2 | |
lda $00 ;if not, mask out all but d3 | |
and #%00001000 | |
sta $00 | |
NoCloud2: ldy #$00 ;start at beginning of bitmasks | |
TerrBChk: lda Bitmasks,y ;load bitmask, then perform AND on contents of first byte | |
bit $00 | |
beq NextTBit ;if not set, skip this part (do not write terrain to buffer) | |
lda $07 | |
sta MetatileBuffer,x ;load terrain type metatile number and store into buffer here | |
NextTBit: inx ;continue until end of buffer | |
cpx #$0d | |
beq RendBBuf ;if we're at the end, break out of this loop | |
lda AreaType ;check world type for underground area | |
cmp #$02 | |
bne EndUChk ;if not underground, skip this part | |
cpx #$0b | |
bne EndUChk ;if we're at the bottom of the screen, override | |
lda #$54 ;old terrain type with ground level terrain type | |
sta $07 | |
EndUChk: iny ;increment bitmasks offset in Y | |
cpy #$08 | |
bne TerrBChk ;if not all bits checked, loop back | |
ldy $01 | |
bne TerrLoop ;unconditional branch, use Y to load next byte | |
RendBBuf: jsr ProcessAreaData ;do the area data loading routine now | |
lda BlockBufferColumnPos | |
jsr GetBlockBufferAddr ;get block buffer address from where we're at | |
ldx #$00 | |
ldy #$00 ;init index regs and start at beginning of smaller buffer | |
ChkMTLow: sty $00 | |
lda MetatileBuffer,x ;load stored metatile number | |
and #%11000000 ;mask out all but 2 MSB | |
asl | |
rol ;make %xx000000 into %000000xx | |
rol | |
tay ;use as offset in Y | |
lda MetatileBuffer,x ;reload original unmasked value here | |
cmp BlockBuffLowBounds,y ;check for certain values depending on bits set | |
bcs StrBlock ;if equal or greater, branch | |
lda #$00 ;if less, init value before storing | |
StrBlock: ldy $00 ;get offset for block buffer | |
sta ($06),y ;store value into block buffer | |
tya | |
clc ;add 16 (move down one row) to offset | |
adc #$10 | |
tay | |
inx ;increment column value | |
cpx #$0d | |
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 | |
ProcessAreaData: | |
ldx #$02 ;start at the end of area object buffer | |
ProcADLoop: stx ObjectOffset | |
lda #$00 ;reset flag | |
sta BehindAreaParserFlag | |
ldy AreaDataOffset ;get offset of area data pointer | |
lda (AreaData),y ;get first byte of area object | |
cmp #$fd ;if end-of-area, skip all this crap | |
beq RdyDecode | |
lda AreaObjectLength,x ;check area object buffer flag | |
bpl RdyDecode ;if buffer not negative, branch, otherwise | |
iny | |
lda (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 | |
bne Chk1Row13 | |
inc AreaObjectPageSel ;if not already set, set it now | |
inc AreaObjectPageLoc ;and increment page location | |
Chk1Row13: dey | |
lda (AreaData),y ;reread first byte of level object | |
and #$0f ;mask out high nybble | |
cmp #$0d ;row 13? | |
bne Chk1Row14 | |
iny ;if so, reread second byte of level object | |
lda (AreaData),y | |
dey ;decrement to get ready to read first byte | |
and #%01000000 ;check for d6 set (if not, object is page control) | |
bne CheckRear | |
lda AreaObjectPageSel ;if page select is set, do not reread | |
bne CheckRear | |
iny ;if d6 not set, reread second byte | |
lda (AreaData),y | |
and #%00011111 ;mask out all but 5 LSB and store in page control | |
sta AreaObjectPageLoc | |
inc AreaObjectPageSel ;increment page select | |
jmp NextAObj | |
Chk1Row14: cmp #$0e ;row 14? | |
bne CheckRear | |
lda BackloadingFlag ;check flag for saved page number and branch if set | |
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 | |
cmp CurrentPageLoc ;behind current page of renderer | |
bcc SetBehind ;if so branch | |
RdyDecode: jsr DecodeAreaData ;do sub and do not turn on flag | |
jmp ChkLength | |
SetBehind: inc BehindAreaParserFlag ;turn on flag if object is behind renderer | |
NextAObj: jsr IncAreaObjOffset ;increment buffer offset and move on | |
ChkLength: ldx ObjectOffset ;get buffer offset | |
lda AreaObjectLength,x ;check object length for anything stored here | |
bmi ProcLoopb ;if not, branch to handle loopback | |
dec 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 | |
bne ProcessAreaData ;branch if true to load more level data, otherwise | |
lda BackloadingFlag ;check for flag set if starting right of page $00 | |
bne ProcessAreaData ;branch if true to load more level data, otherwise leave | |
EndAParse: rts | |
IncAreaObjOffset: | |
inc AreaDataOffset ;increment offset of level pointer | |
inc AreaDataOffset | |
lda #$00 ;reset page select | |
sta AreaObjectPageSel | |
rts | |
DecodeAreaData: | |
lda AreaObjectLength,x ;check current buffer flag | |
bmi Chk1stB | |
ldy AreaObjOffsetBuffer,x ;if not, get offset from buffer | |
Chk1stB: ldx #$10 ;load offset of 16 for special row 15 | |
lda (AreaData),y ;get first byte of level object again | |
cmp #$fd | |
beq EndAParse ;if end of level, leave this routine | |
and #$0f ;otherwise, mask out low nybble | |
cmp #$0f ;row 15? | |
beq ChkRow14 ;if so, keep the offset of 16 | |
ldx #$08 ;otherwise load offset of 8 for special row 12 | |
cmp #$0c ;row 12? | |
beq ChkRow14 ;if so, keep the offset value of 8 | |
ldx #$00 ;otherwise nullify value by default | |
ChkRow14: stx $07 ;store whatever value we just loaded here | |
ldx ObjectOffset ;get object offset again | |
cmp #$0e ;row 14? | |
bne ChkRow13 | |
lda #$00 ;if so, load offset with $00 | |
sta $07 | |
lda #$2e ;and load A with another value | |
bne NormObj ;unconditional branch | |
ChkRow13: cmp #$0d ;row 13? | |
bne ChkSRows | |
lda #$22 ;if so, load offset with 34 | |
sta $07 | |
iny ;get next byte | |
lda (AreaData),y | |
and #%01000000 ;mask out all but d6 (page control obj bit) | |
beq LeavePar ;if d6 clear, branch to leave (we handled this earlier) | |
lda (AreaData),y ;otherwise, get byte again | |
and #%01111111 ;mask out d7 | |
cmp #$4b ;check for loop command in low nybble | |
bne Mask2MSB ;(plus d6 set for object other than page control) | |
inc LoopCommand ;if loop command, set loop command flag | |
Mask2MSB: and #%00111111 ;mask out d7 and d6 | |
jmp NormObj ;and jump | |
ChkSRows: cmp #$0c ;row 12-15? | |
bcs SpecObj | |
iny ;if not, get second byte of level object | |
lda (AreaData),y | |
and #%01110000 ;mask out all but d6-d4 | |
bne LrgObj ;if any bits set, branch to handle large object | |
lda #$16 | |
sta $07 ;otherwise set offset of 24 for small object | |
lda (AreaData),y ;reload second byte of level object | |
and #%00001111 ;mask out higher nybble and jump | |
jmp NormObj | |
LrgObj: sta $00 ;store value here (branch for large objects) | |
cmp #$70 ;check for vertical pipe object | |
bne NotWPipe | |
lda (AreaData),y ;if not, reload second byte | |
and #%00001000 ;mask out all but d3 (usage control bit) | |
beq NotWPipe ;if d3 clear, branch to get original value | |
lda #$00 ;otherwise, nullify value for warp pipe | |
sta $00 | |
NotWPipe: lda $00 ;get value and jump ahead | |
jmp MoveAOId | |
SpecObj: iny ;branch here for rows 12-15 | |
lda (AreaData),y | |
and #%01110000 ;get next byte and mask out all but d6-d4 | |
MoveAOId: lsr ;move d6-d4 to lower nybble | |
lsr | |
lsr | |
lsr | |
NormObj: sta $00 ;store value here (branch for small objects and rows 13 and 14) | |
lda AreaObjectLength,x ;is there something stored here already? | |
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 | |
cmp CurrentPageLoc ;same page as the renderer, and if so, branch | |
beq InitRear | |
ldy AreaDataOffset ;if not, get old offset of level pointer | |
lda (AreaData),y ;and reload first byte | |
and #%00001111 | |
cmp #$0e ;row 14? | |
bne LeavePar | |
lda BackloadingFlag ;if so, check backloading flag | |
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 | |
beq BackColC ;branch to column-wise check | |
lda #$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 | |
lda (AreaData),y | |
and #%11110000 ;mask out low nybble and move high to low | |
lsr | |
lsr | |
lsr | |
lsr | |
cmp 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 | |
sta AreaObjOffsetBuffer,x | |
jsr IncAreaObjOffset ;do sub to increment to next object data | |
RunAObj: lda $00 ;get stored value and add offset to it | |
clc ;then use the jump engine with current contents of A | |
adc $07 | |
jsr JumpEngine | |
;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 | |
;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: | |
ldy AreaObjOffsetBuffer,x ;load offset for level object data saved in buffer | |
iny ;load second byte | |
lda (AreaData),y | |
pha ;save in stack for now | |
and #%01000000 | |
bne Alter2 ;branch if d6 is set | |
pla | |
pha ;pull and push offset to copy to A | |
and #%00001111 ;mask out high nybble and store as | |
sta TerrainControl ;new terrain height type bits | |
pla | |
and #%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 | |
and #%00000111 ;mask out all but 3 LSB | |
cmp #$04 ;if four or greater, set color control bits | |
bcc SetFore ;and nullify foreground scenery bits | |
sta BackgroundColorCtrl | |
lda #$00 | |
SetFore: sta ForegroundScenery ;otherwise set new foreground scenery bits | |
rts | |
;-------------------------------- | |
ScrollLockObject_Warp: | |
ldx #$04 ;load value of 4 for game text routine as default | |
lda WorldNumber ;warp zone (4-3-2), then check world number | |
beq WarpNum | |
inx ;if world number > 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 | |
lda #PiranhaPlant | |
jsr KillEnemies ;load identifier for piranha plants and do sub | |
ScrollLockObject: | |
lda ScrollLock ;invert scroll lock to turn it on | |
eor #%00000001 | |
sta ScrollLock | |
rts | |
;-------------------------------- | |
;$00 - used to store enemy identifier in KillEnemies | |
KillEnemies: | |
sta $00 ;store identifier here | |
lda #$00 | |
ldx #$04 ;check for identifier in enemy object buffer | |
KillELoop: ldy Enemy_ID,x | |
cpy $00 ;if not found, branch | |
bne NoKillE | |
sta 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 $00 ;use area object identifier bit as offset | |
lda FrenzyIDData-8,x ;note that it starts at 8, thus weird address here | |
ldy #$05 | |
FreCompLoop: dey ;check regular slots of enemy object buffer | |
bmi ExitAFrenzy ;if all slots checked and enemy object not found, branch to store | |
cmp Enemy_ID,y ;check for enemy object in buffer versus frenzy object | |
bne FreCompLoop | |
lda #$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 | |
lda AreaObjectLength,x ;check length counter for expiration | |
beq EndTreeL | |
bpl MidTreeL | |
tya | |
sta AreaObjectLength,x ;store lower nybble into buffer flag as length of ledge | |
lda CurrentPageLoc | |
ora CurrentColumnPos ;are we at the start of the level? | |
beq MidTreeL | |
lda #$16 ;render start of tree ledge | |
jmp NoUnder | |
MidTreeL: ldx $07 | |
lda #$17 ;render middle of tree ledge | |
sta MetatileBuffer,x ;note that this is also used if ledge position is | |
lda #$4c ;at the start of level for continuous effect | |
jmp AllUnder ;now render the part underneath | |
EndTreeL: lda #$18 ;render end of tree ledge | |
jmp NoUnder | |
MushroomLedge: | |
jsr ChkLrgObjLength ;get shroom dimensions | |
sty $06 ;store length here for now | |
bcc EndMushL | |
lda AreaObjectLength,x ;divide length by 2 and store elsewhere | |
lsr | |
sta MushroomLedgeHalfLen,x | |
lda #$19 ;render start of mushroom | |
jmp NoUnder | |
EndMushL: lda #$1b ;if at the end, render end of mushroom | |
ldy AreaObjectLength,x | |
beq NoUnder | |
lda MushroomLedgeHalfLen,x ;get divided length and store where length | |
sta $06 ;was stored originally | |
ldx $07 | |
lda #$1a | |
sta MetatileBuffer,x ;render middle of mushroom | |
cpy $06 ;are we smack dab in the center? | |
bne MushLExit ;if not, branch to leave | |
inx | |
lda #$4f | |
sta MetatileBuffer,x ;render stem top of mushroom underneath the middle | |
lda #$50 | |
AllUnder: inx | |
ldy #$0f ;set $0f to render all way down | |
jmp RenderUnderPart ;now render the stem of mushroom | |
NoUnder: ldx $07 ;load row of ledge | |
ldy #$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 | |
ldy #$00 ;initialize metatile offset | |
bcs RenderPul ;if starting, render left pulley | |
iny | |
lda AreaObjectLength,x ;if not at the end, render rope | |
bne RenderPul | |
iny ;otherwise render right pulley | |
RenderPul: lda 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 | |
sty $07 ;if starting row is above $0a, game will crash!!! | |
ldy #$04 | |
jsr ChkLrgObjFixedLength ;load length of castle if not already loaded | |
txa | |
pha ;save obj buffer offset to stack | |
ldy AreaObjectLength,x ;use current length as offset for castle data | |
ldx $07 ;begin at starting row | |
lda #$0b | |
sta $06 ;load upper limit of number of rows to print | |
CRendLoop: lda CastleMetatiles,y ;load current byte using offset | |
sta MetatileBuffer,x | |
inx ;store in buffer and increment buffer offset | |
lda $06 | |
beq ChkCFloor ;have we reached upper limit yet? | |
iny ;if not, increment column-wise | |
iny ;to byte in next row | |
iny | |
iny | |
iny | |
dec $06 ;move closer to upper limit | |
ChkCFloor: cpx #$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 | |
beq ExitCastle ;if we're at page 0, we do not need to do anything else | |
lda AreaObjectLength,x ;check length | |
cmp #$01 ;if length almost about to expire, put brick at floor | |
beq PlayerStop | |
ldy $07 ;check starting row for tall castle ($00) | |
bne NotTall | |
cmp #$03 ;if found, then check to see if we're at the second column | |
beq PlayerStop | |
NotTall: cmp #$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 | |
sta Enemy_X_Position,x ;then write horizontal coordinate for star flag | |
lda CurrentPageLoc | |
sta Enemy_PageLoc,x ;set page location for star flag | |
lda #$01 | |
sta Enemy_Y_HighPos,x ;set vertical high byte | |
sta Enemy_Flag,x ;set flag for buffer | |
lda #$90 | |
sta Enemy_Y_Position,x ;set vertical coordinate | |
lda #StarFlagObject ;set star flag value in buffer itself | |
sta Enemy_ID,x | |
rts | |
PlayerStop: ldy #$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 | |
ldy AreaObjectLength,x ;get length (residual code, water pipe is 1 col thick) | |
ldx $07 ;get row | |
lda #$6b | |
sta MetatileBuffer,x ;draw something here and below it | |
lda #$6c | |
sta 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: | |
ldy #$03 ;check if length set, if not set, set it | |
jsr ChkLrgObjFixedLength | |
ldy #$0a ;set fixed value and render the sideways part | |
jsr RenderSidewaysPipe | |
bcs NoBlankP ;if carry flag set, not time to draw vertical pipe part | |
ldx #$06 ;blank everything above the vertical pipe part | |
VPipeSectLoop: lda #$00 ;all the way to the top of the screen | |
sta MetatileBuffer,x ;because otherwise it will look like exit pipe | |
dex | |
bpl VPipeSectLoop | |
lda 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: | |
ldy #$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 $05 | |
ldy AreaObjectLength,x ;get length left over and store here | |
sty $06 | |
ldx $05 ;get vertical length plus one, use as buffer offset | |
inx | |
lda SidePipeShaftData,y ;check for value $00 based on horizontal offset | |
cmp #$00 | |
beq DrawSidePart ;if found, do not draw the vertical pipe shaft | |
ldx #$00 | |
ldy $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: ldy $06 ;render side pipe part at the bottom | |
lda SidePipeTopPart,y | |
sta MetatileBuffer,x ;note that the pipe parts are stored | |
lda SidePipeBottomPart,y ;backwards horizontally | |
sta MetatileBuffer+1,x | |
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 $00 ;check to see if value was nullified earlier | |
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 | |
ora WorldNumber ;if at world 1-1, do not add piranha plant ever | |
beq DrawPipe | |
ldy AreaObjectLength,x ;if on second column of pipe, branch | |
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 | |
adc #$08 ;add eight to put the piranha plant in the center | |
sta Enemy_X_Position,x ;store as enemy's horizontal coordinate | |
lda CurrentPageLoc ;add carry to current page number | |
adc #$00 | |
sta Enemy_PageLoc,x ;store as enemy's page coordinate | |
lda #$01 | |
sta Enemy_Y_HighPos,x | |
sta Enemy_Flag,x ;activate enemy flag | |
jsr GetAreaObjYPosition ;get piranha plant's vertical coordinate and store here | |
sta Enemy_Y_Position,x | |
lda #PiranhaPlant ;write piranha plant's value into buffer | |
sta Enemy_ID,x | |
jsr InitPiranhaPlant | |
DrawPipe: pla ;get value saved earlier and use as Y | |
tay | |
ldx $07 ;get buffer offset | |
lda VerticalPipeData,y ;draw the appropriate pipe with the Y we loaded earlier | |
sta MetatileBuffer,x ;render the top of the pipe | |
inx | |
lda VerticalPipeData+2,y ;render the rest of the pipe | |
ldy $06 ;subtract one from length and render the part underneath | |
dey | |
jmp RenderUnderPart | |
GetPipeHeight: | |
ldy #$01 ;check for length loaded, if not, load | |
jsr ChkLrgObjFixedLength ;pipe length of 2 (horizontal) | |
jsr GetLrgObjAttrib | |
tya ;get saved lower nybble as height | |
and #$07 ;save only the three lower bits as | |
sta $06 ;vertical length, then load Y with | |
ldy AreaObjectLength,x ;length left over | |
rts | |
FindEmptyEnemySlot: | |
ldx #$00 ;start at first enemy slot | |
EmptyChkLoop: clc ;clear carry flag by default | |
lda Enemy_Flag,x ;check enemy buffer for nonzero | |
beq ExitEmptyChk ;if zero, leave | |
inx | |
cpx #$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 | |
lda #$86 ;render waves | |
sta MetatileBuffer+10 | |
ldx #$0b | |
ldy #$01 ;now render the water underneath | |
lda #$87 | |
jmp RenderUnderPart | |
;-------------------------------- | |
QuestionBlockRow_High: | |
lda #$03 ;start on the fourth row | |
.db $2c ;BIT instruction opcode | |
QuestionBlockRow_Low: | |
lda #$07 ;start on the eighth row | |
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 | |
lda #$c0 | |
sta MetatileBuffer,x | |
rts | |
;-------------------------------- | |
Bridge_High: | |
lda #$06 ;start on the seventh row from top of screen | |
.db $2c ;BIT instruction opcode | |
Bridge_Middle: | |
lda #$07 ;start on the eighth row | |
.db $2c ;BIT instruction opcode | |
Bridge_Low: | |
lda #$09 ;start on the tenth row | |
pha ;save whatever row to the stack for now | |
jsr ChkLrgObjLength ;get low nybble and save as length | |
pla | |
tax ;render bridge railing | |
lda #$0b | |
sta MetatileBuffer,x | |
inx | |
ldy #$00 ;now render the bridge itself | |
lda #$63 | |
jmp RenderUnderPart | |
;-------------------------------- | |
FlagBalls_Residual: | |
jsr GetLrgObjAttrib ;get low nybble from object byte | |
ldx #$02 ;render flag balls on third row from top | |
lda #$6d ;of screen downwards based on low nybble | |
jmp RenderUnderPart | |
;-------------------------------- | |
FlagpoleObject: | |
lda #$24 ;render flagpole ball on top | |
sta MetatileBuffer | |
ldx #$01 ;now render the flagpole shaft | |
ldy #$08 | |
lda #$25 | |
jsr RenderUnderPart | |
lda #$61 ;render solid block at the bottom | |
sta MetatileBuffer+10 | |
jsr GetAreaObjXPosition | |
sec ;get pixel coordinate of where the flagpole is, | |
sbc #$08 ;subtract eight pixels and use as horizontal | |
sta Enemy_X_Position+5 ;coordinate for the flag | |
lda CurrentPageLoc | |
sbc #$00 ;subtract borrow from page location and use as | |
sta Enemy_PageLoc+5 ;page location for the flag | |
lda #$30 | |
sta Enemy_Y_Position+5 ;set vertical coordinate for flag | |
lda #$b0 | |
sta FlagpoleFNum_Y_Pos ;set initial vertical coordinate for flagpole's floatey number | |
lda #FlagpoleFlagObject | |
sta Enemy_ID+5 ;set flag identifier, note that identifier and coordinates | |
inc Enemy_Flag+5 ;use last space in enemy object buffer | |
rts | |
;-------------------------------- | |
EndlessRope: | |
ldx #$00 ;render rope from the top to the bottom of screen | |
ldy #$0f | |
jmp DrawRope | |
BalancePlatRope: | |
txa ;save object buffer offset for now | |
pha | |
ldx #$01 ;blank out all from second row to the bottom | |
ldy #$0f ;with blank used for balance platform rope | |
lda #$44 | |
jsr RenderUnderPart | |
pla ;get back object buffer offset | |
tax | |
jsr GetLrgObjAttrib ;get vertical length from lower nybble | |
ldx #$01 | |
DrawRope: lda #$40 ;render the actual rope | |
jmp RenderUnderPart | |
;-------------------------------- | |
CoinMetatileData: | |
.db $c3, $c2, $c2, $c2 | |
RowOfCoins: | |
ldy AreaType ;get area type | |
lda CoinMetatileData,y ;load appropriate coin metatile | |
jmp GetRow | |
;-------------------------------- | |
C_ObjectRow: | |
.db $06, $07, $08 | |
C_ObjectMetatile: | |
.db $c5, $0c, $89 | |
CastleBridgeObj: | |
ldy #$0c ;load length of 13 columns | |
jsr ChkLrgObjFixedLength | |
jmp ChainObj | |
AxeObj: | |
lda #$08 ;load bowser's palette into sprite portion of palette | |
sta VRAM_Buffer_AddrCtrl | |
ChainObj: | |
ldy $00 ;get value loaded earlier from decoder | |
ldx C_ObjectRow-2,y ;get appropriate row and metatile for object | |
lda C_ObjectMetatile-2,y | |
jmp ColObj | |
EmptyBlock: | |
jsr GetLrgObjAttrib ;get row location | |
ldx $07 | |
lda #$c4 | |
ColObj: ldy #$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 | |
beq DrawBricks | |
ldy #$04 ;if cloud type, override area type | |
DrawBricks: lda BrickMetatiles,y ;get appropriate metatile | |
jmp GetRow ;and go render it | |
RowOfSolidBlocks: | |
ldy AreaType ;load area type obtained from area offset pointer | |
lda SolidBlockMetatiles,y ;get metatile | |
GetRow: pha ;store metatile here | |
jsr ChkLrgObjLength ;get row number, load length | |
DrawRow: ldx $07 | |
ldy #$00 ;set vertical height of 1 | |
pla | |
jmp RenderUnderPart ;render object | |
ColumnOfBricks: | |
ldy AreaType ;load area type obtained from area offset | |
lda BrickMetatiles,y ;get metatile (no cloud override as for row) | |
jmp GetRow2 | |
ColumnOfSolidBlocks: | |
ldy AreaType ;load area type obtained from area offset | |
lda SolidBlockMetatiles,y ;get metatile | |
GetRow2: pha ;save metatile to stack for now | |
jsr GetLrgObjAttrib ;get length and row | |
pla ;restore metatile | |
ldx $07 ;get starting row | |
jmp RenderUnderPart ;now render the column | |
;-------------------------------- | |
BulletBillCannon: | |
jsr GetLrgObjAttrib ;get row and length of bullet bill cannon | |
ldx $07 ;start at first row | |
lda #$64 ;render bullet bill cannon | |
sta MetatileBuffer,x | |
inx | |
dey ;done yet? | |
bmi SetupCannon | |
lda #$65 ;if not, render middle part | |
sta MetatileBuffer,x | |
inx | |
dey ;done yet? | |
bmi SetupCannon | |
lda #$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 | |
sta Cannon_Y_Position,x ;and store it here | |
lda CurrentPageLoc | |
sta Cannon_PageLoc,x ;store page number for cannon here | |
jsr GetAreaObjXPosition ;get proper horizontal coordinate for cannon | |
sta Cannon_X_Position,x ;and store it here | |
inx | |
cpx #$06 ;increment and check offset | |
bcc StrCOffset ;if not yet reached sixth cannon, branch to save offset | |
ldx #$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 | |
lda #$09 ;start past the end for the bottom | |
sta StaircaseControl ;of the staircase | |
NextStair: dec StaircaseControl ;move onto next step (or first if starting) | |
ldy StaircaseControl | |
ldx StaircaseRowData,y ;get starting row and height to render | |
lda StaircaseHeightData,y | |
tay | |
lda #$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 | |
sta Enemy_X_Position,x ;and store | |
lda CurrentPageLoc ;store page location of jumpspring | |
sta Enemy_PageLoc,x | |
jsr GetAreaObjYPosition ;get vertical coordinate for jumpspring | |
sta Enemy_Y_Position,x ;and store | |
sta Jumpspring_FixedYPos,x ;store as permanent coordinate here | |
lda #JumpspringObject | |
sta Enemy_ID,x ;write jumpspring object to enemy object buffer | |
ldy #$01 | |
sty Enemy_Y_HighPos,x ;store vertical high byte | |
inc Enemy_Flag,x ;set flag for enemy object buffer | |
ldx $07 | |
lda #$67 ;draw metatiles in two rows where jumpspring is | |
sta MetatileBuffer,x | |
lda #$68 | |
sta MetatileBuffer+1,x | |
rts | |
;-------------------------------- | |
;$07 - used to save ID of brick object | |
Hidden1UpBlock: | |
lda Hidden1UpFlag ;if flag not set, do not render object | |
beq ExitDecBlock | |
lda #$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: | |
lda #$00 ;initialize multi-coin timer flag | |
sta BrickCoinTimerFlag | |
BrickWithItem: | |
jsr GetAreaObjectID ;save area object ID | |
sty $07 | |
lda #$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 | |
lda #$05 ;otherwise use adder for bricks without lines | |
BWithL: clc ;add object ID to adder | |
adc $07 | |
tay ;use as offset for metatile | |
DrawQBlk: lda 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 $00 ;get value saved from area parser routine | |
sec | |
sbc #$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 | |
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 | |
sec | |
sbc #$10 ;subtract 16 pixels | |
sta Whirlpool_LeftExtent,x ;store as left extent of whirlpool | |
lda CurrentPageLoc ;get page location of where we're at | |
sbc #$00 ;subtract borrow | |
sta 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 | |
sta Whirlpool_Length,x ;save size of whirlpool here | |
inx | |
cpx #$05 ;increment and check offset | |
bcc StrWOffset ;if not yet reached fifth whirlpool, branch to save offset | |
ldx #$00 ;otherwise initialize it | |
StrWOffset: stx Whirlpool_Offset ;save new offset here | |
NoWhirlP: ldx AreaType ;get appropriate metatile, then | |
lda HoleMetatiles,x ;render the hole proper | |
ldx #$08 | |
ldy #$0f ;start at ninth row and go to bottom, run RenderUnderPart | |
;-------------------------------- | |
RenderUnderPart: | |
sty AreaObjectHeight ;store vertical length to render | |
ldy MetatileBuffer,x ;check current spot to see if there's something | |
beq DrawThisRow ;we need to keep, if nothing, go ahead | |
cpy #$17 | |
beq WaitOneRow ;if middle part (tree ledge), wait until next row | |
cpy #$1a | |
beq WaitOneRow ;if middle part (mushroom ledge), wait until next row | |
cpy #$c0 | |
beq DrawThisRow ;if question block w/ coin, overwrite | |
cpy #$c0 | |
bcs WaitOneRow ;if any other metatile with palette 3, wait until next row | |
cpy #$54 | |
bne DrawThisRow ;if cracked rock terrain, overwrite | |
cmp #$50 | |
beq WaitOneRow ;if stem top of mushroom, wait until next row | |
DrawThisRow: sta MetatileBuffer,x ;render contents of A from routine that called this | |
WaitOneRow: inx | |
cpx #$0d ;stop rendering if we're at the bottom of the screen | |
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: | |
lda AreaObjectLength,x ;check for set length counter | |
clc ;clear carry flag for not just starting | |
bpl LenSet ;if counter not set, load it, otherwise leave alone | |
tya ;save length into length counter | |
sta AreaObjectLength,x | |
sec ;set carry flag if just starting | |
LenSet: rts | |
GetLrgObjAttrib: | |
ldy AreaObjOffsetBuffer,x ;get offset saved from area obj decoding routine | |
lda (AreaData),y ;get first byte of level object | |
and #%00001111 | |
sta $07 ;save row location | |
iny | |
lda (AreaData),y ;get next byte, save lower nybble (length or height) | |
and #%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: | |
lda $07 ;multiply value by 16 | |
asl | |
asl ;this will give us the proper vertical pixel coordinate | |
asl | |
asl | |
clc | |
adc #32 ;add 32 pixels for the status bar | |
rts | |
;------------------------------------------------------------------------------------- | |
;$06-$07 - used to store block buffer address used as indirect | |
BlockBufferAddr: | |
.db <Block_Buffer_1, <Block_Buffer_2 | |
.db >Block_Buffer_1, >Block_Buffer_2 | |
GetBlockBufferAddr: | |
pha ;take value of A, save | |
lsr ;move high nybble to low | |
lsr | |
lsr | |
lsr | |
tay ;use nybble as pointer to high byte | |
lda BlockBufferAddr+2,y ;of indirect here | |
sta $07 | |
pla | |
and #%00001111 ;pull from stack, mask out high nybble | |
clc | |
adc BlockBufferAddr,y ;add to low byte | |
sta $06 ;store here and leave | |
rts | |
;------------------------------------------------------------------------------------- | |
;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: and #%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 | |
lda WorldAddrOffsets,y | |
clc ;add area number used to find data | |
adc AreaNumber | |
tay | |
lda 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 | |
and #%00011111 | |
sta AreaAddrsLOffset ;save as low offset | |
lda EnemyAddrHOffsets,y ;load base value with 2 altered MSB, | |
clc ;then add base value to 5 LSB, result | |
adc AreaAddrsLOffset ;becomes offset for level data | |
tay | |
lda EnemyDataAddrLow,y ;use offset to load pointer | |
sta EnemyDataLow | |
lda EnemyDataAddrHigh,y | |
sta EnemyDataHigh | |
ldy AreaType ;use area type as offset | |
lda AreaDataHOffsets,y ;do the same thing but with different base value | |
clc | |
adc AreaAddrsLOffset | |
tay | |
lda AreaDataAddrLow,y ;use this offset to load another pointer | |
sta AreaDataLow | |
lda AreaDataAddrHigh,y | |
sta AreaDataHigh | |
ldy #$00 ;load first byte of header | |
lda (AreaData),y | |
pha ;save it to the stack for now | |
and #%00000111 ;save 3 LSB for foreground scenery or bg color control | |
cmp #$04 | |
bcc StoreFore | |
sta BackgroundColorCtrl ;if 4 or greater, save value here as bg color control | |
lda #$00 | |
StoreFore: sta ForegroundScenery ;if less, save value here as foreground scenery | |
pla ;pull byte from stack and push it back | |
pha | |
and #%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 | |
and #%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 | |
lda (AreaData),y ;load second byte of header | |
pha ;save to stack | |
and #%00001111 ;mask out all but lower nybble | |
sta TerrainControl | |
pla ;pull and push byte to copy it to A | |
pha | |
and #%00110000 ;save 2 MSB for background scenery type | |
lsr | |
lsr ;shift bits to LSBs | |
lsr | |
lsr | |
sta BackgroundScenery ;save as background scenery | |
pla | |
and #%11000000 | |
clc | |
rol ;rotate bits over to LSBs | |
rol | |
rol | |
cmp #%00000011 ;if set to 3, store here | |
bne StoreStyle ;and nullify other value | |
sta CloudTypeOverride ;otherwise store value in other place | |
lda #$00 | |
StoreStyle: sta AreaStyle | |
lda AreaDataLow ;increment area data address by 2 bytes | |
clc | |
adc #$02 | |
sta AreaDataLow | |
lda AreaDataHigh | |
adc #$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 <E_CastleArea1, <E_CastleArea2, <E_CastleArea3, <E_CastleArea4, <E_CastleArea5, <E_CastleArea6 | |
.db <E_GroundArea1, <E_GroundArea2, <E_GroundArea3, <E_GroundArea4, <E_GroundArea5, <E_GroundArea6 | |
.db <E_GroundArea7, <E_GroundArea8, <E_GroundArea9, <E_GroundArea10, <E_GroundArea11, <E_GroundArea12 | |
.db <E_GroundArea13, <E_GroundArea14, <E_GroundArea15, <E_GroundArea16, <E_GroundArea17, <E_GroundArea18 | |
.db <E_GroundArea19, <E_GroundArea20, <E_GroundArea21, <E_GroundArea22, <E_UndergroundArea1 | |
.db <E_UndergroundArea2, <E_UndergroundArea3, <E_WaterArea1, <E_WaterArea2, <E_WaterArea3 | |
EnemyDataAddrHigh: | |
.db >E_CastleArea1, >E_CastleArea2, >E_CastleArea3, >E_CastleArea4, >E_CastleArea5, >E_CastleArea6 | |
.db >E_GroundArea1, >E_GroundArea2, >E_GroundArea3, >E_GroundArea4, >E_GroundArea5, >E_GroundArea6 | |
.db >E_GroundArea7, >E_GroundArea8, >E_GroundArea9, >E_GroundArea10, >E_GroundArea11, >E_GroundArea12 | |
.db >E_GroundArea13, >E_GroundArea14, >E_GroundArea15, >E_GroundArea16, >E_GroundArea17, >E_GroundArea18 | |
.db >E_GroundArea19, >E_GroundArea20, >E_GroundArea21, >E_GroundArea22, >E_UndergroundArea1 | |
.db >E_UndergroundArea2, >E_UndergroundArea3, >E_WaterArea1, >E_WaterArea2, >E_WaterArea3 | |
AreaDataHOffsets: | |
.db $00, $03, $19, $1c | |
AreaDataAddrLow: | |
.db <L_WaterArea1, <L_WaterArea2, <L_WaterArea3, <L_GroundArea1, <L_GroundArea2, <L_GroundArea3 | |
.db <L_GroundArea4, <L_GroundArea5, <L_GroundArea6, <L_GroundArea7, <L_GroundArea8, <L_GroundArea9 | |
.db <L_GroundArea10, <L_GroundArea11, <L_GroundArea12, <L_GroundArea13, <L_GroundArea14, <L_GroundArea15 | |
.db <L_GroundArea16, <L_GroundArea17, <L_GroundArea18, <L_GroundArea19, <L_GroundArea20, <L_GroundArea21 | |
.db <L_GroundArea22, <L_UndergroundArea1, <L_UndergroundArea2, <L_UndergroundArea3, <L_CastleArea1 | |
.db <L_CastleArea2, <L_CastleArea3, <L_CastleArea4, <L_CastleArea5, <L_CastleArea6 | |
AreaDataAddrHigh: | |
.db >L_WaterArea1, >L_WaterArea2, >L_WaterArea3, >L_GroundArea1, >L_GroundArea2, >L_GroundArea3 | |
.db >L_GroundArea4, >L_GroundArea5, >L_GroundArea6, >L_GroundArea7, >L_GroundArea8, >L_GroundArea9 | |
.db >L_GroundArea10, >L_GroundArea11, >L_GroundArea12, >L_GroundArea13, >L_GroundArea14, >L_GroundArea15 | |
.db >L_GroundArea16, >L_GroundArea17, >L_GroundArea18, >L_GroundArea19, >L_GroundArea20, >L_GroundArea21 | |
.db >L_GroundArea22, >L_UndergroundArea1, >L_UndergroundArea2, >L_UndergroundArea3, >L_CastleArea1 | |
.db >L_CastleArea2, >L_CastleArea3, >L_CastleArea4, >L_CastleArea5, >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 | |
;------------------------------------------------------------------------------------- | |
;unused space | |
.db $ff | |
;------------------------------------------------------------------------------------- | |
;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 | |
lda SavedJoypadBits,x ;use appropriate player's controller bits | |
sta SavedJoypadBits ;as the master controller bits | |
jsr GameRoutines ;execute one of many possible subs | |
lda OperMode_Task ;check major task of operating mode | |
cmp #$03 ;if we are supposed to be here, | |
bcs GameEngine ;branch to the game engine itself | |
rts | |
GameEngine: | |
jsr ProcFireball_Bubble ;process fireballs and air bubbles | |
ldx #$00 | |
ProcELoop: stx ObjectOffset ;put incremented offset in X as enemy object offset | |
jsr EnemiesAndLoopsCore ;process enemy objects | |
jsr FloateyNumbersRoutine ;process floatey numbers | |
inx | |
cpx #$06 ;do these two subroutines until the whole buffer is done | |
bne ProcELoop | |
jsr GetPlayerOffscreenBits ;get offscreen bits for player object | |
jsr RelativePlayerPosition ;get relative coordinates for player object | |
jsr PlayerGfxHandler ;draw the player | |
jsr BlockObjMT_Updater ;replace block objects with metatiles if necessary | |
ldx #$01 | |
stx ObjectOffset ;set offset for second | |
jsr BlockObjectsCore ;process second block object | |
dex | |
stx ObjectOffset ;set offset for first | |
jsr BlockObjectsCore ;process first block object | |
jsr MiscObjectsCore ;process misc objects (hammer, jumping coins) | |
jsr ProcessCannons ;process bullet bill cannons | |
jsr ProcessWhirlpools ;process whirlpools | |
jsr FlagpoleRoutine ;process the flagpole | |
jsr RunGameTimer ;count down the game timer | |
jsr ColorRotation ;cycle one of the background colors | |
lda Player_Y_HighPos | |
cmp #$02 ;if player is below the screen, don't bother with the music | |
bpl NoChgMus | |
lda StarInvincibleTimer ;if star mario invincibility timer at zero, | |
beq ClrPlrPal ;skip this part | |
cmp #$04 | |
bne NoChgMus ;if not yet at a certain point, continue | |
lda IntervalTimerControl ;if interval timer not yet expired, | |
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 | |
cpy #$08 ;if timer still above certain point, | |
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 | |
lda #$00 | |
sta Left_Right_Buttons ;nullify left and right buttons temp variable | |
UpdScrollVar: lda VRAM_Buffer_AddrCtrl | |
cmp #$06 ;if vram address controller set to 6 (one of two $0341s) | |
beq ExitEng ;then branch to leave | |
lda AreaParserTaskNum ;otherwise check number of tasks | |
bne RunParser | |
lda ScrollThirtyTwo ;get horizontal scroll in 0-31 or $00-$20 range | |
cmp #$20 ;check to see if exceeded $21 | |
bmi ExitEng ;branch to leave if not | |
lda ScrollThirtyTwo | |
sbc #$20 ;otherwise subtract $20 to set appropriately | |
sta ScrollThirtyTwo ;and store | |
lda #$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 | |
adc 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 | |
bne InitScrlAmt ;skip a bunch of code here if set | |
lda Player_Pos_ForScroll | |
cmp #$50 ;check player's horizontal screen position | |
bcc InitScrlAmt ;if less than 80 pixels to the right, branch | |
lda SideCollisionTimer ;if timer related to player's side collision | |
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 | |
cpy #$02 ;if value $01, branch and do not decrement | |
bcc ChkNearMid | |
dey ;otherwise decrement by one | |
ChkNearMid: lda Player_Pos_ForScroll | |
cmp #$70 ;check player's horizontal screen position | |
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 | |
adc ScrollThirtyTwo ;add to value already set here | |
sta ScrollThirtyTwo ;save as new value here | |
tya | |
clc | |
adc 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 | |
adc #$00 ;add carry to page location for left | |
sta ScreenLeft_PageLoc ;side of the screen | |
and #$01 ;get LSB of page location | |
sta $00 ;save as temp variable for PPU register 1 mirror | |
lda Mirror_PPU_CTRL_REG1 ;get PPU register 1 mirror | |
and #%11111110 ;save all bits except d0 | |
ora $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 | |
lda #$08 | |
sta ScrollIntervalTimer ;set scroll timer (residual, not used elsewhere) | |
jmp ChkPOffscr ;skip this part | |
InitScrlAmt: lda #$00 | |
sta ScrollAmount ;initialize value here | |
ChkPOffscr: ldx #$00 ;set X for player offset | |
jsr GetXOffscreenBits ;get horizontal offscreen bits for player | |
sta $00 ;save them here | |
ldy #$00 ;load default offset (left side) | |
asl ;if d7 of offscreen bits are set, | |
bcs KeepOnscr ;branch with default offset | |
iny ;otherwise use different offset (right side) | |
lda $00 | |
and #%00100000 ;check offscreen bits for d5 set | |
beq InitPlatScrl ;if not set, branch ahead of this part | |
KeepOnscr: lda ScreenEdge_X_Pos,y ;get left or right side coordinate based on offset | |
sec | |
sbc X_SubtracterData,y ;subtract amount based on offset | |
sta Player_X_Position ;store as player position to prevent movement further | |
lda ScreenEdge_PageLoc,y ;get left or right page location based on offset | |
sbc #$00 ;subtract borrow | |
sta Player_PageLoc ;save as player's page location | |
lda Left_Right_Buttons ;check saved controller bits | |
cmp OffscrJoypadBitsData,y ;against bits based on offset | |
beq InitPlatScrl ;if not equal, branch | |
lda #$00 | |
sta Player_X_Speed ;otherwise nullify horizontal speed of player | |
InitPlatScrl: lda #$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 | |
adc #$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 | |
adc #$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 | |
cmp #$02 | |
beq EntrMode2 ;if found, branch to enter from pipe or with vine | |
lda #$00 | |
ldy Player_Y_Position ;if vertical position above a certain | |
cpy #$30 ;point, nullify controller bits and continue | |
bcc AutoControlPlayer ;with player movement code, do not return | |
lda PlayerEntranceCtrl ;check player entry bits from header | |
cmp #$06 | |
beq ChkBehPipe ;if set to 6 or 7, execute pipe intro code | |
cmp #$07 ;otherwise branch to normal entry | |
bne PlayerRdy | |
ChkBehPipe: lda Player_SprAttrib ;check for sprite attributes | |
bne IntroEntr ;branch if found | |
lda #$01 | |
jmp AutoControlPlayer ;force player to walk to the right | |
IntroEntr: jsr EnterSidePipe ;execute sub to move player to the right | |
dec ChangeAreaTimer ;decrement timer for change of area | |
bne ExitEntr ;branch to exit if not yet expired | |
inc 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, | |
bne VineEntr ;branch to enter with vine | |
lda #$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 | |
cmp #$91 ;if player risen to a certain point (this requires pipes | |
bcc PlayerRdy ;to be at specific height to look/function right) branch | |
rts ;to the last part, otherwise leave | |
VineEntr: lda VineHeight | |
cmp #$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 | |
cmp #$99 ;check player's vertical coordinate against preset value | |
ldy #$00 ;load default values to be written to | |
lda #$01 ;this value moves player to the right off the vine | |
bcc OffVine ;if vertical coordinate < preset value, use defaults | |
lda #$03 | |
sta Player_State ;otherwise set player state to climbing | |
iny ;increment value in Y | |
lda #$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 | |
cmp #$48 ;check player's horizontal position | |
bcc ExitEntr ;if not far enough to the right, branch to leave | |
PlayerRdy: lda #$08 ;set routine to be executed by game engine next frame | |
sta GameEngineSubroutine | |
lda #$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 | |
cmp #$0b ;if certain value is set, branch to skip controller bit loading | |
beq SizeChk | |
lda AreaType ;are we in a water type area? | |
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 | |
cmp #$d0 ;if nearing the bottom of the screen or | |
bcc SaveJoyp ;not in the vertical area between status bar or bottom, | |
DisJoyp: lda #$00 ;disable controller bits | |
sta SavedJoypadBits | |
SaveJoyp: lda SavedJoypadBits ;otherwise store A and B buttons in $0a | |
and #%11000000 | |
sta A_B_Buttons | |
lda SavedJoypadBits ;store left and right buttons in $0c | |
and #%00000011 | |
sta Left_Right_Buttons | |
lda SavedJoypadBits ;store up and down buttons in $0b | |
and #%00001100 | |
sta Up_Down_Buttons | |
and #%00000100 ;check for pressing down | |
beq SizeChk ;if not, branch | |
lda Player_State ;check player's state | |
bne SizeChk ;if not on the ground, branch | |
ldy Left_Right_Buttons ;check left and right | |
beq SizeChk ;if neither pressed, branch | |
lda #$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 | |
ldy #$01 ;is player small? | |
lda PlayerSize | |
bne ChkMoveDir | |
ldy #$00 ;check for if crouching | |
lda CrouchingFlag | |
beq ChkMoveDir ;if not, branch ahead | |
ldy #$02 ;if big and crouching, load y with 2 | |
ChkMoveDir: sty Player_BoundBoxCtrl ;set contents of Y as player's bounding box size control | |
lda #$01 ;set moving direction to right by default | |
ldy Player_X_Speed ;check player's horizontal speed | |
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 | |
ldx #$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 | |
cmp #$40 ;check to see if player is higher than 64th pixel | |
bcc PlayerHole ;if so, branch ahead | |
lda GameEngineSubroutine | |
cmp #$05 ;if running end-of-level routine, branch ahead | |
beq PlayerHole | |
cmp #$07 ;if running player entrance routine, branch ahead | |
beq PlayerHole | |
cmp #$04 ;if running routines $00-$03, branch ahead | |
bcc PlayerHole | |
lda Player_SprAttrib | |
and #%11011111 ;otherwise nullify player's | |
sta Player_SprAttrib ;background priority flag | |
PlayerHole: lda Player_Y_HighPos ;check player's vertical high byte | |
cmp #$02 ;for below the screen | |
bmi ExitCtrl ;branch to leave if not that far down | |
ldx #$01 | |
stx ScrollLock ;set scroll lock | |
ldy #$04 | |
sty $07 ;set value here | |
ldx #$00 ;use X as flag, and clear for cloud level | |
ldy GameTimerExpiredFlag ;check game timer expiration flag | |
bne HoleDie ;if set, branch | |
ldy CloudTypeOverride ;check for cloud type override | |
bne ChkHoleX ;skip to last part if found | |
HoleDie: inx ;set flag in X for player death | |
ldy GameEngineSubroutine | |
cpy #$0b ;check for some other routine running | |
beq ChkHoleX ;if so, branch ahead | |
ldy DeathMusicLoaded ;check value here | |
bne HoleBottom ;if already set, branch to next part | |
iny | |
sty EventMusicQueue ;otherwise play death music | |
sty DeathMusicLoaded ;and set value here | |
HoleBottom: ldy #$06 | |
sty $07 ;change value here | |
ChkHoleX: cmp $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 | |
bne ExitCtrl ;branch to leave if so | |
lda #$06 ;otherwise set to run lose life routine | |
sta GameEngineSubroutine ;on next frame | |
ExitCtrl: rts ;leave | |
CloudExit: | |
lda #$00 | |
sta JoypadOverride ;clear controller override bits if any are set | |
jsr SetEntr ;do sub to set secondary mode | |
inc AltEntranceControl ;set mode of entry to 3 | |
rts | |
;------------------------------------------------------------------------------------- | |
Vine_AutoClimb: | |
lda Player_Y_HighPos ;check to see whether player reached position | |
bne AutoClimb ;above the status bar yet and if so, set modes | |
lda Player_Y_Position | |
cmp #$e4 | |
bcc SetEntr | |
AutoClimb: lda #%00001000 ;set controller bits override to up | |
sta JoypadOverride | |
ldy #$03 ;set player state to climbing | |
sty Player_State | |
jmp AutoControlPlayer | |
SetEntr: lda #$02 ;set starting position to override | |
sta AltEntranceControl | |
jmp ChgAreaMode ;set modes | |
;------------------------------------------------------------------------------------- | |
VerticalPipeEntry: | |
lda #$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 | |
ldy #$00 ;load default mode of entry | |
lda WarpZoneControl ;check warp zone control variable/flag | |
bne ChgAreaPipe ;if set, branch to use mode 0 | |
iny | |
lda AreaType ;check for castle level type | |
cmp #$03 | |
bne ChgAreaPipe ;if not castle type level, use mode 1 | |
iny | |
jmp ChgAreaPipe ;otherwise use mode 2 | |
MovePlayerYAxis: | |
clc | |
adc 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 | |
ldy #$02 | |
ChgAreaPipe: dec ChangeAreaTimer ;decrement timer for change of area | |
bne ExitCAPipe | |
sty AltEntranceControl ;when timer expires set mode of alternate entry | |
ChgAreaMode: inc DisableScreenFlag ;set flag to disable screen output | |
lda #$00 | |
sta OperMode_Task ;set secondary mode of operation | |
sta Sprite0HitDetectFlag ;disable sprite 0 check | |
ExitCAPipe: rts ;leave | |
EnterSidePipe: | |
lda #$08 ;set player's horizontal speed | |
sta Player_X_Speed | |
ldy #$01 ;set controller right button by default | |
lda Player_X_Position ;mask out higher nybble of player's | |
and #%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 | |
cmp #$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: cmp #$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 | |
cmp #$f0 ;for specific moment in time | |
bcs ExitBlink ;branch if before that point | |
cmp #$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 | |
bne ExitBoth ;then branch to leave | |
sty PlayerAnimCtrl ;otherwise initialize player's animation frame control | |
inc PlayerChangeSizeFlag ;set growing/shrinking flag | |
lda PlayerSize | |
eor #$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 | |
cmp #$f0 ;for specific moment in time | |
bcs ExitDeath ;branch to leave if before that point | |
jmp PlayerCtrlRoutine ;otherwise run player control routine | |
DonePlayerTask: | |
lda #$00 | |
sta TimerControl ;initialize master timer control to continue timers | |
lda #$08 | |
sta GameEngineSubroutine ;set player control routine to run next frame | |
rts ;leave | |
PlayerFireFlower: | |
lda TimerControl ;check master timer control | |
cmp #$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: | |
and #$03 ;mask out all but d1-d0 (previously d3-d2) | |
sta $00 ;store result here to use as palette bits | |
lda Player_SprAttrib ;get player attributes | |
and #%11111100 ;save any other bits but palette bits | |
ora $00 ;add palette bits | |
sta Player_SprAttrib ;store as new player attributes | |
rts ;and leave | |
ResetPalFireFlower: | |
jsr DonePlayerTask ;do sub to init timer control and run player control routine | |
ResetPalStar: | |
lda Player_SprAttrib ;get player attributes | |
and #%11111100 ;mask out palette bits to force palette 0 | |
sta Player_SprAttrib ;store as new player attributes | |
rts ;and leave | |
ExitDeath: | |
rts ;leave from death routine | |
;------------------------------------------------------------------------------------- | |
FlagpoleSlide: | |
lda Enemy_ID+5 ;check special use enemy slot | |
cmp #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 | |
lda #$00 | |
sta FlagpoleSoundQueue ;init flagpole sound queue | |
ldy Player_Y_Position | |
cpy #$9e ;check to see if player has slid down | |
bcs SlidePlayer ;far enough, and if so, branch with no controller bits set | |
lda #$04 ;otherwise force player to climb down (to slide) | |
SlidePlayer: jmp AutoControlPlayer ;jump to player control routine | |
NoFPObj: inc GameEngineSubroutine ;increment to next routine (this may | |
rts ;be residual code) | |
;------------------------------------------------------------------------------------- | |
Hidden1UpCoinAmts: | |
.db $15, $23, $16, $1b, $17, $18, $23, $63 | |
PlayerEndLevel: | |
lda #$01 ;force player to walk to the right | |
jsr AutoControlPlayer | |
lda Player_Y_Position ;check player's vertical position | |
cmp #$ae | |
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 | |
beq ChkStop ;because we only need to do this part once | |
lda #EndOfLevelMusic | |
sta EventMusicQueue ;load win level music in event music queue | |
lda #$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, | |
bne InCastle ;go ahead with the rest of the code | |
inc StarFlagTaskControl ;otherwise set task control now (this gets ball rolling!) | |
InCastle: lda #%00100000 ;set player's background priority bit to | |
sta Player_SprAttrib ;give illusion of being inside the castle | |
RdyNextA: lda StarFlagTaskControl | |
cmp #$05 ;if star flag task control not yet set | |
bne ExitNA ;beyond last valid task number, branch to leave | |
inc LevelNumber ;increment level number used for game logic | |
lda LevelNumber | |
cmp #$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 | |
cmp Hidden1UpCoinAmts,y ;against minimum value, if player has not collected | |
bcc NextArea ;at least this number of coins, leave flag clear | |
inc Hidden1UpFlag ;otherwise set hidden 1-up box control flag | |
NextArea: inc AreaNumber ;increment area number used for address loader | |
jsr LoadAreaPointer ;get new level pointer | |
inc 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) | |
lda #Silence | |
sta EventMusicQueue ;silence music and leave | |
ExitNA: rts | |
;------------------------------------------------------------------------------------- | |
PlayerMovementSubs: | |
lda #$00 ;set A to init crouch flag by default | |
ldy PlayerSize ;is player small? | |
bne SetCrouch ;if so, branch | |
lda Player_State ;check state of player | |
bne ProcMove ;if not on the ground, branch | |
lda Up_Down_Buttons ;load controller bits for up and down | |
and #%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, | |
bne NoMoveSub ;branch to leave | |
lda Player_State | |
cmp #$03 ;get player state | |
beq MoveSubs ;if climbing, branch ahead, leave timer unset | |
ldy #$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 | |
beq GndMove ;if left/right controller bits not set, skip instruction | |
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 | |
bpl DumpFall ;or moving downwards, branch to falling | |
lda A_B_Buttons | |
and #A_Button ;check to see if A button is being pressed | |
and 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 | |
sec | |
sbc Player_Y_Position ;subtract current from original vertical coordinate | |
cmp DiffToHaltJump ;compare to value set here to see if player is in mid-jump | |
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, | |
beq LRAir ;branch ahead to last part | |
jsr GetPlayerAnimSpeed ;do a sub to get animation frame timing | |
lda Player_Y_Position | |
cmp #$14 ;check vertical position against preset value | |
bcs LRWater ;if not yet reached a certain position, branch ahead | |
lda #$18 | |
sta VerticalForce ;otherwise set fractional | |
LRWater: lda Left_Right_Buttons ;check left/right controller bits (check for swimming) | |
beq LRAir ;if not pressing any, skip | |
sta PlayerFacingDir ;otherwise set facing direction accordingly | |
LRAir: lda Left_Right_Buttons ;check left/right controller bits (check for jumping/falling) | |
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 | |
cmp #$0b ;check for specific routine selected | |
bne ExitMov1 ;branch if not set to run | |
lda #$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 | |
adc Player_Y_MoveForce ;save with carry | |
sta Player_YMF_Dummy | |
ldy #$00 ;set default adder here | |
lda Player_Y_Speed ;get player's vertical speed | |
bpl MoveOnVine ;if not moving upwards, branch | |
dey ;otherwise set adder to $ff | |
MoveOnVine: sty $00 ;store adder here | |
adc 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 | |
adc $00 ;add carry to player's page location | |
sta Player_Y_HighPos ;and store | |
lda Left_Right_Buttons ;compare left/right controller bits | |
and Player_CollisionBits ;to collision flag | |
beq InitCSTimer ;if not set, skip to end | |
ldy ClimbSideTimer ;otherwise check timer | |
bne ExitCSub ;if timer not expired, branch to leave | |
ldy #$18 | |
sty ClimbSideTimer ;otherwise set timer now | |
ldx #$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 | |
adc 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 | |
adc ClimbAdderHigh,x ;from the player's page location | |
sta Player_PageLoc | |
lda Left_Right_Buttons ;get left/right controller bits again | |
eor #%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 | |
cmp #$03 | |
bne CheckForJumping ;if not climbing, branch | |
ldy #$00 | |
lda Up_Down_Buttons ;get controller bits for up/down | |
and Player_CollisionBits ;check against player's collision detection bits | |
beq ProcClimb ;if not pressing up or down, branch | |
iny | |
and #%00001000 ;check for pressing up | |
bne ProcClimb | |
iny | |
ProcClimb: ldx Climb_Y_MForceData,y ;load value here | |
stx Player_Y_MoveForce ;store as vertical movement force | |
lda #$08 ;load default animation timing | |
ldx Climb_Y_SpeedData,y ;load some other value here | |
stx Player_Y_Speed ;store as vertical speed | |
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, | |
bne NoJump ;skip ahead to something else | |
lda A_B_Buttons ;check for A button press | |
and #A_Button | |
beq NoJump ;if not, branch to something else | |
and 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 | |
beq InitJS ;if on the ground, branch | |
lda SwimmingFlag ;if swimming flag not set, jump to do something else | |
beq NoJump ;to prevent midair jumping, otherwise continue | |
lda JumpSwimTimer ;if jump/swim timer nonzero, branch | |
bne InitJS | |
lda Player_Y_Speed ;check player's vertical speed | |
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: lda #$20 ;set jump/swim timer | |
sta JumpSwimTimer | |
ldy #$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 | |
lda #$01 ;set player state to jumping/swimming | |
sta Player_State | |
lda Player_XSpeedAbsolute ;check value related to walking/running speed | |
cmp #$09 | |
bcc ChkWtr ;branch if below certain values, increment Y | |
iny ;for each amount equal or exceeded | |
cmp #$10 | |
bcc ChkWtr | |
iny | |
cmp #$19 | |
bcc ChkWtr | |
iny | |
cmp #$1c | |
bcc ChkWtr ;note that for jumping, range is 0-4 for Y | |
iny | |
ChkWtr: lda #$01 ;set value here (apparently always set to 1) | |
sta DiffToHaltJump | |
lda SwimmingFlag ;if swimming flag disabled, branch | |
beq GetYPhy | |
ldy #$05 ;otherwise set Y to 5, range is 5-6 | |
lda Whirlpool_Flag ;if whirlpool flag not set, branch | |
beq GetYPhy | |
iny ;otherwise increment to 6 | |
GetYPhy: lda JumpMForceData,y ;store appropriate jump/swim | |
sta VerticalForce ;data here | |
lda FallMForceData,y | |
sta VerticalForceDown | |
lda InitMForceData,y | |
sta Player_Y_MoveForce | |
lda PlayerYSpdData,y | |
sta Player_Y_Speed | |
lda SwimmingFlag ;if swimming flag disabled, branch | |
beq PJumpSnd | |
lda #Sfx_EnemyStomp ;load swim/goomba stomp sound into | |
sta Square1SoundQueue ;square 1's sfx queue | |
lda Player_Y_Position | |
cmp #$14 ;check vertical low byte of player position | |
bcs X_Physics ;if below a certain point, branch | |
lda #$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: lda #Sfx_BigJump ;load big mario's jump sound by default | |
ldy PlayerSize ;is mario big? | |
beq SJumpSnd | |
lda #Sfx_SmallJump ;if not, load small mario's jump sound | |
SJumpSnd: sta Square1SoundQueue ;store appropriate jump sound in square 1 sfx queue | |
X_Physics: ldy #$00 | |
sty $00 ;init value here | |
lda Player_State ;if mario is on the ground, branch | |
beq ProcPRun | |
lda Player_XSpeedAbsolute ;check something that seems to be related | |
cmp #$19 ;to mario's speed | |
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 | |
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 | |
cmp 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 | |
and #B_Button | |
bne SetRTmr ;if pressed, skip ahead to set timer | |
lda RunningTimer ;check for running timer set | |
bne GetXPhy ;if set, branch | |
ChkRFast: iny ;if running timer not set or level type is water, | |
inc $00 ;increment Y again and temp variable in memory | |
lda RunningSpeed | |
bne FastXSp ;if running speed set here, branch | |
lda Player_XSpeedAbsolute | |
cmp #$21 ;otherwise check player's walking/running speed | |
bcc GetXPhy ;if less than a certain amount, branch ahead | |
FastXSp: inc $00 ;if running speed set or speed => $21 increment $00 | |
jmp GetXPhy ;and jump ahead | |
SetRTmr: lda #$0a ;if b button pressed, set running timer | |
sta RunningTimer | |
GetXPhy: lda MaxLeftXSpdData,y ;get maximum speed to the left | |
sta MaximumLeftSpeed | |
lda GameEngineSubroutine ;check for specific routine running | |
cmp #$07 ;(player entrance) | |
bne GetXPhy2 ;if not running, skip and use old value of Y | |
ldy #$03 ;otherwise set Y to 3 | |
GetXPhy2: lda MaxRightXSpdData,y ;get maximum speed to the right | |
sta MaximumRightSpeed | |
ldy $00 ;get other value in memory | |
lda FrictionData,y ;get value using value in memory as offset | |
sta FrictionAdderLow | |
lda #$00 | |
sta FrictionAdderHigh ;init something here | |
lda PlayerFacingDir | |
cmp Player_MovingDir ;check facing direction against moving direction | |
beq ExitPhy ;if the same, branch to leave | |
asl FrictionAdderLow ;otherwise shift d7 of friction adder low into carry | |
rol FrictionAdderHigh ;then rotate carry onto d0 of friction adder high | |
ExitPhy: rts ;and then leave | |
;------------------------------------------------------------------------------------- | |
PlayerAnimTmrData: | |
.db $02, $04, $07 | |
GetPlayerAnimSpeed: | |
ldy #$00 ;initialize offset in Y | |
lda Player_XSpeedAbsolute ;check player's walking/running speed | |
cmp #$1c ;against preset amount | |
bcs SetRunSpd ;if greater than a certain amount, branch ahead | |
iny ;otherwise increment Y | |
cmp #$0e ;compare against lower amount | |
bcs ChkSkid ;if greater than this but not greater than first, skip increment | |
iny ;otherwise increment Y again | |
ChkSkid: lda SavedJoypadBits ;get controller bits | |
and #%01111111 ;mask out A button | |
beq SetAnimSpd ;if no other buttons pressed, branch ahead of all this | |
and #$03 ;mask out all others except left and right | |
cmp Player_MovingDir ;check against moving direction | |
bne ProcSkid ;if left/right controller bits <> moving direction, branch | |
lda #$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 | |
cmp #$0b ;against one last amount | |
bcs SetAnimSpd ;if greater than this amount, branch | |
lda PlayerFacingDir | |
sta Player_MovingDir ;otherwise use facing direction to set moving direction | |
lda #$00 | |
sta Player_X_Speed ;nullify player's horizontal speed | |
sta Player_X_MoveForce ;and dummy variable for player | |
SetAnimSpd: lda PlayerAnimTmrData,y ;get animation timer setting using Y as offset | |
sta PlayerAnimTimerSet | |
rts | |
;------------------------------------------------------------------------------------- | |
ImposeFriction: | |
and Player_CollisionBits ;perform AND between left/right controller bits and collision flag | |
cmp #$00 ;then compare to zero (this instruction is redundant) | |
bne JoypFrict ;if any bits set, branch to next part | |
lda Player_X_Speed | |
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 | |
clc | |
adc FrictionAdderLow ;add to it another value set here | |
sta Player_X_MoveForce ;store here | |
lda Player_X_Speed | |
adc FrictionAdderHigh ;add value plus carry to horizontal speed | |
sta Player_X_Speed ;set as new horizontal speed | |
cmp 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 | |
sec | |
sbc FrictionAdderLow ;subtract from it another value set here | |
sta Player_X_MoveForce ;store here | |
lda Player_X_Speed | |
sbc FrictionAdderHigh ;subtract value plus borrow from horizontal speed | |
sta Player_X_Speed ;set as new horizontal speed | |
cmp 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: cmp #$00 ;if player not moving or moving to the right, | |
bpl SetAbsSpd ;branch and leave horizontal speed value unmodified | |
eor #$ff | |
clc ;otherwise get two's compliment to get absolute | |
adc #$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 | |
cmp #$02 | |
bcc ProcAirBubbles ;if not fiery, branch | |
lda A_B_Buttons | |
and #B_Button ;check for b button pressed | |
beq ProcFireballs ;branch if not pressed | |
and PreviousA_B_Buttons | |
bne ProcFireballs ;if button pressed in previous frame, branch | |
lda FireballCounter ;load fireball counter | |
and #%00000001 ;get LSB and use as offset for buffer | |
tax | |
lda Fireball_State,x ;load fireball state | |
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 | |
bne ProcFireballs | |
lda Player_State ;if player's state = climbing, branch | |
cmp #$03 | |
beq ProcFireballs | |
lda #Sfx_Fireball ;play fireball sound effect | |
sta Square1SoundQueue | |
lda #$02 ;load state | |
sta 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 | |
inc FireballCounter ;increment fireball counter | |
ProcFireballs: | |
ldx #$00 | |
jsr FireballObjCore ;process first fireball object | |
ldx #$01 | |
jsr FireballObjCore ;process second fireball object, then do air bubbles | |
ProcAirBubbles: | |
lda AreaType ;if not water type level, skip the rest of this | |
bne BublExit | |
ldx #$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 | |
lda Fireball_State,x ;check for d7 = 1 | |
asl | |
bcs FireballExplosion ;if so, branch to get relative coordinates and draw explosion | |
ldy Fireball_State,x ;if fireball inactive, branch to leave | |
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 | |
adc #$04 ;add four pixels and store as fireball's horizontal position | |
sta Fireball_X_Position,x | |
lda Player_PageLoc ;get player's page location | |
adc #$00 ;add carry and store as fireball's page location | |
sta Fireball_PageLoc,x | |
lda Player_Y_Position ;get player's vertical position and store | |
sta Fireball_Y_Position,x | |
lda #$01 ;set high byte of vertical position | |
sta Fireball_Y_HighPos,x | |
ldy PlayerFacingDir ;get player's facing direction | |
dey ;decrement to use as offset here | |
lda FireballXSpdData,y ;set horizontal speed of fireball accordingly | |
sta Fireball_X_Speed,x | |
lda #$04 ;set vertical speed of fireball | |
sta Fireball_Y_Speed,x | |
lda #$07 | |
sta Fireball_BoundBoxCtrl,x ;set bounding box size control for fireball | |
dec 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 | |
adc #$07 | |
tax | |
lda #$50 ;set downward movement force here | |
sta $00 | |
lda #$03 ;set maximum speed here | |
sta $02 | |
lda #$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 | |
and #%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: lda #$00 ;erase fireball state | |
sta Fireball_State,x | |
NoFBall: rts ;leave | |
FireballExplosion: | |
jsr RelativeFireballPosition | |
jmp DrawExplosion_Fireball | |
BubbleCheck: | |
lda PseudoRandomBitReg+1,x ;get part of LSFR | |
and #$01 | |
sta $07 ;store pseudorandom bit here | |
lda Bubble_Y_Position,x ;get vertical coordinate for air bubble | |
cmp #$f8 ;if offscreen coordinate not set, | |
bne MoveBubl ;branch to move air bubble | |
lda AirBubbleTimer ;if air bubble timer not expired, | |
bne ExitBubl ;branch to leave, otherwise create new air bubble | |
SetupBubble: | |
ldy #$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 | |
ldy #$08 ;otherwise load alternate value here | |
PosBubl: tya ;use value loaded as adder | |
adc Player_X_Position ;add to player's horizontal position | |
sta Bubble_X_Position,x ;save as horizontal position for airbubble | |
lda Player_PageLoc | |
adc #$00 ;add carry to player's page location | |
sta Bubble_PageLoc,x ;save as page location for airbubble | |
lda Player_Y_Position | |
clc ;add eight pixels to player's vertical position | |
adc #$08 | |
sta Bubble_Y_Position,x ;save as vertical position for air bubble | |
lda #$01 | |
sta Bubble_Y_HighPos,x ;set vertical high byte for air bubble | |
ldy $07 ;get pseudorandom bit, use as offset | |
lda BubbleTimerData,y ;get data for air bubble timer | |
sta AirBubbleTimer ;set air bubble timer | |
MoveBubl: ldy $07 ;get pseudorandom bit again, use as offset | |
lda Bubble_YMF_Dummy,x | |
sec ;subtract pseudorandom amount from dummy variable | |
sbc Bubble_MForceData,y | |
sta Bubble_YMF_Dummy,x ;save dummy variable | |
lda Bubble_Y_Position,x | |
sbc #$00 ;subtract borrow from airbubble's vertical coordinate | |
cmp #$20 ;if below the status bar, | |
bcs Y_Bubl ;branch to go ahead and use to move air bubble upwards | |
lda #$f8 ;otherwise set offscreen coordinate | |
Y_Bubl: sta 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 | |
beq ExGTimer ;branch to leave if in title screen mode | |
lda GameEngineSubroutine | |
cmp #$08 ;if routine number less than eight running, | |
bcc ExGTimer ;branch to leave | |
cmp #$0b ;if running death routine, | |
beq ExGTimer ;branch to leave | |
lda Player_Y_HighPos | |
cmp #$02 ;if player below the screen, | |
bcs ExGTimer ;branch to leave regardless of level type | |
lda GameTimerCtrlTimer ;if game timer control not yet expired, | |
bne ExGTimer ;branch to leave | |
lda GameTimerDisplay | |
ora GameTimerDisplay+1 ;otherwise check game timer digits | |
ora 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 | |
ora GameTimerDisplay+2 | |
bne ResGTCtrl ;if timer not at 100, branch to reset game timer control | |
lda #TimeRunningOutMusic | |
sta EventMusicQueue ;otherwise load time running out music | |
ResGTCtrl: lda #$18 ;reset game timer control | |
sta GameTimerCtrlTimer | |
ldy #$23 ;set offset for last digit | |
lda #$ff ;set value to decrement game timer digit | |
sta DigitModifier+5 | |
jsr DigitsMathRoutine ;do sub to decrement game timer slowly | |
lda #$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) | |
inc GameTimerExpiredFlag ;set game timer expiration flag | |
ExGTimer: rts ;leave | |
;------------------------------------------------------------------------------------- | |
WarpZoneObject: | |
lda ScrollLock ;check for scroll lock flag | |
beq ExGTimer ;branch if not set to leave | |
lda Player_Y_Position ;check to see if player's vertical coordinate has | |
and 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 | |
inc 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 | |
bne ExitWh ;branch to leave if not found | |
sta Whirlpool_Flag ;otherwise initialize whirlpool flag | |
lda TimerControl ;if master timer control set, | |
bne ExitWh ;branch to leave | |
ldy #$04 ;otherwise start with last whirlpool data | |
WhLoop: lda Whirlpool_LeftExtent,y ;get left extent of whirlpool | |
clc | |
adc Whirlpool_Length,y ;add length of whirlpool | |
sta $02 ;store result as right extent here | |
lda Whirlpool_PageLoc,y ;get page location | |
beq NextWh ;if none or page 0, branch to get next data | |
adc #$00 ;add carry | |
sta $01 ;store result as page location of right extent here | |
lda Player_X_Position ;get player's horizontal position | |
sec | |
sbc Whirlpool_LeftExtent,y ;subtract left extent | |
lda Player_PageLoc ;get player's page location | |
sbc Whirlpool_PageLoc,y ;subtract borrow | |
bmi NextWh ;if player too far left, branch to get next data | |
lda $02 ;otherwise get right extent | |
sec | |
sbc Player_X_Position ;subtract player's horizontal coordinate | |
lda $01 ;get right extent's page location | |
sbc 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: | |
lda Whirlpool_Length,y ;get length of whirlpool | |
lsr ;divide by 2 | |
sta $00 ;save here | |
lda Whirlpool_LeftExtent,y ;get left extent of whirlpool | |
clc | |
adc $00 ;add length divided by 2 | |
sta $01 ;save as center of whirlpool | |
lda Whirlpool_PageLoc,y ;get page location | |
adc #$00 ;add carry | |
sta $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 $01 ;get center | |
sec | |
sbc Player_X_Position ;subtract player's horizontal coordinate | |
lda $00 ;get page location of center | |
sbc 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 | |
sec | |
sbc #$01 ;subtract one pixel | |
sta Player_X_Position ;set player's new horizontal coordinate | |
lda Player_PageLoc | |
sbc #$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 | |
adc #$01 ;add one pixel | |
sta Player_X_Position ;set player's new horizontal coordinate | |
lda Player_PageLoc | |
adc #$00 ;add carry | |
SetPWh: sta Player_PageLoc ;set player's new page location | |
WhPull: lda #$10 | |
sta $00 ;set vertical movement force | |
lda #$01 | |
sta Whirlpool_Flag ;set whirlpool flag to be used later | |
sta $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: | |
ldx #$05 ;set enemy object offset | |
stx ObjectOffset ;to special use slot | |
lda Enemy_ID,x | |
cmp #FlagpoleFlagObject ;if flagpole flag not found, | |
bne ExitFlagP ;branch to leave | |
lda GameEngineSubroutine | |
cmp #$04 ;if flagpole slide routine not running, | |
bne SkipScore ;branch to near the end of code | |
lda Player_State | |
cmp #$03 ;if player state not climbing, | |
bne SkipScore ;branch to near the end of code | |
lda Enemy_Y_Position,x ;check flagpole flag's vertical coordinate | |
cmp #$aa ;if flagpole flag down to a certain point, | |
bcs GiveFPScr ;branch to end the level | |
lda Player_Y_Position ;check player's vertical coordinate | |
cmp #$a2 ;if player down to a certain point, | |
bcs GiveFPScr ;branch to end the level | |
lda Enemy_YMF_Dummy,x | |
adc #$ff ;add movement amount to dummy variable | |
sta Enemy_YMF_Dummy,x ;save dummy variable | |
lda Enemy_Y_Position,x ;get flag's vertical coordinate | |
adc #$01 ;add 1 plus carry to move flag, and | |
sta Enemy_Y_Position,x ;store vertical coordinate | |
lda FlagpoleFNum_YMFDummy | |
sec ;subtract movement amount from dummy variable | |
sbc #$ff | |
sta FlagpoleFNum_YMFDummy ;save dummy variable | |
lda FlagpoleFNum_Y_Pos | |
sbc #$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) | |
lda FlagpoleScoreMods,y ;get amount to award player points | |
ldx FlagpoleScoreDigits,y ;get digit with which to award points | |
sta DigitModifier,x ;store in digit modifier | |
jsr AddToScore ;do sub to award player points depending on height of collision | |
lda #$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 | |
bne DrawJSpr ;branch to last section if set | |
lda JumpspringAnimCtrl ;check jumpspring frame control | |
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 | |
and #%00000010 ;mask out all but d1, original value still in Y | |
bne DownJSpr ;if set, branch to move player up | |
inc Player_Y_Position | |
inc Player_Y_Position ;move player's vertical position down two pixels | |
jmp PosJSpr ;skip to next part | |
DownJSpr: dec Player_Y_Position ;move player's vertical position up two pixels | |
dec Player_Y_Position | |
PosJSpr: lda Jumpspring_FixedYPos,x ;get permanent vertical position | |
clc | |
adc Jumpspring_Y_PosData,y ;add value using frame control as offset | |
sta Enemy_Y_Position,x ;store as new vertical position | |
cpy #$01 ;check frame control offset (second frame is $00) | |
bcc BounceJS ;if offset not yet at third frame ($01), skip to next part | |
lda A_B_Buttons | |
and #A_Button ;check saved controller bits for A button press | |
beq BounceJS ;skip to next part if A not pressed | |
and PreviousA_B_Buttons ;check for A button pressed in previous frame | |
bne BounceJS ;skip to next part if so | |
lda #$f4 | |
sta JumpspringForce ;otherwise write new jumpspring force here | |
BounceJS: cpy #$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 | |
lda #$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 | |
beq ExJSpring ;trying to animate it, just leave | |
lda JumpspringTimer | |
bne ExJSpring ;if jumpspring timer not expired yet, leave | |
lda #$04 | |
sta JumpspringTimer ;otherwise initialize jumpspring timer | |
inc JumpspringAnimCtrl ;increment frame control to animate jumpspring | |
ExJSpring: rts ;leave | |
;------------------------------------------------------------------------------------- | |
Setup_Vine: | |
lda #VineObject ;load identifier for vine object | |
sta Enemy_ID,x ;store in buffer | |
lda #$01 | |
sta Enemy_Flag,x ;set flag for enemy object buffer | |
lda Block_PageLoc,y | |
sta Enemy_PageLoc,x ;copy page location from previous object | |
lda Block_X_Position,y | |
sta Enemy_X_Position,x ;copy horizontal coordinate from previous object | |
lda Block_Y_Position,y | |
sta Enemy_Y_Position,x ;copy vertical coordinate from previous object | |
ldy VineFlagOffset ;load vine flag/offset to next available vine slot | |
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 | |
sta VineObjOffset,y ;using vine flag as offset | |
inc VineFlagOffset ;increment vine flag offset | |
lda #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: | |
cpx #$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 | |
cmp 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 | |
sbc #$01 ;subtract vertical position of vine | |
sta Enemy_Y_Position+5 ;one pixel every frame it's time | |
inc VineHeight ;increment vine height | |
RunVSubs: lda VineHeight ;if vine still very small, | |
cmp #$08 ;branch to leave | |
bcc ExitVH | |
jsr RelativeEnemyPosition ;get relative coordinates of vine, | |
jsr GetEnemyOffscreenBits ;and any offscreen bits | |
ldy #$00 ;initialize offset used in draw vine sub | |
VDrawLoop: jsr DrawVine ;draw vine | |
iny ;increment offset | |
cpy VineFlagOffset ;if offset in Y and offset here | |
bne VDrawLoop ;do not yet match, loop back to draw more vine | |
lda Enemy_OffscreenBits | |
and #%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: ldx 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 | |
cmp #$20 ;if vine small (less than 32 pixels tall) | |
bcc ExitVH ;then branch ahead to leave | |
ldx #$06 ;set offset in X to last enemy slot | |
lda #$01 ;set A to obtain horizontal in $04, but we don't care | |
ldy #$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 $02 | |
cpy #$d0 ;if vertical high nybble offset beyond extent of | |
bcs ExitVH ;current block buffer, branch to leave, do not write | |
lda ($06),y ;otherwise check contents of block buffer at | |
bne ExitVH ;current offset, if not empty, branch to leave | |
lda #$26 | |
sta ($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 | |
beq ExCannon ;if water type area, branch to leave | |
ldx #$02 | |
ThreeSChk: stx ObjectOffset ;start at third enemy slot | |
lda Enemy_Flag,x ;check enemy buffer flag | |
bne Chk_BB ;if set, branch to check enemy | |
lda PseudoRandomBitReg+1,x ;otherwise get part of LSFR | |
ldy SecondaryHardMode ;get secondary hard mode flag, use as offset | |
and CannonBitmasks,y ;mask out bits of LSFR as decided by flag | |
cmp #$06 ;check to see if lower nybble is above certain value | |
bcs Chk_BB ;if so, branch to check enemy | |
tay ;transfer masked contents of LSFR to Y as pseudorandom offset | |
lda Cannon_PageLoc,y ;get page location | |
beq Chk_BB ;if not set or on page 0, branch to check enemy | |
lda Cannon_Timer,y ;get cannon timer | |
beq FireCannon ;if expired, branch to fire cannon | |
sbc #$00 ;otherwise subtract borrow (note carry will always be clear here) | |
sta Cannon_Timer,y ;to count timer down | |
jmp Chk_BB ;then jump ahead to check enemy | |
FireCannon: | |
lda TimerControl ;if master timer control set, | |
bne Chk_BB ;branch to check enemy | |
lda #$0e ;otherwise we start creating one | |
sta Cannon_Timer,y ;first, reset cannon timer | |
lda Cannon_PageLoc,y ;get page location of cannon | |
sta Enemy_PageLoc,x ;save as page location of bullet bill | |
lda Cannon_X_Position,y ;get horizontal coordinate of cannon | |
sta Enemy_X_Position,x ;save as horizontal coordinate of bullet bill | |
lda Cannon_Y_Position,y ;get vertical coordinate of cannon | |
sec | |
sbc #$08 ;subtract eight pixels (because enemies are 24 pixels tall) | |
sta Enemy_Y_Position,x ;save as vertical coordinate of bullet bill | |
lda #$01 | |
sta Enemy_Y_HighPos,x ;set vertical high byte of bullet bill | |
sta Enemy_Flag,x ;set buffer flag | |
lsr ;shift right once to init A | |
sta Enemy_State,x ;then initialize enemy's state | |
lda #$09 | |
sta Enemy_BoundBoxCtrl,x ;set bounding box size control for bullet bill | |
lda #BulletBill_CannonVar | |
sta Enemy_ID,x ;load identifier for bullet bill (cannon variant) | |
jmp Next3Slt ;move onto next slot | |
Chk_BB: lda Enemy_ID,x ;check enemy identifier for bullet bill (cannon variant) | |
cmp #BulletBill_CannonVar | |
bne Next3Slt ;if not found, branch to get next slot | |
jsr OffscreenBoundsCheck ;otherwise, check to see if it went offscreen | |
lda Enemy_Flag,x ;check enemy buffer flag | |
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, | |
bne RunBBSubs ;branch to run subroutines except movement sub | |
lda Enemy_State,x | |
bne ChkDSte ;if bullet bill's state set, branch to check defeated state | |
lda Enemy_OffscreenBits ;otherwise load offscreen bits | |
and #%00001100 ;mask out bits | |
cmp #%00001100 ;check to see if all bits are set | |
beq KillBB ;if so, branch to kill this object | |
ldy #$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: sty Enemy_MovingDir,x ;set bullet bill's moving direction | |
dey ;decrement to use as offset | |
lda BulletBillXSpdData,y ;get horizontal speed based on moving direction | |
sta Enemy_X_Speed,x ;and store it | |
lda $00 ;get horizontal difference | |
adc #$28 ;add 40 pixels | |
cmp #$50 ;if less than a certain amount, player is too close | |
bcc KillBB ;to cannon either on left or right side, thus branch | |
lda #$01 | |
sta Enemy_State,x ;otherwise set bullet bill's state | |
lda #$0a | |
sta EnemyFrameTimer,x ;set enemy frame timer | |
lda #Sfx_Blast | |
sta Square2SoundQueue ;play fireworks/gunfire sound | |
ChkDSte: lda Enemy_State,x ;check enemy state for d5 set | |
and #%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 | |
and #%00000111 ;second part of LSFR | |
bne SetMOfs ;if any bits are set, branch and use as offset | |
lda PseudoRandomBitReg+1 | |
and #%00001000 ;get d3 from same part of LSFR | |
SetMOfs: tay ;use either d3 or d2-d0 for offset here | |
lda Misc_State,y ;if any values loaded in | |
bne NoHammer ;$2a-$32 where offset is then leave with carry clear | |
ldx HammerEnemyOfsData,y ;get offset of enemy slot to check using Y as offset | |
lda Enemy_Flag,x ;check enemy buffer flag at offset | |
bne NoHammer ;if buffer flag set, branch to leave with carry clear | |
ldx ObjectOffset ;get original enemy object offset | |
txa | |
sta HammerEnemyOffset,y ;save here | |
lda #$90 | |
sta Misc_State,y ;save hammer's state here | |
lda #$07 | |
sta 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 | |
bne RunHSubs ;skip all of this code and go to last subs at the end | |
lda Misc_State,x ;otherwise get hammer's state | |
and #%01111111 ;mask out d7 | |
ldy HammerEnemyOffset,x ;get enemy object offset that spawned this hammer | |
cmp #$02 ;check hammer's state | |
beq SetHSpd ;if currently at 2, branch | |
bcs SetHPos ;if greater than 2, branch elsewhere | |
txa | |
clc ;add 13 bytes to use | |
adc #$0d ;proper misc object | |
tax ;return offset to X | |
lda #$10 | |
sta $00 ;set downward movement force | |
lda #$0f | |
sta $01 ;set upward movement force (not used) | |
lda #$04 | |
sta $02 ;set maximum vertical speed | |
lda #$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: lda #$fe | |
sta Misc_Y_Speed,x ;set hammer's vertical speed | |
lda Enemy_State,y ;get enemy object state | |
and #%11110111 ;mask out d3 | |
sta Enemy_State,y ;store new state | |
ldx Enemy_MovingDir,y ;get enemy's moving direction | |
dex ;decrement to use as offset | |
lda HammerXSpdData,x ;get proper speed to use based on moving direction | |
ldx ObjectOffset ;reobtain hammer's buffer offset | |
sta Misc_X_Speed,x ;set hammer's horizontal speed | |
SetHPos: dec Misc_State,x ;decrement hammer's state | |
lda Enemy_X_Position,y ;get enemy's horizontal position | |
clc | |
adc #$02 ;set position 2 pixels to the right | |
sta Misc_X_Position,x ;store as hammer's horizontal position | |
lda Enemy_PageLoc,y ;get enemy's page location | |
adc #$00 ;add carry | |
sta Misc_PageLoc,x ;store as hammer's page location | |
lda Enemy_Y_Position,y ;get enemy's vertical position | |
sec | |
sbc #$0a ;move position 10 pixels upward | |
sta Misc_Y_Position,x ;store as hammer's vertical position | |
lda #$01 | |
sta Misc_Y_HighPos,x ;set hammer's vertical high byte | |
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 | |
lda Block_PageLoc,x ;get page location of block object | |
sta Misc_PageLoc,y ;store as page location of misc object | |
lda Block_X_Position,x ;get horizontal coordinate of block object | |
ora #$05 ;add 5 pixels | |
sta Misc_X_Position,y ;store as horizontal coordinate of misc object | |
lda Block_Y_Position,x ;get vertical coordinate of block object | |
sbc #$10 ;subtract 16 pixels | |
sta 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 | |
lda Block_PageLoc2,x ;get page location saved earlier | |
sta Misc_PageLoc,y ;and save as page location for misc object | |
lda $06 ;get low byte of block buffer offset | |
asl | |
asl ;multiply by 16 to use lower nybble | |
asl | |
asl | |
ora #$05 ;add five pixels | |
sta Misc_X_Position,y ;save as horizontal coordinate for misc object | |
lda $02 ;get vertical high nybble offset from earlier | |
adc #$20 ;add 32 pixels for the status bar | |
sta Misc_Y_Position,y ;store as vertical coordinate | |
JCoinC: lda #$fb | |
sta Misc_Y_Speed,y ;set vertical speed | |
lda #$01 | |
sta Misc_Y_HighPos,y ;set vertical high byte | |
sta 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 | |
inc CoinTallyFor1Ups ;increment coin tally used to activate 1-up block flag | |
rts | |
FindEmptyMiscSlot: | |
ldy #$08 ;start at end of misc objects buffer | |
FMiscLoop: lda Misc_State,y ;get misc object state | |
beq UseMiscS ;branch if none found to use current offset | |
dey ;decrement offset | |
cpy #$05 ;do this for three slots | |
bne FMiscLoop ;do this until all slots are checked | |
ldy #$08 ;if no empty slots found, use last slot | |
UseMiscS: sty JumpCoinMiscOffset ;store offset of misc object buffer here (residual) | |
rts | |
;------------------------------------------------------------------------------------- | |
MiscObjectsCore: | |
ldx #$08 ;set at end of misc object buffer | |
MiscLoop: stx ObjectOffset ;store misc object offset here | |
lda Misc_State,x ;check misc object state | |
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: | |
ldy 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 | |
inc Misc_State,x ;otherwise increment state to either start off or as timer | |
lda Misc_X_Position,x ;get horizontal coordinate for misc object | |
clc ;whether its jumping coin (state 0 only) or floatey number | |
adc ScrollAmount ;add current scroll speed | |
sta Misc_X_Position,x ;store as new horizontal coordinate | |
lda Misc_PageLoc,x ;get page location | |
adc #$00 ;add carry | |
sta Misc_PageLoc,x ;store as new page location | |
lda Misc_State,x | |
cmp #$30 ;check state of object for preset value | |
bne RunJCSubs ;if not yet reached, branch to subroutines | |
lda #$00 | |
sta 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 | |
adc #$0d | |
tax | |
lda #$50 ;set downward movement amount | |
sta $00 | |
lda #$06 ;set maximum vertical speed | |
sta $02 | |
lsr ;divide by 2 and set | |
sta $01 ;as upward movement amount (apparently residual) | |
lda #$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 | |
lda Misc_Y_Speed,x ;check vertical speed | |
cmp #$05 | |
bne RunJCSubs ;if not moving downward fast enough, keep state as-is | |
inc 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: | |
lda #$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 | |
ldy CoinTallyOffsets,x ;get offset for player's coin tally | |
jsr DigitsMathRoutine ;update the coin tally | |
inc CoinTally ;increment onscreen player's coin amount | |
lda CoinTally | |
cmp #100 ;does player have 100 coins yet? | |
bne CoinPoints ;if not, skip all of this | |
lda #$00 | |
sta CoinTally ;otherwise, reinitialize coin amount | |
inc NumberofLives ;give the player an extra life | |
lda #Sfx_ExtraLife | |
sta Square2SoundQueue ;play 1-up sound | |
CoinPoints: | |
lda #$02 ;set digit modifier to award | |
sta DigitModifier+4 ;200 points to the player | |
AddToScore: | |
ldx CurrentPlayer ;get current player | |
ldy 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 | |
lda 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 | |
lda VRAM_Buffer1-6,y ;check highest digit of score | |
bne NoZSup ;if zero, overwrite with space tile for zero suppression | |
lda #$24 | |
sta VRAM_Buffer1-6,y | |
NoZSup: ldx ObjectOffset ;get enemy object buffer offset | |
rts | |
;------------------------------------------------------------------------------------- | |
SetupPowerUp: | |
lda #PowerUpObject ;load power-up identifier into | |
sta Enemy_ID+5 ;special use slot of enemy object buffer | |
lda Block_PageLoc,x ;store page location of block object | |
sta Enemy_PageLoc+5 ;as page location of power-up object | |
lda Block_X_Position,x ;store horizontal coordinate of block object | |
sta Enemy_X_Position+5 ;as horizontal coordinate of power-up object | |
lda #$01 | |
sta Enemy_Y_HighPos+5 ;set vertical high byte of power-up object | |
lda Block_Y_Position,x ;get vertical coordinate of block object | |
sec | |
sbc #$08 ;subtract 8 pixels | |
sta Enemy_Y_Position+5 ;and use as vertical coordinate of power-up object | |
PwrUpJmp: lda #$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 | |
lda #$03 | |
sta Enemy_BoundBoxCtrl+5 ;set bounding box size control for power-up object | |
lda PowerUpType | |
cmp #$02 ;check currently loaded power-up type | |
bcs PutBehind ;if star or 1-up, branch ahead | |
lda PlayerStatus ;otherwise check player's current status | |
cmp #$02 | |
bcc StrType ;if player not fiery, use status as power-up type | |
lsr ;otherwise shift right to force fire flower type | |
StrType: sta PowerUpType ;store type here | |
PutBehind: lda #%00100000 | |
sta Enemy_SprAttrib+5 ;set background priority bit | |
lda #Sfx_GrowPowerUp | |
sta Square2SoundQueue ;load power-up reveal sound and leave | |
rts | |
;------------------------------------------------------------------------------------- | |
PowerUpObjHandler: | |
ldx #$05 ;set object offset for last slot in enemy object buffer | |
stx ObjectOffset | |
lda Enemy_State+5 ;check power-up object's state | |
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, | |
bne RunPUSubs ;branch ahead to enemy object routines | |
lda PowerUpType ;check power-up type | |
beq ShroomM ;if normal mushroom, branch ahead to move it | |
cmp #$03 | |
beq ShroomM ;if 1-up mushroom, branch ahead to move it | |
cmp #$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 | |
and #$03 ;mask out all but 2 LSB | |
bne ChkPUSte ;if any bits set here, branch | |
dec Enemy_Y_Position+5 ;otherwise decrement vertical coordinate slowly | |
lda Enemy_State+5 ;load power-up object state | |
inc Enemy_State+5 ;increment state for next frame (to make power-up rise) | |
cmp #$11 ;if power-up object state not yet past 16th pixel, | |
bcc ChkPUSte ;branch ahead to last part here | |
lda #$10 | |
sta Enemy_X_Speed,x ;otherwise set horizontal speed | |
lda #%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 | |
sta Enemy_MovingDir,x ;set moving direction | |
ChkPUSte: lda Enemy_State+5 ;check power-up object's state | |
cmp #$06 ;for if power-up has risen enough | |
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 | |
lda #$11 ;load unbreakable block object state by default | |
ldx SprDataOffset_Ctrl ;load offset control bit here | |
ldy PlayerSize ;check player's size | |
bne DBlockSte ;if small, branch | |
lda #$12 ;otherwise load breakable block object state | |
DBlockSte: sta 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 $02 ;get vertical high nybble offset used in block buffer routine | |
sta Block_Orig_YPos,x ;set as vertical coordinate for block object | |
tay | |
lda $06 ;get low byte of block buffer address used in same routine | |
sta Block_BBuf_Low,x ;save as offset here to be used later | |
lda ($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 $00 ;store metatile here | |
ldy PlayerSize ;check player's size | |
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 | |
ldy #$11 ;otherwise load unbreakable state into block object buffer | |
sty Block_State,x ;note this applies to both player sizes | |
lda #$c4 ;load empty block metatile into A for now | |
ldy $00 ;get metatile from before | |
cpy #$58 ;is it brick with coins (with line)? | |
beq StartBTmr ;if so, branch | |
cpy #$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 | |
bne ContBTmr ;if set, timer expired or counting down, thus branch | |
lda #$0b | |
sta BrickCoinTimer ;if not set, set brick coin timer | |
inc BrickCoinTimerFlag ;and set flag linked to it | |
ContBTmr: lda BrickCoinTimer ;check brick coin timer | |
bne PutOldMT ;if not yet expired, branch to use current metatile | |
ldy #$c4 ;otherwise use empty block metatile | |
PutOldMT: tya ;put metatile into A | |
PutMTileB: sta Block_Metatile,x ;store whatever metatile be appropriate here | |
jsr InitBlock_XY_Pos ;get block object horizontal coordinates saved | |
ldy $02 ;get vertical high nybble offset | |
lda #$23 | |
sta ($06),y ;write blank metatile $23 to block buffer | |
lda #$10 | |
sta BlockBounceTimer ;set block bounce timer | |
pla ;pull original metatile from stack | |
sta $05 ;and save here | |
ldy #$00 ;set default offset | |
lda CrouchingFlag ;is player crouching? | |
bne SmallBP ;if so, branch to increment offset | |
lda PlayerSize ;is player big? | |
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 | |
adc BlockYPosAdderData,y ;add value determined by size | |
and #$f0 ;mask out low nybble to get 16-pixel correspondence | |
sta Block_Y_Position,x ;save as vertical coordinate for block object | |
ldy Block_State,x ;get block object state | |
cpy #$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 | |
eor #$01 ;and floatey numbers | |
sta SprDataOffset_Ctrl | |
rts ;leave! | |
;-------------------------------- | |
InitBlock_XY_Pos: | |
lda Player_X_Position ;get player's horizontal coordinate | |
clc | |
adc #$08 ;add eight pixels | |
and #$f0 ;mask out low nybble to give 16-pixel correspondence | |
sta Block_X_Position,x ;save as horizontal coordinate for block object | |
lda Player_PageLoc | |
adc #$00 ;add carry to page location of player | |
sta Block_PageLoc,x ;save as page location of block object | |
sta Block_PageLoc2,x ;save elsewhere to be used later | |
lda Player_Y_HighPos | |
sta 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 | |
lda #Sfx_Bump | |
sta Square1SoundQueue ;play bump sound | |
lda #$00 | |
sta Block_X_Speed,x ;initialize horizontal speed for block object | |
sta Block_Y_MoveForce,x ;init fractional movement force | |
sta Player_Y_Speed ;init player's vertical speed | |
lda #$fe | |
sta Block_Y_Speed,x ;set vertical speed for block object | |
lda $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 | |
cmp #$09 ;if block number was within 0-8 range, | |
bcc BlockCode ;branch to use current number | |
sbc #$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: | |
lda #$00 ;load mushroom/fire flower into power-up type | |
.db $2c ;BIT instruction opcode | |
StarBlock: | |
lda #$02 ;load star into power-up type | |
.db $2c ;BIT instruction opcode | |
ExtraLifeMushBlock: | |
lda #$03 ;load 1-up mushroom into power-up type | |
sta $39 ;store correct power-up type | |
jmp SetupPowerUp | |
VineBlock: | |
ldx #$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: | |
ldy #$0d ;start at end of metatile data | |
BumpChkLoop: cmp 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 | |
MatchBump: rts ;note carry is set if found match | |
;-------------------------------- | |
BrickShatter: | |
jsr CheckTopOfBlock ;check to see if there's a coin directly above this block | |
lda #Sfx_BrickShatter | |
sta Block_RepFlag,x ;set flag for block object to immediately replace metatile | |
sta NoiseSoundQueue ;load brick shatter sound | |
jsr SpawnBrickChunks ;create brick chunk objects | |
lda #$fe | |
sta Player_Y_Speed ;set vertical speed for player | |
lda #$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 $02 ;get vertical high nybble offset used in block buffer | |
beq TopEx ;branch to leave if set to zero, because we're at the top | |
tya ;otherwise set to A | |
sec | |
sbc #$10 ;subtract $10 to move up one row in the block buffer | |
sta $02 ;store as new vertical high nybble offset | |
tay | |
lda ($06),y ;get contents of block buffer in same column, one row up | |
cmp #$c2 ;is it a coin? (not underwater) | |
bne TopEx ;if not, branch to leave | |
lda #$00 | |
sta ($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: | |
lda Block_X_Position,x ;set horizontal coordinate of block object | |
sta Block_Orig_XPos,x ;as original horizontal coordinate here | |
lda #$f0 | |
sta Block_X_Speed,x ;set horizontal speed for brick chunk objects | |
sta Block_X_Speed+2,x | |
lda #$fa | |
sta Block_Y_Speed,x ;set vertical speed for one | |
lda #$fc | |
sta Block_Y_Speed+2,x ;set lower vertical speed for the other | |
lda #$00 | |
sta Block_Y_MoveForce,x ;init fractional movement force for both | |
sta Block_Y_MoveForce+2,x | |
lda Block_PageLoc,x | |
sta Block_PageLoc+2,x ;copy page location | |
lda Block_X_Position,x | |
sta Block_X_Position+2,x ;copy horizontal coordinate | |
lda Block_Y_Position,x | |
clc ;add 8 pixels to vertical coordinate | |
adc #$08 ;and save as vertical coordinate for one of them | |
sta Block_Y_Position+2,x | |
lda #$fa | |
sta Block_Y_Speed,x ;set vertical speed...again??? (redundant) | |
rts | |
;------------------------------------------------------------------------------------- | |
BlockObjectsCore: | |
lda Block_State,x ;get state of block object | |
beq UpdSte ;if not set, branch to leave | |
and #$0f ;mask out high nybble | |
pha ;push to stack | |
tay ;put in Y for now | |
txa | |
clc | |
adc #$09 ;add 9 bytes to offset (note two block objects are created | |
tax ;when using brick chunks, but only one offset for both) | |
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 | |
adc #$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 | |
ldy Block_Y_HighPos,x ;check vertical high byte of block object | |
beq UpdSte ;if above the screen, branch to kill it | |
pha ;otherwise save state back into stack | |
lda #$f0 | |
cmp Block_Y_Position+2,x ;check to see if bottom block object went | |
bcs ChkTop ;to the bottom of the screen, and branch if not | |
sta Block_Y_Position+2,x ;otherwise set offscreen coordinate | |
ChkTop: lda Block_Y_Position,x ;get top block object's vertical coordinate | |
cmp #$f0 ;see if it went to the bottom of the screen | |
pla ;pull block object state from stack | |
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 | |
lda Block_Y_Position,x ;get vertical coordinate | |
and #$0f ;mask out high nybble | |
cmp #$05 ;check to see if low nybble wrapped around | |
pla ;pull state from stack | |
bcs UpdSte ;if still above amount, not time to kill block yet, thus branch | |
lda #$01 | |
sta Block_RepFlag,x ;otherwise set flag to replace metatile | |
KillBlock: lda #$00 ;if branched here, nullify object state | |
UpdSte: sta 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: | |
ldx #$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, | |
bne NextBUpd ;branch to move onto next block object | |
lda Block_RepFlag,x ;if flag for block object already clear, | |
beq NextBUpd ;branch to move onto next block object | |
lda Block_BBuf_Low,x ;get low byte of block buffer | |
sta $06 ;store into block buffer address | |
lda #$05 | |
sta $07 ;set high byte of block buffer address | |
lda Block_Orig_YPos,x ;get original vertical coordinate of block object | |
sta $02 ;store here and use as offset to block buffer | |
tay | |
lda Block_Metatile,x ;get metatile to be written | |
sta ($06),y ;write it to the block buffer | |
jsr ReplaceBlockMetatile ;do sub to replace metatile where block object is | |
lda #$00 | |
sta 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, | |
bne ExXMove ;branch to leave | |
tax ;otherwise set zero for offset to use player's stuff | |
MoveObjectHorizontally: | |
lda SprObject_X_Speed,x ;get currently saved value (horizontal | |
asl ;speed, secondary counter, whatever) | |
asl ;and move low nybble to high | |
asl | |
asl | |
sta $01 ;store result here | |
lda SprObject_X_Speed,x ;get saved value again | |
lsr ;move high nybble to low | |
lsr | |
lsr | |
lsr | |
cmp #$08 ;if < 8, branch, do not change | |
bcc SaveXSpd | |
ora #%11110000 ;otherwise alter high nybble | |
SaveXSpd: sta $00 ;save result here | |
ldy #$00 ;load default Y value here | |
cmp #$00 ;if result positive, leave Y alone | |
bpl UseAdder | |
dey ;otherwise decrement Y | |
UseAdder: sty $02 ;save Y here | |
lda SprObject_X_MoveForce,x ;get whatever number's here | |
clc | |
adc $01 ;add low nybble moved to high | |
sta SprObject_X_MoveForce,x ;store result here | |
lda #$00 ;init A | |
rol ;rotate carry into d0 | |
pha ;push onto stack | |
ror ;rotate d0 back onto carry | |
lda SprObject_X_Position,x | |
adc $00 ;add carry plus saved value (high nybble moved to low | |
sta SprObject_X_Position,x ;plus $f0 if necessary) to object's horizontal position | |
lda SprObject_PageLoc,x | |
adc $02 ;add carry plus other saved value to the | |
sta SprObject_PageLoc,x ;object's page location and save | |
pla | |
clc ;pull old carry from stack and add | |
adc $00 ;to high nybble moved to low | |
ExXMove: rts ;and leave | |
;------------------------------------------------------------------------------------- | |
;$00 - used for downward force | |
;$01 - used for upward force | |
;$02 - used for maximum vertical speed | |
MovePlayerVertically: | |
ldx #$00 ;set X for player offset | |
lda TimerControl | |
bne NoJSChk ;if master timer control set, branch ahead | |
lda JumpspringAnimCtrl ;otherwise check to see if jumpspring is animating | |
bne ExXMove ;branch to leave if so | |
NoJSChk: lda VerticalForce ;dump vertical force | |
sta $00 | |
lda #$04 ;set maximum vertical speed here | |
jmp ImposeGravitySprObj ;then jump to move player vertically | |
;-------------------------------- | |
MoveD_EnemyVertically: | |
ldy #$3d ;set quick movement amount downwards | |
lda Enemy_State,x ;then check enemy state | |
cmp #$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: | |
ldy #$20 ;set movement amount | |
ContVMove: jmp SetHiMax ;jump to skip the rest of this | |
;-------------------------------- | |
MoveRedPTroopaDown: | |
ldy #$00 ;set Y to move downwards | |
jmp MoveRedPTroopa ;skip to movement routine | |
MoveRedPTroopaUp: | |
ldy #$01 ;set Y to move upwards | |
MoveRedPTroopa: | |
inx ;increment X for enemy offset | |
lda #$03 | |
sta $00 ;set downward movement amount here | |
lda #$06 | |
sta $01 ;set upward movement amount here | |
lda #$02 | |
sta $02 ;set maximum speed here | |
tya ;set movement direction in A, and | |
jmp RedPTroopaGrav ;jump to move this thing | |
;-------------------------------- | |
MoveDropPlatform: | |
ldy #$7f ;set movement amount for drop platform | |
bne SetMdMax ;skip ahead of other value set here | |
MoveEnemySlowVert: | |
ldy #$0f ;set movement amount for bowser/other objects | |
SetMdMax: lda #$02 ;set maximum speed in A | |
bne SetXMoveAmt ;unconditional branch | |
;-------------------------------- | |
MoveJ_EnemyVertically: | |
ldy #$1c ;set movement amount for podoboo/other objects | |
SetHiMax: lda #$03 ;set maximum speed in A | |
SetXMoveAmt: sty $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: | |
ldy #$00 ;this part appears to be residual, | |
.db $2c ;no code branches or jumps to it... | |
ImposeGravityBlock: | |
ldy #$01 ;set offset for maximum speed | |
lda #$50 ;set movement amount here | |
sta $00 | |
lda MaxSpdBlockData,y ;get maximum speed | |
ImposeGravitySprObj: | |
sta $02 ;set maximum speed here | |
lda #$00 ;set value to move downwards | |
jmp ImposeGravity ;jump to the code that actually moves it | |
;-------------------------------- | |
MovePlatformDown: | |
lda #$00 ;save value to stack (if branching here, execute next | |
.db $2c ;part as BIT instruction) | |
MovePlatformUp: | |
lda #$01 ;save value to stack | |
pha | |
ldy Enemy_ID,x ;get enemy object identifier | |
inx ;increment offset for enemy object | |
lda #$05 ;load default value here | |
cpy #$29 ;residual comparison, object #29 never executes | |
bne SetDplSpd ;this code, thus unconditional branch here | |
lda #$09 ;residual code | |
SetDplSpd: sta $00 ;save downward movement amount here | |
lda #$0a ;save upward movement amount here | |
sta $01 | |
lda #$03 ;save maximum vertical speed here | |
sta $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 | |
lda SprObject_YMF_Dummy,x | |
clc ;add value in movement force to contents of dummy variable | |
adc SprObject_Y_MoveForce,x | |
sta SprObject_YMF_Dummy,x | |
ldy #$00 ;set Y to zero by default | |
lda SprObject_Y_Speed,x ;get current vertical speed | |
bpl AlterYP ;if currently moving downwards, do not decrement Y | |
dey ;otherwise decrement Y | |
AlterYP: sty $07 ;store Y here | |
adc SprObject_Y_Position,x ;add vertical position to vertical speed plus carry | |
sta SprObject_Y_Position,x ;store as new vertical position | |
lda SprObject_Y_HighPos,x | |
adc $07 ;add carry plus contents of $07 to vertical high byte | |
sta SprObject_Y_HighPos,x ;store as new vertical high byte | |
lda SprObject_Y_MoveForce,x | |
clc | |
adc $00 ;add downward movement amount to contents of $0433 | |
sta SprObject_Y_MoveForce,x | |
lda SprObject_Y_Speed,x ;add carry to vertical speed and store | |
adc #$00 | |
sta SprObject_Y_Speed,x | |
cmp $02 ;compare to maximum speed | |
bmi ChkUpM ;if less than preset value, skip this part | |
lda SprObject_Y_MoveForce,x | |
cmp #$80 ;if less positively than preset maximum, skip this part | |
bcc ChkUpM | |
lda $02 | |
sta SprObject_Y_Speed,x ;keep vertical speed within maximum value | |
lda #$00 | |
sta SprObject_Y_MoveForce,x ;clear fractional | |
ChkUpM: pla ;get value from stack | |
beq ExVMove ;if set to zero, branch to leave | |
lda $02 | |
eor #%11111111 ;otherwise get two's compliment of maximum speed | |
tay | |
iny | |
sty $07 ;store two's compliment here | |
lda SprObject_Y_MoveForce,x | |
sec ;subtract upward movement amount from contents | |
sbc $01 ;of movement force, note that $01 is twice as large as $00, | |
sta SprObject_Y_MoveForce,x ;thus it effectively undoes add we did earlier | |
lda SprObject_Y_Speed,x | |
sbc #$00 ;subtract borrow from vertical speed and store | |
sta SprObject_Y_Speed,x | |
cmp $07 ;compare vertical speed to two's compliment | |
bpl ExVMove ;if less negatively than preset maximum, skip this part | |
lda SprObject_Y_MoveForce,x | |
cmp #$80 ;check if fractional part is above certain amount, | |
bcs ExVMove ;and if so, branch to leave | |
lda $07 | |
sta SprObject_Y_Speed,x ;keep vertical speed within maximum value | |
lda #$ff | |
sta SprObject_Y_MoveForce,x ;clear fractional | |
ExVMove: rts ;leave! | |
;------------------------------------------------------------------------------------- | |
EnemiesAndLoopsCore: | |
lda 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 | |
beq ChkAreaTsk ;if data zero, branch | |
jmp RunEnemyObjectsCore ;otherwise, jump to run enemy subroutines | |
ChkAreaTsk: lda AreaParserTaskNum ;check number of tasks to perform | |
and #$07 | |
cmp #$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 | |
and #%00001111 ;mask out high nybble | |
tay | |
lda Enemy_Flag,y ;use as pointer and load same place with different offset | |
bne ExitELCore | |
sta 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 | |
sec | |
sbc #$04 | |
sta Player_PageLoc | |
lda CurrentPageLoc ;send current page back four pages | |
sec | |
sbc #$04 | |
sta CurrentPageLoc | |
lda ScreenLeft_PageLoc ;subtract four from page location | |
sec ;of screen's left border | |
sbc #$04 | |
sta ScreenLeft_PageLoc | |
lda ScreenRight_PageLoc ;do the same for the page location | |
sec ;of screen's right border | |
sbc #$04 | |
sta ScreenRight_PageLoc | |
lda AreaObjectPageLoc ;subtract four from page control | |
sec ;for area objects | |
sbc #$04 | |
sta AreaObjectPageLoc | |
lda #$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 | |
lda 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 | |
beq ChkEnemyFrenzy | |
lda CurrentColumnPos ;check to see if we're still on the first page | |
bne ChkEnemyFrenzy ;if not, do not loop yet | |
ldy #$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 | |
cmp LoopCmdWorldNumber,y ;matches our current world number | |
bne FindLoop | |
lda CurrentPageLoc ;check to see if one of the page numbers | |
cmp 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 | |
cmp LoopCmdYPosition,y ;if not, branch to check for world 7 | |
bne WrongChk | |
lda Player_State ;check to see if the player is | |
cmp #$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 | |
cmp #World7 ;vertical position and on solid ground) | |
bne InitMLp ;if not, initialize flags used there, otherwise | |
inc MultiLoopCorrectCntr ;increment counter for correct progression | |
IncMLoop: inc MultiLoopPassCntr ;increment master multi-part counter | |
lda MultiLoopPassCntr ;have we done all three parts? | |
cmp #$03 | |
bne InitLCmd ;if not, skip this part | |
lda MultiLoopCorrectCntr ;if so, have we done them all correctly? | |
cmp #$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 | |
cmp #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: lda #$00 ;initialize counters used for multi-part loop commands | |
sta MultiLoopPassCntr | |
sta MultiLoopCorrectCntr | |
InitLCmd: lda #$00 ;initialize loop command flag | |
sta LoopCommand | |
;-------------------------------- | |
ChkEnemyFrenzy: | |
lda EnemyFrenzyQueue ;check for enemy object in frenzy queue | |
beq ProcessEnemyData ;if not, skip this part | |
sta Enemy_ID,x ;store as enemy object identifier here | |
lda #$01 | |
sta Enemy_Flag,x ;activate enemy object flag | |
lda #$00 | |
sta 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 | |
lda (EnemyData),y ;load first byte | |
cmp #$ff ;check for EOD terminator | |
bne CheckEndofBuffer | |
jmp CheckFrenzyBuffer ;if found, jump to check frenzy buffer, otherwise | |
CheckEndofBuffer: | |
and #%00001111 ;check for special row $0e | |
cmp #$0e | |
beq CheckRightBounds ;if found, branch, otherwise | |
cpx #$05 ;check for end of buffer | |
bcc CheckRightBounds ;if not at end of buffer, branch | |
iny | |
lda (EnemyData),y ;check for specific value here | |
and #%00111111 ;not sure what this was intended for, exactly | |
cmp #$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 | |
adc #$30 | |
and #%11110000 ;store high nybble | |
sta $07 | |
lda ScreenRight_PageLoc ;add carry to page location of right boundary | |
adc #$00 | |
sta $06 ;store page location + carry | |
ldy EnemyDataOffset | |
iny | |
lda (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 | |
bne CheckPageCtrlRow | |
inc EnemyObjectPageSel ;otherwise, if MSB is set, set page select | |
inc EnemyObjectPageLoc ;and increment page control | |
CheckPageCtrlRow: | |
dey | |
lda (EnemyData),y ;reread first byte | |
and #$0f | |
cmp #$0f ;check for special row $0f | |
bne PositionEnemyObj ;if not found, branch to position enemy object | |
lda EnemyObjectPageSel ;if page select set, | |
bne PositionEnemyObj ;branch without reading second byte | |
iny | |
lda (EnemyData),y ;otherwise, get second byte, mask out 2 MSB | |
and #%00111111 | |
sta EnemyObjectPageLoc ;store as page control for enemy object data | |
inc EnemyDataOffset ;increment enemy object data offset 2 bytes | |
inc EnemyDataOffset | |
inc 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 | |
sta Enemy_PageLoc,x ;for enemy object | |
lda (EnemyData),y ;get first byte of enemy object | |
and #%11110000 | |
sta Enemy_X_Position,x ;store column position | |
cmp ScreenRight_X_Pos ;check column position against right boundary | |
lda Enemy_PageLoc,x ;without subtracting, then subtract borrow | |
sbc ScreenRight_PageLoc ;from page location | |
bcs CheckRightExtBounds ;if enemy object beyond or at boundary, branch | |
lda (EnemyData),y | |
and #%00001111 ;check for special row $0e | |
cmp #$0e ;if found, jump elsewhere | |
beq ParseRow0e | |
jmp CheckThreeBytes ;if not found, unconditional jump | |
CheckRightExtBounds: | |
lda $07 ;check right boundary + 48 against | |
cmp Enemy_X_Position,x ;column position without subtracting, | |
lda $06 ;then subtract borrow from page control temp | |
sbc Enemy_PageLoc,x ;plus carry | |
bcc CheckFrenzyBuffer ;if enemy object beyond extended boundary, branch | |
lda #$01 ;store value in vertical high byte | |
sta Enemy_Y_HighPos,x | |
lda (EnemyData),y ;get first byte again | |
asl ;multiply by four to get the vertical | |
asl ;coordinate | |
asl | |
asl | |
sta Enemy_Y_Position,x | |
cmp #$e0 ;do one last check for special row $0e | |
beq ParseRow0e ;(necessary if branched to $c1cb) | |
iny | |
lda (EnemyData),y ;get second byte of object | |
and #%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 | |
beq Inc2B ;is on, and if not, branch to skip this object completely | |
CheckForEnemyGroup: | |
lda (EnemyData),y ;get second byte and mask out 2 MSB | |
and #%00111111 | |
cmp #$37 ;check for value below $37 | |
bcc BuzzyBeetleMutate | |
cmp #$3f ;if $37 or greater, check for value | |
bcc DoGroup ;below $3f, branch if below $3f | |
BuzzyBeetleMutate: | |
cmp #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 | |
beq StrID ;and if so, change goomba to buzzy beetle | |
lda #BuzzyBeetle | |
StrID: sta Enemy_ID,x ;store enemy object number into buffer | |
lda #$01 | |
sta Enemy_Flag,x ;set flag for enemy in buffer | |
jsr InitEnemyObject | |
lda Enemy_Flag,x ;check to see if flag is set | |
bne Inc2B ;if not, leave, otherwise branch | |
rts | |
CheckFrenzyBuffer: | |
lda EnemyFrenzyBuffer ;if enemy object stored in frenzy buffer | |
bne StrFre ;then branch ahead to store in enemy object buffer | |
lda VineFlagOffset ;otherwise check vine flag offset | |
cmp #$01 | |
bne ExEPar ;if other value <> 1, leave | |
lda #VineObject ;otherwise put vine in enemy identifier | |
StrFre: sta Enemy_ID,x ;store contents of frenzy buffer into enemy identifier value | |
InitEnemyObject: | |
lda #$00 ;initialize enemy state | |
sta 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 | |
lda (EnemyData),y | |
lsr ;move 3 MSB to the bottom, effectively | |
lsr ;making %xxx00000 into %00000xxx | |
lsr | |
lsr | |
lsr | |
cmp 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) | |
lda (EnemyData),y ;otherwise, get second byte and use as offset | |
sta AreaPointer ;to addresses for level and enemy object data | |
iny | |
lda (EnemyData),y ;get third byte again, and this time mask out | |
and #%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 | |
lda (EnemyData),y ;get first byte | |
and #%00001111 ;check for special row $0e | |
cmp #$0e | |
bne Inc2B | |
Inc3B: inc EnemyDataOffset ;if row = $0e, increment three bytes | |
Inc2B: inc EnemyDataOffset ;otherwise increment two bytes | |
inc EnemyDataOffset | |
lda #$00 ;init page select for enemy objects | |
sta EnemyObjectPageSel | |
ldx ObjectOffset ;reload current offset in enemy buffers | |
rts ;and leave | |
CheckpointEnemyID: | |
lda Enemy_ID,x | |
cmp #$15 ;check enemy object identifier for $15 or greater | |
bcs InitEnemyRoutines ;and branch straight to the jump engine if found | |
tay ;save identifier in Y register for now | |
lda Enemy_Y_Position,x | |
adc #$08 ;add eight pixels to what will eventually be the | |
sta Enemy_Y_Position,x ;enemy object's vertical coordinate ($00-$14 only) | |
lda #$01 | |
sta 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: | |
lda #$02 ;set enemy position to below | |
sta Enemy_Y_HighPos,x ;the bottom of the screen | |
sta Enemy_Y_Position,x | |
lsr | |
sta EnemyIntervalTimer,x ;set timer for enemy | |
lsr | |
sta Enemy_State,x ;initialize enemy state, then jump to use | |
jmp SmallBBox ;$09 as bounding box size and set other things | |
;-------------------------------- | |
InitRetainerObj: | |
lda #$b8 ;set fixed vertical position for | |
sta Enemy_Y_Position,x ;princess/mushroom retainer object | |
rts | |
;-------------------------------- | |
NormalXSpdData: | |
.db $f8, $f4 | |
InitNormalEnemy: | |
ldy #$01 ;load offset of 1 by default | |
lda PrimaryHardMode ;check for primary hard mode flag set | |
bne GetESpd | |
dey ;if not set, decrement offset | |
GetESpd: lda NormalXSpdData,y ;get appropriate horizontal speed | |
SetESpd: sta 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 | |
lda #$01 ;set enemy state for red koopa troopa $03 | |
sta Enemy_State,x | |
rts | |
;-------------------------------- | |
HBroWalkingTimerData: | |
.db $80, $50 | |
InitHammerBro: | |
lda #$00 ;init horizontal speed and timer used by hammer bro | |
sta HammerThrowingTimer,x ;apparently to time hammer throwing | |
sta Enemy_X_Speed,x | |
ldy SecondaryHardMode ;get secondary hard mode flag | |
lda HBroWalkingTimerData,y | |
sta EnemyIntervalTimer,x ;set value as delay for hammer bro to walk left | |
lda #$0b ;set specific value for bounding box size control | |
jmp SetBBox | |
;-------------------------------- | |
InitHorizFlySwimEnemy: | |
lda #$00 ;initialize horizontal speed | |
jmp SetESpd | |
;-------------------------------- | |
InitBloober: | |
lda #$00 ;initialize horizontal speed | |
sta BlooperMoveSpeed,x | |
SmallBBox: lda #$09 ;set specific bounding box size control | |
bne SetBBox ;unconditional branch | |
;-------------------------------- | |
InitRedPTroopa: | |
ldy #$30 ;load central position adder for 48 pixels down | |
lda Enemy_Y_Position,x ;set vertical coordinate into location to | |
sta RedPTroopaOrigXPos,x ;be used as original vertical coordinate | |
bpl GetCent ;if vertical coordinate < $80 | |
ldy #$e0 ;if => $80, load position adder for 32 pixels up | |
GetCent: tya ;send central position adder to A | |
adc Enemy_Y_Position,x ;add to current vertical coordinate | |
sta RedPTroopaCenterYPos,x ;store as central vertical coordinate | |
TallBBox: lda #$03 ;set specific bounding box size control | |
SetBBox: sta Enemy_BoundBoxCtrl,x ;set bounding box control here | |
lda #$02 ;set moving direction for left | |
sta Enemy_MovingDir,x | |
InitVStf: lda #$00 ;initialize vertical speed | |
sta Enemy_Y_Speed,x ;and movement force | |
sta Enemy_Y_MoveForce,x | |
rts | |
;-------------------------------- | |
InitBulletBill: | |
lda #$02 ;set moving direction for left | |
sta Enemy_MovingDir,x | |
lda #$09 ;set bounding box control for $09 | |
sta Enemy_BoundBoxCtrl,x | |
rts | |
;-------------------------------- | |
InitCheepCheep: | |
jsr SmallBBox ;set vertical bounding box, speed, init others | |
lda PseudoRandomBitReg,x ;check one portion of LSFR | |
and #%00010000 ;get d4 from it | |
sta CheepCheepMoveMFlag,x ;save as movement flag of some sort | |
lda Enemy_Y_Position,x | |
sta CheepCheepOrigYPos,x ;save original vertical coordinate here | |
rts | |
;-------------------------------- | |
InitLakitu: | |
lda EnemyFrenzyBuffer ;check to see if an enemy is already in | |
bne KillLakitu ;the frenzy buffer, and branch to kill lakitu if so | |
SetupLakitu: | |
lda #$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 | |
bne ExLSHand | |
cpx #$05 ;if we are on the special use slot, leave | |
bcs ExLSHand | |
lda #$80 ;set timer | |
sta FrenzyEnemyTimer | |
ldy #$04 ;start with the last enemy slot | |
ChkLak: lda Enemy_ID,y ;check all enemy slots to see | |
cmp #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 | |
inc LakituReappearTimer ;increment reappearance timer | |
lda LakituReappearTimer | |
cmp #$07 ;check to see if we're up to a certain value yet | |
bcc ExLSHand ;if not, leave | |
ldx #$04 ;start with the last enemy slot again | |
ChkNoEn: lda Enemy_Flag,x ;check enemy buffer flag for non-active enemy slot | |
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: lda #$00 ;initialize enemy state | |
sta Enemy_State,x | |
lda #Lakitu ;create lakitu enemy object | |
sta Enemy_ID,x | |
jsr SetupLakitu ;do a sub to set up lakitu | |
lda #$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 | |
cmp #$2c | |
bcc ExLSHand | |
lda Enemy_State,y ;if lakitu is not in normal state, branch to leave | |
bne ExLSHand | |
lda Enemy_PageLoc,y ;store horizontal coordinates (high and low) of lakitu | |
sta Enemy_PageLoc,x ;into the coordinates of the spiny we're going to create | |
lda Enemy_X_Position,y | |
sta Enemy_X_Position,x | |
lda #$01 ;put spiny within vertical screen unit | |
sta Enemy_Y_HighPos,x | |
lda Enemy_Y_Position,y ;put spiny eight pixels above where lakitu is | |
sec | |
sbc #$08 | |
sta Enemy_Y_Position,x | |
lda PseudoRandomBitReg,x ;get 2 LSB of LSFR and save to Y | |
and #%00000011 | |
tay | |
ldx #$02 | |
DifLoop: lda PRDiffAdjustData,y ;get three values and save them | |
sta $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 | |
cpy #$08 | |
bcs SetSpSpd ;if moving faster than a certain amount, branch elsewhere | |
tay ;otherwise save value in A to Y for now | |
lda PseudoRandomBitReg+1,x | |
and #%00000011 ;get one of the LSFR parts and save the 2 LSB | |
beq UsePosv ;branch if neither bits are set | |
tya | |
eor #%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 | |
ldy #$02 | |
sta Enemy_X_Speed,x ;set horizontal speed to zero because previous contents | |
cmp #$00 ;of A were lost...branch here will never be taken for | |
bmi SpinyRte ;the same reason | |
dey | |
SpinyRte: sty Enemy_MovingDir,x ;set moving direction to the right | |
lda #$fd | |
sta Enemy_Y_Speed,x ;set vertical speed to move upwards | |
lda #$01 | |
sta Enemy_Flag,x ;enable enemy object by setting flag | |
lda #$05 | |
sta 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: | |
lda #$00 ;initialize low byte of spin state | |
sta FirebarSpinState_Low,x | |
lda Enemy_ID,x ;subtract $1b from enemy identifier | |
sec ;to get proper offset for firebar data | |
sbc #$1b | |
tay | |
lda FirebarSpinSpdData,y ;get spinning speed of firebar | |
sta FirebarSpinSpeed,x | |
lda FirebarSpinDirData,y ;get spinning direction of firebar | |
sta FirebarSpinDirection,x | |
lda Enemy_Y_Position,x | |
clc ;add four pixels to vertical coordinate | |
adc #$04 | |
sta Enemy_Y_Position,x | |
lda Enemy_X_Position,x | |
clc ;add four pixels to horizontal coordinate | |
adc #$04 | |
sta Enemy_X_Position,x | |
lda Enemy_PageLoc,x | |
adc #$00 ;add carry to page location | |
sta 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 | |
bne ChpChpEx | |
jsr SmallBBox ;jump to set bounding box size $09 and init other values | |
lda PseudoRandomBitReg+1,x | |
and #%00000011 ;set pseudorandom offset here | |
tay | |
lda FlyCCTimerData,y ;load timer with pseudorandom offset | |
sta FrenzyEnemyTimer | |
ldy #$03 ;load Y with default value | |
lda SecondaryHardMode | |
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 $00 ;store whatever pseudorandom bits are in Y | |
cpx $00 ;compare enemy object buffer offset with Y | |
bcs ChpChpEx ;if X => Y, branch to leave | |
lda PseudoRandomBitReg,x | |
and #%00000011 ;get last two bits of LSFR, first part | |
sta $00 ;and store in two places | |
sta $01 | |
lda #$fb ;set vertical speed for cheep-cheep | |
sta Enemy_Y_Speed,x | |
lda #$00 ;load default value | |
ldy Player_X_Speed ;check player's horizontal speed | |
beq GSeed ;if player not moving left or right, skip this part | |
lda #$04 | |
cpy #$19 ;if moving to the right but not very quickly, | |
bcc GSeed ;do not change A | |
asl ;otherwise, multiply A by 2 | |
GSeed: pha ;save to stack | |
clc | |
adc $00 ;add to last two bits of LSFR we saved earlier | |
sta $00 ;save it there | |
lda PseudoRandomBitReg+1,x | |
and #%00000011 ;if neither of the last two bits of second LSFR set, | |
beq RSeed ;skip this part and save contents of $00 | |
lda PseudoRandomBitReg+2,x | |
and #%00001111 ;otherwise overwrite with lower nybble of | |
sta $00 ;third LSFR part | |
RSeed: pla ;get value from stack we saved earlier | |
clc | |
adc $01 ;add to last two bits of LSFR we saved in other place | |
tay ;use as pseudorandom offset here | |
lda FlyCCXSpeedData,y ;get horizontal speed using pseudorandom offset | |
sta Enemy_X_Speed,x | |
lda #$01 ;set to move towards the right | |
sta Enemy_MovingDir,x | |
lda Player_X_Speed ;if player moving left or right, branch ahead of this part | |
bne D2XPos1 | |
ldy $00 ;get first LSFR or third LSFR lower nybble | |
tya ;and check for d1 set | |
and #%00000010 | |
beq D2XPos1 ;if d1 not set, branch | |
lda Enemy_X_Speed,x | |
eor #$ff ;if d1 set, change horizontal speed | |
clc ;into two's compliment, thus moving in the opposite | |
adc #$01 ;direction | |
sta Enemy_X_Speed,x | |
inc Enemy_MovingDir,x ;increment to move towards the left | |
D2XPos1: tya ;get first LSFR or third LSFR lower nybble again | |
and #%00000010 | |
beq D2XPos2 ;check for d1 set again, branch again if not set | |
lda Player_X_Position ;get player's horizontal position | |
clc | |
adc FlyCCXPositionData,y ;if d1 set, add value obtained from pseudorandom offset | |
sta Enemy_X_Position,x ;and save as enemy's horizontal position | |
lda Player_PageLoc ;get player's page location | |
adc #$00 ;add carry and jump past this part | |
jmp FinCCSt | |
D2XPos2: lda Player_X_Position ;get player's horizontal position | |
sec | |
sbc FlyCCXPositionData,y ;if d1 not set, subtract value obtained from pseudorandom | |
sta Enemy_X_Position,x ;offset and save as enemy's horizontal position | |
lda Player_PageLoc ;get player's page location | |
sbc #$00 ;subtract borrow | |
FinCCSt: sta Enemy_PageLoc,x ;save as enemy's page location | |
lda #$01 | |
sta Enemy_Flag,x ;set enemy's buffer flag | |
sta Enemy_Y_HighPos,x ;set enemy's high vertical byte | |
lda #$f8 | |
sta 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 | |
lda #$00 | |
sta BowserBodyControls ;initialize bowser's body controls | |
sta BridgeCollapseOffset ;and bridge collapse offset | |
lda Enemy_X_Position,x | |
sta BowserOrigXPos ;store original horizontal position here | |
lda #$df | |
sta BowserFireBreathTimer ;store something here | |
sta Enemy_MovingDir,x ;and in moving direction | |
lda #$20 | |
sta BowserFeetCounter ;set bowser's feet timer and in enemy timer | |
sta EnemyFrameTimer,x | |
lda #$05 | |
sta BowserHitPoints ;give bowser 5 hit points | |
lsr | |
sta BowserMovementSpeed ;set default movement speed here | |
rts | |
;-------------------------------- | |
DuplicateEnemyObj: | |
ldy #$ff ;start at beginning of enemy slots | |
FSLoop: iny ;increment one slot | |
lda Enemy_Flag,y ;check enemy buffer flag for empty slot | |
bne FSLoop ;if set, branch and keep checking | |
sty DuplicateObj_Offset ;otherwise set offset here | |
txa ;transfer original enemy buffer offset | |
ora #%10000000 ;store with d7 set as flag in new enemy | |
sta Enemy_Flag,y ;slot as well as enemy offset | |
lda Enemy_PageLoc,x | |
sta Enemy_PageLoc,y ;copy page location and horizontal coordinates | |
lda Enemy_X_Position,x ;from original enemy to new enemy | |
sta Enemy_X_Position,y | |
lda #$01 | |
sta Enemy_Flag,x ;set flag as normal for original enemy | |
sta Enemy_Y_HighPos,y ;set high vertical byte for new enemy | |
lda Enemy_Y_Position,x | |
sta 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 | |
bne FlmEx | |
sta Enemy_Y_MoveForce,x ;reset something here | |
lda NoiseSoundQueue | |
ora #Sfx_BowserFlame ;load bowser's flame sound into queue | |
sta NoiseSoundQueue | |
ldy BowserFront_Offset ;get bowser's buffer offset | |
lda Enemy_ID,y ;check for bowser | |
cmp #Bowser | |
beq SpawnFromMouth ;branch if found | |
jsr SetFlameTimer ;get timer data based on flame counter | |
clc | |
adc #$20 ;add 32 frames by default | |
ldy SecondaryHardMode | |
beq SetFrT ;if secondary mode flag not set, use as timer setting | |
sec | |
sbc #$10 ;otherwise subtract 16 frames for secondary hard mode | |
SetFrT: sta FrenzyEnemyTimer ;set timer accordingly | |
lda PseudoRandomBitReg,x | |
and #%00000011 ;get 2 LSB from first part of LSFR | |
sta BowserFlamePRandomOfs,x ;set here | |
tay ;use as offset | |
lda FlameYPosData,y ;load vertical position based on pseudorandom offset | |
PutAtRightExtent: | |
sta Enemy_Y_Position,x ;set vertical position | |
lda ScreenRight_X_Pos | |
clc | |
adc #$20 ;place enemy 32 pixels beyond right side of screen | |
sta Enemy_X_Position,x | |
lda ScreenRight_PageLoc | |
adc #$00 ;add carry | |
sta Enemy_PageLoc,x | |
jmp FinishFlame ;skip this part to finish setting values | |
SpawnFromMouth: | |
lda Enemy_X_Position,y ;get bowser's horizontal position | |
sec | |
sbc #$0e ;subtract 14 pixels | |
sta Enemy_X_Position,x ;save as flame's horizontal position | |
lda Enemy_PageLoc,y | |
sta Enemy_PageLoc,x ;copy page location from bowser to flame | |
lda Enemy_Y_Position,y | |
clc ;add 8 pixels to bowser's vertical position | |
adc #$08 | |
sta Enemy_Y_Position,x ;save as flame's vertical position | |
lda PseudoRandomBitReg,x | |
and #%00000011 ;get 2 LSB from first part of LSFR | |
sta Enemy_YMF_Dummy,x ;save here | |
tay ;use as offset | |
lda FlameYPosData,y ;get value here using bits as offset | |
ldy #$00 ;load default offset | |
cmp Enemy_Y_Position,x ;compare value to flame's current vertical position | |
bcc SetMF ;if less, do not increment offset | |
iny ;otherwise increment now | |
SetMF: lda FlameYMFAdderData,y ;get value here and save | |
sta Enemy_Y_MoveForce,x ;to vertical movement force | |
lda #$00 | |
sta EnemyFrenzyBuffer ;clear enemy frenzy buffer | |
FinishFlame: | |
lda #$08 ;set $08 for bounding box control | |
sta Enemy_BoundBoxCtrl,x | |
lda #$01 ;set high byte of vertical and | |
sta Enemy_Y_HighPos,x ;enemy buffer flag | |
sta Enemy_Flag,x | |
lsr | |
sta Enemy_X_MoveForce,x ;initialize horizontal movement force, and | |
sta 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 | |
bne ExitFWk | |
lda #$20 ;otherwise reset timer | |
sta FrenzyEnemyTimer | |
dec FireworksCounter ;decrement for each explosion | |
ldy #$06 ;start at last slot | |
StarFChk: dey | |
lda Enemy_ID,y ;check for presence of star flag object | |
cmp #StarFlagObject ;if there isn't a star flag object, | |
bne StarFChk ;routine goes into infinite loop = crash | |
lda Enemy_X_Position,y | |
sec ;get horizontal coordinate of star flag object, then | |
sbc #$30 ;subtract 48 pixels from it and save to | |
pha ;the stack | |
lda Enemy_PageLoc,y | |
sbc #$00 ;subtract the carry from the page location | |
sta $00 ;of the star flag object | |
lda FireworksCounter ;get fireworks counter | |
clc | |
adc 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 | |
adc FireworksXPosData,y ;add number based on offset of fireworks counter | |
sta Enemy_X_Position,x ;store as the fireworks object horizontal coordinate | |
lda $00 | |
adc #$00 ;add carry and store as page location for | |
sta Enemy_PageLoc,x ;the fireworks object | |
lda FireworksYPosData,y ;get vertical position using same offset | |
sta Enemy_Y_Position,x ;and store as vertical coordinate for fireworks object | |
lda #$01 | |
sta Enemy_Y_HighPos,x ;store in vertical high byte | |
sta Enemy_Flag,x ;and activate enemy buffer flag | |
lsr | |
sta ExplosionGfxCounter,x ;initialize explosion counter | |
lda #$08 | |
sta 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 | |
bne ExF17 | |
lda AreaType ;are we in a water-type level? | |
bne DoBulletBills ;if not, branch elsewhere | |
cpx #$03 ;are we past third enemy slot? | |
bcs ExF17 ;if so, branch to leave | |
ldy #$00 ;load default offset | |
lda PseudoRandomBitReg,x | |
cmp #$aa ;check first part of LSFR against preset value | |
bcc ChkW2 ;if less than preset, do not increment offset | |
iny ;otherwise increment | |
ChkW2: lda WorldNumber ;check world number | |
cmp #World2 | |
beq Get17ID ;if we're on world 2, do not increment offset | |
iny ;otherwise increment | |
Get17ID: tya | |
and #%00000001 ;mask out all but last bit of offset | |
tay | |
lda SwimCC_IDData,y ;load identifier for cheep-cheeps | |
Set17ID: sta Enemy_ID,x ;store whatever's in A as enemy identifier | |
lda BitMFilter | |
cmp #$ff ;if not all bits set, skip init part and compare bits | |
bne GetRBit | |
lda #$00 ;initialize vertical position filter | |
sta BitMFilter | |
GetRBit: lda PseudoRandomBitReg,x ;get first part of LSFR | |
and #%00000111 ;mask out all but 3 LSB | |
ChkRBit: tay ;use as offset | |
lda Bitmasks,y ;load bitmask | |
bit BitMFilter ;perform AND on filter without changing it | |
beq AddFBit | |
iny ;increment offset | |
tya | |
and #%00000111 ;mask out all but 3 LSB thus keeping it 0-7 | |
jmp ChkRBit ;do another check | |
AddFBit: ora BitMFilter ;add bit to already set bits in filter | |
sta BitMFilter ;and store | |
lda Enemy17YPosData,y ;load vertical position using offset | |
jsr PutAtRightExtent ;set vertical position and other values | |
sta Enemy_YMF_Dummy,x ;initialize dummy variable | |
lda #$20 ;set timer | |
sta FrenzyEnemyTimer | |
jmp CheckpointEnemyID ;process our new enemy object | |
DoBulletBills: | |
ldy #$ff ;start at beginning of enemy slots | |
BB_SLoop: iny ;move onto the next slot | |
cpy #$05 ;branch to play sound if we've done all slots | |
bcs FireBulletBill | |
lda Enemy_Flag,y ;if enemy buffer flag not set, | |
beq BB_SLoop ;loop back and check another slot | |
lda Enemy_ID,y | |
cmp #BulletBill_FrenzyVar ;check enemy identifier for | |
bne BB_SLoop ;bullet bill object (frenzy variant) | |
ExF17: rts ;if found, leave | |
FireBulletBill: | |
lda Square2SoundQueue | |
ora #Sfx_Blast ;play fireworks/gunfire sound | |
sta Square2SoundQueue | |
lda #BulletBill_FrenzyVar ;load identifier for bullet bill object | |
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: | |
ldy #$00 ;load value for green koopa troopa | |
sec | |
sbc #$37 ;subtract $37 from second byte read | |
pha ;save result in stack for now | |
cmp #$04 ;was byte in $3b-$3e range? | |
bcs SnglID ;if so, branch | |
pha ;save another copy to stack | |
ldy #Goomba ;load value for goomba enemy | |
lda PrimaryHardMode ;if primary hard mode flag not set, | |
beq PullID ;branch, otherwise change to value | |
ldy #BuzzyBeetle ;for buzzy beetle | |
PullID: pla ;get second copy from stack | |
SnglID: sty $01 ;save enemy id here | |
ldy #$b0 ;load default y coordinate | |
and #$02 ;check to see if d1 was set | |
beq SetYGp ;if so, move y coordinate up, | |
ldy #$70 ;otherwise branch and use default | |
SetYGp: sty $00 ;save y coordinate here | |
lda ScreenRight_PageLoc ;get page number of right edge of screen | |
sta $02 ;save here | |
lda ScreenRight_X_Pos ;get pixel coordinate of right edge | |
sta $03 ;save here | |
ldy #$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: ldx #$ff ;start at beginning of enemy buffers | |
GSltLp: inx ;increment and branch if past | |
cpx #$05 ;end of buffers | |
bcs NextED | |
lda Enemy_Flag,x ;check to see if enemy is already | |
bne GSltLp ;stored in buffer, and branch if so | |
lda $01 | |
sta Enemy_ID,x ;store enemy object identifier | |
lda $02 | |
sta Enemy_PageLoc,x ;store page location for enemy object | |
lda $03 | |
sta Enemy_X_Position,x ;store x coordinate for enemy object | |
clc | |
adc #$18 ;add 24 pixels for next enemy | |
sta $03 | |
lda $02 ;add carry to page location for | |
adc #$00 ;next enemy | |
sta $02 | |
lda $00 ;store y coordinate for enemy object | |
sta Enemy_Y_Position,x | |
lda #$01 ;activate flag for buffer, and | |
sta Enemy_Y_HighPos,x ;put enemy within the screen vertically | |
sta Enemy_Flag,x | |
jsr CheckpointEnemyID ;process each enemy object separately | |
dec NumberofGroupEnemies ;do this until we run out of enemy objects | |
bne GrLoop | |
NextED: jmp Inc2B ;jump to increment data offset and leave | |
;-------------------------------- | |
InitPiranhaPlant: | |
lda #$01 ;set initial speed | |
sta PiranhaPlant_Y_Speed,x | |
lsr | |
sta Enemy_State,x ;initialize enemy state and what would normally | |
sta PiranhaPlant_MoveFlag,x ;be used as vertical speed, but not in this case | |
lda Enemy_Y_Position,x | |
sta PiranhaPlantDownYPos,x ;save original vertical coordinate here | |
sec | |
sbc #$18 | |
sta PiranhaPlantUpYPos,x ;save original vertical coordinate - 24 pixels here | |
lda #$09 | |
jmp SetBBox2 ;set specific value for bounding box control | |
;-------------------------------- | |
InitEnemyFrenzy: | |
lda Enemy_ID,x ;load enemy identifier | |
sta EnemyFrenzyBuffer ;save in enemy frenzy buffer | |
sec | |
sbc #$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: | |
ldy #$05 ;start at last slot | |
LakituChk: lda Enemy_ID,y ;check enemy identifiers | |
cmp #Lakitu ;for lakitu | |
bne NextFSlot | |
lda #$01 ;if found, set state | |
sta Enemy_State,y | |
NextFSlot: dey ;move onto the next slot | |
bpl LakituChk ;do this until all slots are checked | |
lda #$00 | |
sta EnemyFrenzyBuffer ;empty enemy frenzy buffer | |
sta Enemy_Flag,x ;disable enemy buffer flag for this object | |
rts | |
;-------------------------------- | |
InitJumpGPTroopa: | |
lda #$02 ;set for movement to the left | |
sta Enemy_MovingDir,x | |
lda #$f8 ;set horizontal speed | |
sta Enemy_X_Speed,x | |
TallBBox2: lda #$03 ;set specific value for bounding box control | |
SetBBox2: sta Enemy_BoundBoxCtrl,x ;set bounding box control then leave | |
rts | |
;-------------------------------- | |
InitBalPlatform: | |
dec Enemy_Y_Position,x ;raise vertical position by two pixels | |
dec Enemy_Y_Position,x | |
ldy SecondaryHardMode ;if secondary hard mode flag not set, | |
bne AlignP ;branch ahead | |
ldy #$02 ;otherwise set value here | |
jsr PosPlatform ;do a sub to add or subtract pixels | |
AlignP: ldy #$ff ;set default value here for now | |
lda BalPlatformAlignment ;get current balance platform alignment | |
sta Enemy_State,x ;set platform alignment to object state here | |
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 | |
lda #$00 | |
sta Enemy_MovingDir,x ;init moving direction | |
tay ;init Y | |
jsr PosPlatform ;do a sub to add 8 pixels, then run shared code here | |
;-------------------------------- | |
InitDropPlatform: | |
lda #$ff | |
sta PlatformCollisionFlag,x ;set some value here | |
jmp CommonPlatCode ;then jump ahead to execute more code | |
;-------------------------------- | |
InitHoriPlatform: | |
lda #$00 | |
sta XMoveSecondaryCounter,x ;init one of the moving counters | |
jmp CommonPlatCode ;jump ahead to execute more code | |
;-------------------------------- | |
InitVertPlatform: | |
ldy #$40 ;set default value here | |
lda Enemy_Y_Position,x ;check vertical position | |
bpl SetYO ;if above a certain point, skip this part | |
eor #$ff | |
clc ;otherwise get two's compliment | |
adc #$01 | |
ldy #$c0 ;get alternate value to add to vertical position | |
SetYO: sta YPlatformTopYPos,x ;save as top vertical position | |
tya | |
clc ;load value from earlier, add number of pixels | |
adc Enemy_Y_Position,x ;to vertical position | |
sta YPlatformCenterYPos,x ;save result as central vertical position | |
;-------------------------------- | |
CommonPlatCode: | |
jsr InitVStf ;do a sub to init certain other values | |
SPBBox: lda #$05 ;set default bounding box size control | |
ldy AreaType | |
cpy #$03 ;check for castle-type level | |
beq CasPBB ;use default value if found | |
ldy SecondaryHardMode ;otherwise check for secondary hard mode flag | |
bne CasPBB ;if set, use default value | |
lda #$06 ;use alternate value if not castle or secondary not set | |
CasPBB: sta 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: | |
lda #$10 ;set movement amount here | |
sta Enemy_Y_MoveForce,x | |
lda #$ff ;set moving speed for platforms going up | |
sta Enemy_Y_Speed,x | |
jmp CommonSmallLift ;skip ahead to part we should be executing | |
;-------------------------------- | |
PlatLiftDown: | |
lda #$f0 ;set movement amount here | |
sta Enemy_Y_MoveForce,x | |
lda #$00 ;set moving speed for platforms going down | |
sta Enemy_Y_Speed,x | |
;-------------------------------- | |
CommonSmallLift: | |
ldy #$01 | |
jsr PosPlatform ;do a sub to add 12 pixels due to preset value | |
lda #$04 | |
sta Enemy_BoundBoxCtrl,x ;set bounding box control for small platforms | |
rts | |
;-------------------------------- | |
PlatPosDataLow: | |
.db $08,$0c,$f8 | |
PlatPosDataHigh: | |
.db $00,$00,$ff | |
PosPlatform: | |
lda Enemy_X_Position,x ;get horizontal coordinate | |
clc | |
adc PlatPosDataLow,y ;add or subtract pixels depending on offset | |
sta Enemy_X_Position,x ;store as new horizontal coordinate | |
lda Enemy_PageLoc,x | |
adc PlatPosDataHigh,y ;add or subtract page location depending on offset | |
sta Enemy_PageLoc,x ;store as new page location | |
rts ;and go back | |
;-------------------------------- | |
EndOfEnemyInitCode: | |
rts | |
;------------------------------------------------------------------------------------- | |
RunEnemyObjectsCore: | |
ldx ObjectOffset ;get offset for enemy object buffer | |
lda #$00 ;load value 0 for jump engine by default | |
ldy Enemy_ID,x | |
cpy #$15 ;if enemy object < $15, use default value | |
bcc JmpEO | |
tya ;otherwise subtract $14 from the value and use | |
sbc #$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: | |
lda #$00 ;init sprite attributes | |
sta Enemy_SprAttrib,x | |
jsr GetEnemyOffscreenBits | |
jsr RelativeEnemyPosition | |
jsr EnemyGfxHandler | |
jsr GetEnemyBoundBox | |
jsr EnemyToBGCollisionDet | |
jsr EnemiesCollision | |
jsr PlayerEnemyCollision | |
ldy TimerControl ;if master timer control set, skip to last routine | |
bne SkipMove | |
jsr EnemyMovementSubs | |
SkipMove: jmp OffscreenBoundsCheck | |
EnemyMovementSubs: | |
lda 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, | |
bne SkipPT ;skip subroutine tree | |
jsr LargePlatformSubroutines | |
SkipPT: jsr RelativeEnemyPosition | |
jsr DrawLargePlatform | |
jmp OffscreenBoundsCheck | |
;-------------------------------- | |
LargePlatformSubroutines: | |
lda Enemy_ID,x ;subtract $24 to get proper offset for jump table | |
sec | |
sbc #$24 | |
jsr JumpEngine | |
.dw BalancePlatform ;table used by objects $24-$2a | |
.dw YMovingPlatform | |
.dw MoveLargeLiftPlat | |
.dw MoveLargeLiftPlat | |
.dw XMovingPlatform | |
.dw DropPlatform | |
.dw RightPlatform | |
;------------------------------------------------------------------------------------- | |
EraseEnemyObject: | |
lda #$00 ;clear all enemy object variables | |
sta Enemy_Flag,x | |
sta Enemy_ID,x | |
sta Enemy_State,x | |
sta FloateyNum_Control,x | |
sta EnemyIntervalTimer,x | |
sta ShellChainCounter,x | |
sta Enemy_SprAttrib,x | |
sta EnemyFrameTimer,x | |
rts | |
;------------------------------------------------------------------------------------- | |
MovePodoboo: | |
lda EnemyIntervalTimer,x ;check enemy timer | |
bne PdbM ;branch to move enemy if not expired | |
jsr InitPodoboo ;otherwise set up podoboo again | |
lda PseudoRandomBitReg+1,x ;get part of LSFR | |
ora #%10000000 ;set d7 | |
sta Enemy_Y_MoveForce,x ;store as movement force | |
and #%00001111 ;mask out high nybble | |
ora #$06 ;set for at least six intervals | |
sta EnemyIntervalTimer,x ;store as new enemy timer | |
lda #$f9 | |
sta 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: | |
lda Enemy_State,x ;check hammer bro's enemy state for d5 set | |
and #%00100000 | |
beq ChkJH ;if not set, go ahead with code | |
jmp MoveDefeatedEnemy ;otherwise jump to something else | |
ChkJH: lda HammerBroJumpTimer,x ;check jump timer | |
beq HammerBroJumpCode ;if expired, branch to jump | |
dec HammerBroJumpTimer,x ;otherwise decrement jump timer | |
lda Enemy_OffscreenBits | |
and #%00001100 ;check offscreen bits | |
bne MoveHammerBroXDir ;if hammer bro a little offscreen, skip to movement code | |
lda HammerThrowingTimer,x ;check hammer throwing timer | |
bne DecHT ;if not expired, skip ahead, do not throw hammer | |
ldy SecondaryHardMode ;otherwise get secondary hard mode flag | |
lda HammerThrowTmrData,y ;get timer data using flag as offset | |
sta 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 | |
lda Enemy_State,x | |
ora #%00001000 ;set d3 in enemy state for hammer throw | |
sta Enemy_State,x | |
jmp MoveHammerBroXDir ;jump to move hammer bro | |
DecHT: dec HammerThrowingTimer,x ;decrement timer | |
jmp MoveHammerBroXDir ;jump to move hammer bro | |
HammerBroJumpLData: | |
.db $20, $37 | |
HammerBroJumpCode: | |
lda Enemy_State,x ;get hammer bro's enemy state | |
and #%00000111 ;mask out all but 3 LSB | |
cmp #$01 ;check for d0 set (for jumping) | |
beq MoveHammerBroXDir ;if set, branch ahead to moving code | |
lda #$00 ;load default value here | |
sta $00 ;save into temp variable for now | |
ldy #$fa ;set default vertical speed | |
lda Enemy_Y_Position,x ;check hammer bro's vertical coordinate | |
bmi SetHJ ;if on the bottom half of the screen, use current speed | |
ldy #$fd ;otherwise set alternate vertical speed | |
cmp #$70 ;check to see if hammer bro is above the middle of screen | |
inc $00 ;increment preset value to $01 | |
bcc SetHJ ;if above the middle of the screen, use current speed and $01 | |
dec $00 ;otherwise return value to $00 | |
lda PseudoRandomBitReg+1,x ;get part of LSFR, mask out all but LSB | |
and #$01 | |
bne SetHJ ;if d0 of LSFR set, branch and use current speed and $00 | |
ldy #$fa ;otherwise reset to default vertical speed | |
SetHJ: sty Enemy_Y_Speed,x ;set vertical speed for jumping | |
lda Enemy_State,x ;set d0 in enemy state for jumping | |
ora #$01 | |
sta Enemy_State,x | |
lda $00 ;load preset value here to use as bitmask | |
and PseudoRandomBitReg+2,x ;and do bit-wise comparison with part of LSFR | |
tay ;then use as offset | |
lda SecondaryHardMode ;check secondary hard mode flag | |
bne HJump | |
tay ;if secondary hard mode flag clear, set offset to 0 | |
HJump: lda HammerBroJumpLData,y ;get jump length timer data using offset from before | |
sta EnemyFrameTimer,x ;save in enemy timer | |
lda PseudoRandomBitReg+1,x | |
ora #%11000000 ;get contents of part of LSFR, set d7 and d6, then | |
sta HammerBroJumpTimer,x ;store in jump timer | |
MoveHammerBroXDir: | |
ldy #$fc ;move hammer bro a little to the left | |
lda FrameCounter | |
and #%01000000 ;change hammer bro's direction every 64 frames | |
bne Shimmy | |
ldy #$04 ;if d6 set in counter, move him a little to the right | |
Shimmy: sty Enemy_X_Speed,x ;store horizontal speed | |
ldy #$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 | |
lda EnemyIntervalTimer,x ;check walking timer | |
bne SetShim ;if not yet expired, skip to set moving direction | |
lda #$f8 | |
sta Enemy_X_Speed,x ;otherwise, make the hammer bro walk left towards player | |
SetShim: sty Enemy_MovingDir,x ;set moving direction | |
MoveNormalEnemy: | |
ldy #$00 ;init Y to leave horizontal movement as-is | |
lda Enemy_State,x | |
and #%01000000 ;check enemy state for d6 set, if set skip | |
bne FallE ;to move enemy vertically, then horizontally if necessary | |
lda Enemy_State,x | |
asl ;check enemy state for d7 set | |
bcs SteadM ;if set, branch to move enemy horizontally | |
lda Enemy_State,x | |
and #%00100000 ;check enemy state for d5 set | |
bne MoveDefeatedEnemy ;if set, branch to move defeated enemy object | |
lda Enemy_State,x | |
and #%00000111 ;check d2-d0 of enemy state for any set bits | |
beq SteadM ;if enemy in normal state, branch to move enemy horizontally | |
cmp #$05 | |
beq FallE ;if enemy in state used by spiny's egg, go ahead here | |
cmp #$03 | |
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 | |
ldy #$00 | |
lda Enemy_State,x ;check for enemy state $02 | |
cmp #$02 | |
beq MEHor ;if found, branch to move enemy horizontally | |
and #%01000000 ;check for d6 set | |
beq SteadM ;if not set, branch to something else | |
lda Enemy_ID,x | |
cmp #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 <> $2e and d6 set | |
SlowM: ldy #$01 ;if branched here, increment Y to slow horizontal movement | |
SteadM: lda Enemy_X_Speed,x ;get current horizontal speed | |
pha ;save to stack | |
bpl AddHS ;if not moving or moving right, skip, leave Y alone | |
iny | |
iny ;otherwise increment Y to next data | |
AddHS: clc | |
adc XSpeedAdderData,y ;add value here to slow enemy down if necessary | |
sta Enemy_X_Speed,x ;save as horizontal speed temporarily | |
jsr MoveEnemyHorizontally ;then do a sub to move horizontally | |
pla | |
sta Enemy_X_Speed,x ;get old horizontal speed from stack and return to | |
rts ;original memory location, then leave | |
ReviveStunned: | |
lda EnemyIntervalTimer,x ;if enemy timer not expired yet, | |
bne ChkKillGoomba ;skip ahead to something else | |
sta Enemy_State,x ;otherwise initialize enemy state to normal | |
lda FrameCounter | |
and #$01 ;get d0 of frame counter | |
tay ;use as Y and increment for movement direction | |
iny | |
sty Enemy_MovingDir,x ;store as pseudorandom movement direction | |
dey ;decrement for use as pointer | |
lda PrimaryHardMode ;check primary hard mode flag | |
beq SetRSpd ;if not set, use pointer as-is | |
iny | |
iny ;otherwise increment 2 bytes to next data | |
SetRSpd: lda RevivedXSpeed,y ;load and store new horizontal speed | |
sta 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: | |
cmp #$0e ;check to see if enemy timer has reached | |
bne NKGmba ;a certain point, and branch to leave if not | |
lda Enemy_ID,x | |
cmp #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: | |
lda Enemy_Y_Speed,x | |
ora Enemy_Y_MoveForce,x ;check for any vertical force or speed | |
bne MoveRedPTUpOrDown ;branch if any found | |
sta Enemy_YMF_Dummy,x ;initialize something here | |
lda Enemy_Y_Position,x ;check current vs. original vertical coordinate | |
cmp RedPTroopaOrigXPos,x | |
bcs MoveRedPTUpOrDown ;if current => original, skip ahead to more code | |
lda FrameCounter ;get frame counter | |
and #%00000111 ;mask out all but 3 LSB | |
bne NoIncPT ;if any bits set, branch to leave | |
inc Enemy_Y_Position,x ;otherwise increment red paratroopa's vertical position | |
NoIncPT: rts ;leave | |
MoveRedPTUpOrDown: | |
lda Enemy_Y_Position,x ;check current vs. central vertical coordinate | |
cmp RedPTroopaCenterYPos,x | |
bcc MovPTDwn ;if current < 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 | |
ldy #$01 ;set Y to move green paratroopa down | |
lda FrameCounter | |
and #%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 | |
and #%01000000 ;check frame counter for d6 set | |
bne YSway ;branch to move green paratroopa down if set | |
ldy #$ff ;otherwise set Y to move green paratroopa up | |
YSway: sty $00 ;store adder here | |
lda Enemy_Y_Position,x | |
clc ;add or subtract from vertical position | |
adc $00 ;to give green paratroopa a wavy flight | |
sta Enemy_Y_Position,x | |
NoMGPT: rts ;leave! | |
XMoveCntr_GreenPTroopa: | |
lda #$13 ;load preset maximum value for secondary counter | |
XMoveCntr_Platform: | |
sta $01 ;store value here | |
lda FrameCounter | |
and #%00000011 ;branch to leave if not on | |
bne NoIncXM ;every fourth frame | |
ldy XMoveSecondaryCounter,x ;get secondary counter | |
lda XMovePrimaryCounter,x ;get primary counter | |
lsr | |
bcs DecSeXM ;if d0 of primary counter set, branch elsewhere | |
cpy $01 ;compare secondary counter to preset maximum value | |
beq IncPXM ;if equal, branch ahead of this part | |
inc XMoveSecondaryCounter,x ;increment secondary counter and leave | |
NoIncXM: rts | |
IncPXM: inc XMovePrimaryCounter,x ;increment primary counter and leave | |
rts | |
DecSeXM: tya ;put secondary counter in A | |
beq IncPXM ;if secondary counter at zero, branch back | |
dec XMoveSecondaryCounter,x ;otherwise decrement secondary counter and leave | |
rts | |
MoveWithXMCntrs: | |
lda XMoveSecondaryCounter,x ;save secondary counter to stack | |
pha | |
ldy #$01 ;set value here by default | |
lda XMovePrimaryCounter,x | |
and #%00000010 ;if d1 of primary counter is | |
bne XMRight ;set, branch ahead of this part here | |
lda XMoveSecondaryCounter,x | |
eor #$ff ;otherwise change secondary | |
clc ;counter to two's compliment | |
adc #$01 | |
sta XMoveSecondaryCounter,x | |
ldy #$02 ;load alternate value here | |
XMRight: sty Enemy_MovingDir,x ;store as moving direction | |
jsr MoveEnemyHorizontally | |
sta $00 ;save value obtained from sub here | |
pla ;get secondary counter from stack | |
sta XMoveSecondaryCounter,x ;and return to original place | |
rts | |
;-------------------------------- | |
BlooberBitmasks: | |
.db %00111111, %00000011 | |
MoveBloober: | |
lda Enemy_State,x | |
and #%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 | |
lda PseudoRandomBitReg+1,x ;get LSFR | |
and 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: ldy #$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: sty Enemy_MovingDir,x ;set moving direction of bloober, then continue on here | |
BlooberSwim: | |
jsr ProcSwimmingB ;execute sub to make bloober swim characteristically | |
lda Enemy_Y_Position,x ;get vertical coordinate | |
sec | |
sbc Enemy_Y_MoveForce,x ;subtract movement force | |
cmp #$20 ;check to see if position is above edge of status bar | |
bcc SwimX ;if so, don't do it | |
sta Enemy_Y_Position,x ;otherwise, set new vertical position, make bloober swim | |
SwimX: ldy Enemy_MovingDir,x ;check moving direction | |
dey | |
bne LeftSwim ;if moving to the left, branch to second part | |
lda Enemy_X_Position,x | |
clc ;add movement speed to horizontal coordinate | |
adc BlooperMoveSpeed,x | |
sta Enemy_X_Position,x ;store result as new horizontal coordinate | |
lda Enemy_PageLoc,x | |
adc #$00 ;add carry to page location | |
sta Enemy_PageLoc,x ;store as new page location and leave | |
rts | |
LeftSwim: | |
lda Enemy_X_Position,x | |
sec ;subtract movement speed from horizontal coordinate | |
sbc BlooperMoveSpeed,x | |
sta Enemy_X_Position,x ;store result as new horizontal coordinate | |
lda Enemy_PageLoc,x | |
sbc #$00 ;subtract borrow from page location | |
sta Enemy_PageLoc,x ;store as new page location and leave | |
rts | |
MoveDefeatedBloober: | |
jmp MoveEnemySlowVert ;jump to move defeated bloober downwards | |
ProcSwimmingB: | |
lda BlooperMoveCounter,x ;get enemy's movement counter | |
and #%00000010 ;check for d1 set | |
bne ChkForFloatdown ;branch if set | |
lda FrameCounter | |
and #%00000111 ;get 3 LSB of frame counter | |
pha ;and save it to the stack | |
lda 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 | |
bne BSwimE ;branch to leave, execute code only every eighth frame | |
lda Enemy_Y_MoveForce,x | |
clc ;add to movement force to speed up swim | |
adc #$01 | |
sta Enemy_Y_MoveForce,x ;set movement force | |
sta BlooperMoveSpeed,x ;set as movement speed | |
cmp #$02 | |
bne BSwimE ;if certain horizontal speed, branch to leave | |
inc BlooperMoveCounter,x ;otherwise increment movement counter | |
BSwimE: rts | |
SlowSwim: | |
pla ;pull 3 LSB of frame counter from the stack | |
bne NoSSw ;branch to leave, execute code only every eighth frame | |
lda Enemy_Y_MoveForce,x | |
sec ;subtract from movement force to slow swim | |
sbc #$01 | |
sta Enemy_Y_MoveForce,x ;set movement force | |
sta BlooperMoveSpeed,x ;set as movement speed | |
bne NoSSw ;if any speed, branch to leave | |
inc BlooperMoveCounter,x ;otherwise increment movement counter | |
lda #$02 | |
sta EnemyIntervalTimer,x ;set enemy's timer | |
NoSSw: rts ;leave | |
ChkForFloatdown: | |
lda EnemyIntervalTimer,x ;get enemy timer | |
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 | |
inc Enemy_Y_Position,x ;otherwise increment vertical coordinate | |
NoFD: rts ;leave | |
ChkNearPlayer: | |
lda Enemy_Y_Position,x ;get vertical coordinate | |
adc #$10 ;add sixteen pixels | |
cmp Player_Y_Position ;compare result with player's vertical coordinate | |
bcc Floatdown ;if modified vertical less than player's, branch | |
lda #$00 | |
sta BlooperMoveCounter,x ;otherwise nullify movement counter | |
rts | |
;-------------------------------- | |
MoveBulletBill: | |
lda Enemy_State,x ;check bullet bill's enemy object state for d5 set | |
and #%00100000 | |
beq NotDefB ;if not set, continue with movement code | |
jmp MoveJ_EnemyVertically ;otherwise jump to move defeated bullet bill downwards | |
NotDefB: lda #$e8 ;set bullet bill's horizontal speed | |
sta 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: | |
lda Enemy_State,x ;check cheep-cheep's enemy object state | |
and #%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 $03 ;save enemy state in $03 | |
lda Enemy_ID,x ;get enemy identifier | |
sec | |
sbc #$0a ;subtract ten for cheep-cheep identifiers | |
tay ;use as offset | |
lda SwimCCXMoveData,y ;load value here | |
sta $02 | |
lda Enemy_X_MoveForce,x ;load horizontal force | |
sec | |
sbc $02 ;subtract preset value from horizontal force | |
sta Enemy_X_MoveForce,x ;store as new horizontal force | |
lda Enemy_X_Position,x ;get horizontal coordinate | |
sbc #$00 ;subtract borrow (thus moving it slowly) | |
sta Enemy_X_Position,x ;and save as new horizontal coordinate | |
lda Enemy_PageLoc,x | |
sbc #$00 ;subtract borrow again, this time from the | |
sta Enemy_PageLoc,x ;page location, then save | |
lda #$20 | |
sta $02 ;save new value here | |
cpx #$02 ;check enemy object offset | |
bcc ExSwCC ;if in first or second slot, branch to leave | |
lda CheepCheepMoveMFlag,x ;check movement flag | |
cmp #$10 ;if movement speed set to $00, | |
bcc CCSwimUpwards ;branch to move upwards | |
lda Enemy_YMF_Dummy,x | |
clc | |
adc $02 ;add preset value to dummy variable to get carry | |
sta Enemy_YMF_Dummy,x ;and save dummy | |
lda Enemy_Y_Position,x ;get vertical coordinate | |
adc $03 ;add carry to it plus enemy state to slowly move it downwards | |
sta Enemy_Y_Position,x ;save as new vertical coordinate | |
lda Enemy_Y_HighPos,x | |
adc #$00 ;add carry to page location and | |
jmp ChkSwimYPos ;jump to end of movement code | |
CCSwimUpwards: | |
lda Enemy_YMF_Dummy,x | |
sec | |
sbc $02 ;subtract preset value to dummy variable to get borrow | |
sta Enemy_YMF_Dummy,x ;and save dummy | |
lda Enemy_Y_Position,x ;get vertical coordinate | |
sbc $03 ;subtract borrow to it plus enemy state to slowly move it upwards | |
sta Enemy_Y_Position,x ;save as new vertical coordinate | |
lda Enemy_Y_HighPos,x | |
sbc #$00 ;subtract borrow from page location | |
ChkSwimYPos: | |
sta Enemy_Y_HighPos,x ;save new page location here | |
ldy #$00 ;load movement speed to upwards by default | |
lda Enemy_Y_Position,x ;get vertical coordinate | |
sec | |
sbc CheepCheepOrigYPos,x ;subtract original coordinate from current | |
bpl YPDiff ;if result positive, skip to next part | |
ldy #$10 ;otherwise load movement speed to downwards | |
eor #$ff | |
clc ;get two's compliment of result | |
adc #$01 ;to obtain total difference of original vs. current | |
YPDiff: cmp #$0f ;if difference between original vs. current vertical | |
bcc ExSwCC ;coordinates < 15 pixels, leave movement speed alone | |
tya | |
sta 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 | |
and #%00001000 ;if so, branch to leave | |
bne SkipFBar | |
lda TimerControl ;if master timer control set, branch | |
bne SusFbar ;ahead of this part | |
lda FirebarSpinSpeed,x ;load spinning speed of firebar | |
jsr FirebarSpin ;modify current spinstate | |
and #%00011111 ;mask out all but 5 LSB | |
sta FirebarSpinState_High,x ;and store as new high byte of spinstate | |
SusFbar: lda FirebarSpinState_High,x ;get high byte of spinstate | |
ldy Enemy_ID,x ;check enemy identifier | |
cpy #$1f | |
bcc SetupGFB ;if < $1f (long firebar), branch | |
cmp #$08 ;check high byte of spinstate | |
beq SkpFSte ;if eight, branch to change | |
cmp #$18 | |
bne SetupGFB ;if not at twenty-four branch to not change | |
SkpFSte: clc | |
adc #$01 ;add one to spinning thing to avoid horizontal state | |
sta FirebarSpinState_High,x | |
SetupGFB: sta $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) | |
ldy Enemy_SprDataOffset,x ;get OAM data offset | |
lda Enemy_Rel_YPos ;get relative vertical coordinate | |
sta Sprite_Y_Position,y ;store as Y in OAM data | |
sta $07 ;also save here | |
lda Enemy_Rel_XPos ;get relative horizontal coordinate | |
sta Sprite_X_Position,y ;store as X in OAM data | |
sta $06 ;also save here | |
lda #$01 | |
sta $00 ;set $01 value here (not necessary) | |
jsr FirebarCollision ;draw fireball part and do collision detection | |
ldy #$05 ;load value for short firebars by default | |
lda Enemy_ID,x | |
cmp #$1f ;are we doing a long firebar? | |
bcc SetMFbar ;no, branch then | |
ldy #$0b ;otherwise load value for long firebars | |
SetMFbar: sty $ed ;store maximum value for length of firebars | |
lda #$00 | |
sta $00 ;initialize counter here | |
DrawFbar: lda $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 $00 ;check which firebar part | |
cmp #$04 | |
bne NextFbar | |
ldy DuplicateObj_Offset ;if we arrive at fifth firebar part, | |
lda Enemy_SprDataOffset,y ;get offset from long firebar and load OAM data offset | |
sta $06 ;using long firebar offset, then store as new one here | |
NextFbar: inc $00 ;move onto the next firebar part | |
lda $00 | |
cmp $ed ;if we end up at the maximum part, go on and leave | |
bcc DrawFbar ;otherwise go back and do another | |
SkipFBar: rts | |
DrawFirebar_Collision: | |
lda $03 ;store mirror data elsewhere | |
sta $05 | |
ldy $06 ;load OAM data offset for firebar | |
lda $01 ;load horizontal adder we got from position loader | |
lsr $05 ;shift LSB of mirror data | |
bcs AddHA ;if carry was set, skip this part | |
eor #$ff | |
adc #$01 ;otherwise get two's compliment of horizontal adder | |
AddHA: clc ;add horizontal coordinate relative to screen to | |
adc Enemy_Rel_XPos ;horizontal adder, modified or otherwise | |
sta Sprite_X_Position,y ;store as X coordinate here | |
sta $06 ;store here for now, note offset is saved in Y still | |
cmp Enemy_Rel_XPos ;compare X coordinate of sprite to original X of firebar | |
bcs SubtR1 ;if sprite coordinate => original coordinate, branch | |
lda Enemy_Rel_XPos | |
sec ;otherwise subtract sprite X from the | |
sbc $06 ;original one and skip this part | |
jmp ChkFOfs | |
SubtR1: sec ;subtract original X from the | |
sbc Enemy_Rel_XPos ;current sprite X | |
ChkFOfs: cmp #$59 ;if difference of coordinates within a certain range, | |
bcc VAHandl ;continue by handling vertical adder | |
lda #$f8 ;otherwise, load offscreen Y coordinate | |
bne SetVFbr ;and unconditionally branch to move sprite offscreen | |
VAHandl: lda Enemy_Rel_YPos ;if vertical relative coordinate offscreen, | |
cmp #$f8 ;skip ahead of this part and write into sprite Y coordinate | |
beq SetVFbr | |
lda $02 ;load vertical adder we got from position loader | |
lsr $05 ;shift LSB of mirror data one more time | |
bcs AddVA ;if carry was set, skip this part | |
eor #$ff | |
adc #$01 ;otherwise get two's compliment of second part | |
AddVA: clc ;add vertical coordinate relative to screen to | |
adc Enemy_Rel_YPos ;the second data, modified or otherwise | |
SetVFbr: sta Sprite_Y_Position,y ;store as Y coordinate here | |
sta $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 | |
ora TimerControl ;or master timer controls set | |
bne NoColFB ;then skip all of this | |
sta $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 | |
bne AdjSm ;if player small, branch to alter variables | |
lda CrouchingFlag | |
beq BigJp ;if player big and not crouching, jump ahead | |
AdjSm: inc $05 ;if small or big but crouching, execute this part | |
inc $05 ;first increment our counter twice (setting $02 as flag) | |
tya | |
clc ;then add 24 pixels to the player's | |
adc #$18 ;vertical coordinate | |
tay | |
BigJp: tya ;get vertical coordinate, altered or otherwise, from Y | |
FBCLoop: sec ;subtract vertical position of firebar | |
sbc $07 ;from the vertical coordinate of the player | |
bpl ChkVFBD ;if player lower on the screen than firebar, | |
eor #$ff ;skip two's compliment part | |
clc ;otherwise get two's compliment | |
adc #$01 | |
ChkVFBD: cmp #$08 ;if difference => 8 pixels, skip ahead of this part | |
bcs Chk2Ofs | |
lda $06 ;if firebar on far right on the screen, skip this, | |
cmp #$f0 ;because, really, what's the point? | |
bcs Chk2Ofs | |
lda Sprite_X_Position+4 ;get OAM X coordinate for sprite #1 | |
clc | |
adc #$04 ;add four pixels | |
sta $04 ;store here | |
sec ;subtract horizontal coordinate of firebar | |
sbc $06 ;from the X coordinate of player's sprite 1 | |
bpl ChkFBCl ;if modded X coordinate to the right of firebar | |
eor #$ff ;skip two's compliment part | |
clc ;otherwise get two's compliment | |
adc #$01 | |
ChkFBCl: cmp #$08 ;if difference < 8 pixels, collision, thus branch | |
bcc ChgSDir ;to process | |
Chk2Ofs: lda $05 ;if value of $02 was set earlier for whatever reason, | |
cmp #$02 ;branch to increment OAM offset and leave, no collision | |
beq NoColFB | |
ldy $05 ;otherwise get temp here and use as offset | |
lda Player_Y_Position | |
clc | |
adc FirebarYPos,y ;add value loaded with offset to player's vertical coordinate | |
inc $05 ;then increment temp and jump back | |
jmp FBCLoop | |
ChgSDir: ldx #$01 ;set movement direction by default | |
lda $04 ;if OAM X coordinate of player's sprite 1 | |
cmp $06 ;is greater than horizontal coordinate of firebar | |
bcs SetSDir ;then do not alter movement direction | |
inx ;otherwise increment it | |
SetSDir: stx Enemy_MovingDir ;store movement direction here | |
ldx #$00 | |
lda $00 ;save value written to $00 to stack | |
pha | |
jsr InjurePlayer ;perform sub to hurt or kill player | |
pla | |
sta $00 ;get value of $00 from stack | |
NoColFB: pla ;get OAM data offset | |
clc ;add four to it and save | |
adc #$04 | |
sta $06 | |
ldx ObjectOffset ;get enemy object buffer offset and leave | |
rts | |
GetFirebarPosition: | |
pha ;save high byte of spinstate to the stack | |
and #%00001111 ;mask out low nybble | |
cmp #$09 | |
bcc GetHAdder ;if lower than $09, branch ahead | |
eor #%00001111 ;otherwise get two's compliment to oscillate | |
clc | |
adc #$01 | |
GetHAdder: sta $01 ;store result, modified or not, here | |
ldy $00 ;load number of firebar ball where we're at | |
lda FirebarTblOffsets,y ;load offset to firebar position data | |
clc | |
adc $01 ;add oscillated high byte of spinstate | |
tay ;to offset here and use as new offset | |
lda FirebarPosLookupTbl,y ;get data here and store as horizontal adder | |
sta $01 | |
pla ;pull whatever was in A from the stack | |
pha ;save it again because we still need it | |
clc | |
adc #$08 ;add eight this time, to get vertical adder | |
and #%00001111 ;mask out high nybble | |
cmp #$09 ;if lower than $09, branch ahead | |
bcc GetVAdder | |
eor #%00001111 ;otherwise get two's compliment | |
clc | |
adc #$01 | |
GetVAdder: sta $02 ;store result here | |
ldy $00 | |
lda FirebarTblOffsets,y ;load offset to firebar position data again | |
clc | |
adc $02 ;this time add value in $02 to offset here and use as offset | |
tay | |
lda FirebarPosLookupTbl,y ;get data here and store as vertica adder | |
sta $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 | |
lda FirebarMirrorData,y ;load mirroring data here | |
sta $03 ;store | |
rts | |
;-------------------------------- | |
PRandomSubtracter: | |
.db $f8, $a0, $70, $bd, $00 | |
FlyCCBPriority: | |
.db $20, $20, $20, $00, $00 | |
MoveFlyingCheepCheep: | |
lda Enemy_State,x ;check cheep-cheep's enemy state | |
and #%00100000 ;for d5 set | |
beq FlyCC ;branch to continue code if not set | |
lda #$00 | |
sta 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 | |
ldy #$0d ;set vertical movement amount | |
lda #$05 ;set maximum speed | |
jsr SetXMoveAmt ;branch to impose gravity on flying cheep-cheep | |
lda 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) | |
lda Enemy_Y_Position,x ;get vertical position | |
sec ;subtract pseudorandom value based on offset from position | |
sbc PRandomSubtracter,y | |
bpl AddCCF ;if result within top half of screen, skip this part | |
eor #$ff | |
clc ;otherwise get two's compliment | |
adc #$01 | |
AddCCF: cmp #$08 ;if result or two's compliment greater than eight, | |
bcs BPGet ;skip to the end without changing movement force | |
lda Enemy_Y_MoveForce,x | |
clc | |
adc #$10 ;otherwise add to it | |
sta Enemy_Y_MoveForce,x | |
lsr ;move high nybble to low again | |
lsr | |
lsr | |
lsr | |
tay | |
BPGet: lda FlyCCBPriority,y ;load bg priority data and store (this is very likely | |
sta 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: | |
lda Enemy_State,x ;check lakitu's enemy state | |
and #%00100000 ;for d5 set | |
beq ChkLS ;if not set, continue with code | |
jmp MoveD_EnemyVertically ;otherwise jump to move defeated lakitu downwards | |
ChkLS: lda Enemy_State,x ;if lakitu's enemy state not set at all, | |
beq Fr12S ;go ahead and continue with code | |
lda #$00 | |
sta LakituMoveDirection,x ;otherwise initialize moving direction to move to left | |
sta EnemyFrenzyBuffer ;initialize frenzy buffer | |
lda #$10 | |
bne SetLSpd ;load horizontal speed and do unconditional branch | |
Fr12S: lda #Spiny | |
sta EnemyFrenzyBuffer ;set spiny identifier in frenzy buffer | |
ldy #$02 | |
LdLDa: lda LakituDiffAdj,y ;load values | |
sta $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: sta LakituMoveSpeed,x ;set movement speed returned from sub | |
ldy #$01 ;set moving direction to right by default | |
lda LakituMoveDirection,x | |
and #$01 ;get LSB of moving direction | |
bne SetLMov ;if set, branch to the end to use moving direction | |
lda LakituMoveSpeed,x | |
eor #$ff ;get two's compliment of moving speed | |
clc | |
adc #$01 | |
sta LakituMoveSpeed,x ;store as new moving speed | |
iny ;increment moving direction to left | |
SetLMov: sty Enemy_MovingDir,x ;store moving direction | |
jmp MoveEnemyHorizontally ;move lakitu horizontally | |
PlayerLakituDiff: | |
ldy #$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 $00 | |
eor #$ff ;get two's compliment of low byte of horizontal difference | |
clc | |
adc #$01 ;store two's compliment as horizontal difference | |
sta $00 | |
ChkLakDif: lda $00 ;get low byte of horizontal difference | |
cmp #$3c ;if within a certain distance of player, branch | |
bcc ChkPSpeed | |
lda #$3c ;otherwise set maximum distance | |
sta $00 | |
lda Enemy_ID,x ;check if lakitu is in our current enemy slot | |
cmp #Lakitu | |
bne ChkPSpeed ;if not, branch elsewhere | |
tya ;compare contents of Y, now in A | |
cmp LakituMoveDirection,x ;to what is being used as horizontal movement direction | |
beq ChkPSpeed ;if moving toward the player, branch, do not alter | |
lda LakituMoveDirection,x ;if moving to the left beyond maximum distance, | |
beq SetLMovD ;branch and alter without delay | |
dec LakituMoveSpeed,x ;decrement horizontal speed | |
lda LakituMoveSpeed,x ;if horizontal speed not yet at zero, branch to leave | |
bne ExMoveLak | |
SetLMovD: tya ;set horizontal direction depending on horizontal | |
sta LakituMoveDirection,x ;difference between enemy and player if necessary | |
ChkPSpeed: lda $00 | |
and #%00111100 ;mask out all but four bits in the middle | |
lsr ;divide masked difference by four | |
lsr | |
sta $00 ;store as new value | |
ldy #$00 ;init offset | |
lda Player_X_Speed | |
beq SubDifAdj ;if player not moving horizontally, branch | |
lda ScrollAmount | |
beq SubDifAdj ;if scroll speed not set, branch to same place | |
iny ;otherwise increment offset | |
lda Player_X_Speed | |
cmp #$19 ;if player not running, branch | |
bcc ChkSpinyO | |
lda ScrollAmount | |
cmp #$02 ;if scroll speed below a certain amount, branch | |
bcc ChkSpinyO ;to same place | |
iny ;otherwise increment once more | |
ChkSpinyO: lda Enemy_ID,x ;check for spiny object | |
cmp #Spiny | |
bne ChkEmySpd ;branch if not found | |
lda Player_X_Speed ;if player not moving, skip this part | |
bne SubDifAdj | |
ChkEmySpd: lda Enemy_Y_Speed,x ;check vertical speed | |
bne SubDifAdj ;branch if nonzero | |
ldy #$00 ;otherwise reinit offset | |
SubDifAdj: lda $0001,y ;get one of three saved values from earlier | |
ldy $00 ;get saved horizontal difference | |
SPixelLak: sec ;subtract one for each pixel of horizontal difference | |
sbc #$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 | |
lda Enemy_ID,x ;check enemy object identifier for bowser | |
cmp #Bowser ;if not found, branch ahead, | |
bne SetM2 ;metatile removal not necessary | |
stx ObjectOffset ;store as enemy offset here | |
lda Enemy_State,x ;if bowser in normal state, skip all of this | |
beq RemoveBridge | |
and #%01000000 ;if bowser's state has d6 clear, skip to silence music | |
beq SetM2 | |
lda Enemy_Y_Position,x ;check bowser's vertical coordinate | |
cmp #$e0 ;if bowser not yet low enough, skip this part ahead | |
bcc MoveD_Bowser | |
SetM2: lda #Silence ;silence music | |
sta EventMusicQueue | |
inc 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: | |
dec BowserFeetCounter ;decrement timer to control bowser's feet | |
bne NoBFall ;if not expired, skip all of this | |
lda #$04 | |
sta BowserFeetCounter ;otherwise, set timer now | |
lda BowserBodyControls | |
eor #$01 ;invert bit to control bowser's feet | |
sta BowserBodyControls | |
lda #$22 ;put high byte of name table address here for now | |
sta $05 | |
ldy BridgeCollapseOffset ;get bridge collapse offset here | |
lda BridgeCollapseData,y ;load low byte of name table address and store here | |
sta $04 | |
ldy VRAM_Buffer1_Offset ;increment vram buffer offset | |
iny | |
ldx #$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 | |
lda #Sfx_Blast ;load the fireworks/gunfire sound into the square 2 sfx | |
sta Square2SoundQueue ;queue while at the same time loading the brick | |
lda #Sfx_BrickShatter ;shatter sound into the noise sfx queue thus | |
sta NoiseSoundQueue ;producing the unique sound of the bridge collapsing | |
inc BridgeCollapseOffset ;increment bridge collapse offset | |
lda BridgeCollapseOffset | |
cmp #$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 | |
lda #%01000000 | |
sta Enemy_State,x ;set bowser's state to one of defeated states (d6 set) | |
lda #Sfx_BowserFall | |
sta Square2SoundQueue ;play bowser defeat sound | |
NoBFall: jmp BowserGfxHandler ;jump to code that draws bowser | |
;-------------------------------- | |
PRandomRange: | |
.db $21, $41, $11, $31 | |
RunBowser: | |
lda Enemy_State,x ;if d5 in enemy state is not set | |
and #%00100000 ;then branch elsewhere to run bowser | |
beq BowserControl | |
lda Enemy_Y_Position,x ;otherwise check vertical position | |
cmp #$e0 ;if above a certain point, branch to move defeated bowser | |
bcc MoveD_Bowser ;otherwise proceed to KillAllEnemies | |
KillAllEnemies: | |
ldx #$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: | |
lda #$00 | |
sta EnemyFrenzyBuffer ;empty frenzy buffer | |
lda TimerControl ;if master timer control not set, | |
beq ChkMouth ;skip jump and execute code here | |
jmp SkipToFB ;otherwise, jump over a bunch of code | |
ChkMouth: lda BowserBodyControls ;check bowser's mouth | |
bpl FeetTmr ;if bit clear, go ahead with code here | |
jmp HammerChk ;otherwise skip a whole section starting here | |
FeetTmr: dec BowserFeetCounter ;decrement timer to control bowser's feet | |
bne ResetMDr ;if not expired, skip this part | |
lda #$20 ;otherwise, reset timer | |
sta BowserFeetCounter | |
lda BowserBodyControls ;and invert bit used | |
eor #%00000001 ;to control bowser's feet | |
sta BowserBodyControls | |
ResetMDr: lda FrameCounter ;check frame counter | |
and #%00001111 ;if not on every sixteenth frame, skip | |
bne B_FaceP ;ahead to continue code | |
lda #$02 ;otherwise reset moving/facing direction every | |
sta Enemy_MovingDir,x ;sixteen frames | |
B_FaceP: lda EnemyFrameTimer,x ;if timer set here expired, | |
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 | |
lda #$01 | |
sta Enemy_MovingDir,x ;set bowser to move and face to the right | |
lda #$02 | |
sta BowserMovementSpeed ;set movement speed | |
lda #$20 | |
sta EnemyFrameTimer,x ;set timer here | |
sta BowserFireBreathTimer ;set timer used for bowser's flame | |
lda Enemy_X_Position,x | |
cmp #$c8 ;if bowser to the right past a certain point, | |
bcs HammerChk ;skip ahead to some other section | |
GetPRCmp: lda FrameCounter ;get frame counter | |
and #%00000011 | |
bne HammerChk ;execute this code every fourth frame, otherwise branch | |
lda Enemy_X_Position,x | |
cmp BowserOrigXPos ;if bowser not at original horizontal position, | |
bne GetDToO ;branch to skip this part | |
lda PseudoRandomBitReg,x | |
and #%00000011 ;get pseudorandom offset | |
tay | |
lda PRandomRange,y ;load value using pseudorandom offset | |
sta MaxRangeFromOrigin ;and store here | |
GetDToO: lda Enemy_X_Position,x | |
clc ;add movement speed to bowser's horizontal | |
adc BowserMovementSpeed ;coordinate and save as new horizontal position | |
sta Enemy_X_Position,x | |
ldy Enemy_MovingDir,x | |
cpy #$01 ;if bowser moving and facing to the right, skip ahead | |
beq HammerChk | |
ldy #$ff ;set default movement speed here (move left) | |
sec ;get difference of current vs. original | |
sbc BowserOrigXPos ;horizontal position | |
bpl CompDToO ;if current position to the right of original, skip ahead | |
eor #$ff | |
clc ;get two's compliment | |
adc #$01 | |
ldy #$01 ;set alternate movement speed here (move right) | |
CompDToO: cmp MaxRangeFromOrigin ;compare difference with pseudorandom value | |
bcc HammerChk ;if difference < pseudorandom value, leave speed alone | |
sty BowserMovementSpeed ;otherwise change bowser's movement speed | |
HammerChk: lda EnemyFrameTimer,x ;if timer set here not expired yet, skip ahead to | |
bne MakeBJump ;some other section of code | |
jsr MoveEnemySlowVert ;otherwise start by moving bowser downwards | |
lda WorldNumber ;check world number | |
cmp #World6 | |
bcc SetHmrTmr ;if world 1-5, skip this part (not time to throw hammers yet) | |
lda FrameCounter | |
and #%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: lda Enemy_Y_Position,x ;get current vertical position | |
cmp #$80 ;if still above a certain point | |
bcc ChkFireB ;then skip to world number check for flames | |
lda PseudoRandomBitReg,x | |
and #%00000011 ;get pseudorandom offset | |
tay | |
lda PRandomRange,y ;get value using pseudorandom offset | |
sta EnemyFrameTimer,x ;set for timer here | |
SkipToFB: jmp ChkFireB ;jump to execute flames code | |
MakeBJump: cmp #$01 ;if timer not yet about to expire, | |
bne ChkFireB ;skip ahead to next part | |
dec Enemy_Y_Position,x ;otherwise decrement vertical coordinate | |
jsr InitVStf ;initialize movement amount | |
lda #$fe | |
sta Enemy_Y_Speed,x ;set vertical speed to move bowser upwards | |
ChkFireB: lda WorldNumber ;check world number here | |
cmp #World8 ;world 8? | |
beq SpawnFBr ;if so, execute this part here | |
cmp #World6 ;world 6-7? | |
bcs BowserGfxHandler ;if so, skip this part here | |
SpawnFBr: lda BowserFireBreathTimer ;check timer here | |
bne BowserGfxHandler ;if not expired yet, skip all of this | |
lda #$20 | |
sta BowserFireBreathTimer ;set timer here | |
lda BowserBodyControls | |
eor #%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 | |
beq SetFBTmr ;if secondary hard mode flag not set, skip this | |
sec | |
sbc #$10 ;otherwise subtract from value in A | |
SetFBTmr: sta BowserFireBreathTimer ;set value as timer here | |
lda #BowserFlame ;put bowser's flame identifier | |
sta EnemyFrenzyBuffer ;in enemy frenzy buffer | |
;-------------------------------- | |
BowserGfxHandler: | |
jsr ProcessBowserHalf ;do a sub here to process bowser's front | |
ldy #$10 ;load default value here to position bowser's rear | |
lda Enemy_MovingDir,x ;check moving direction | |
lsr | |
bcc CopyFToR ;if moving left, use default | |
ldy #$f0 ;otherwise load alternate positioning value here | |
CopyFToR: tya ;move bowser's rear object position value to A | |
clc | |
adc Enemy_X_Position,x ;add to bowser's front object horizontal coordinate | |
ldy DuplicateObj_Offset ;get bowser's rear object offset | |
sta Enemy_X_Position,y ;store A as bowser's rear horizontal coordinate | |
lda Enemy_Y_Position,x | |
clc ;add eight pixels to bowser's front object | |
adc #$08 ;vertical coordinate and store as vertical coordinate | |
sta Enemy_Y_Position,y ;for bowser's rear | |
lda Enemy_State,x | |
sta Enemy_State,y ;copy enemy state directly from front to rear | |
lda Enemy_MovingDir,x | |
sta 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 | |
lda #Bowser ;set bowser's enemy identifier | |
sta 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 | |
lda #$00 ;nullify bowser's front/rear graphics flag | |
sta BowserGfxFlag | |
ExBGfxH: rts ;leave! | |
ProcessBowserHalf: | |
inc BowserGfxFlag ;increment bowser's graphics flag, then run subroutines | |
jsr RunRetainerObj ;to get offscreen bits, relative position and draw bowser (finally!) | |
lda Enemy_State,x | |
bne ExBGfxH ;if either enemy object not in normal state, branch to leave | |
lda #$0a | |
sta 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 | |
inc BowserFlameTimerCtrl ;increment | |
lda BowserFlameTimerCtrl ;mask out all but 3 LSB | |
and #%00000111 ;to keep in range of 0-7 | |
sta BowserFlameTimerCtrl | |
lda FlameTimerData,y ;load value to be used then leave | |
ExFl: rts | |
ProcBowserFlame: | |
lda TimerControl ;if master timer control flag set, | |
bne SetGfxF ;skip all of this | |
lda #$40 ;load default movement force | |
ldy SecondaryHardMode | |
beq SFlmX ;if secondary hard mode flag not set, use default | |
lda #$60 ;otherwise load alternate movement force to go faster | |
SFlmX: sta $00 ;store value here | |
lda Enemy_X_MoveForce,x | |
sec ;subtract value from movement force | |
sbc $00 | |
sta Enemy_X_MoveForce,x ;save new value | |
lda Enemy_X_Position,x | |
sbc #$01 ;subtract one from horizontal position to move | |
sta Enemy_X_Position,x ;to the left | |
lda Enemy_PageLoc,x | |
sbc #$00 ;subtract borrow from page location | |
sta Enemy_PageLoc,x | |
ldy BowserFlamePRandomOfs,x ;get some value here and use as offset | |
lda Enemy_Y_Position,x ;load vertical coordinate | |
cmp FlameYPosData,y ;compare against coordinate data using $0417,x as offset | |
beq SetGfxF ;if equal, branch and do not modify coordinate | |
clc | |
adc Enemy_Y_MoveForce,x ;otherwise add value here to coordinate and store | |
sta Enemy_Y_Position,x ;as new vertical coordinate | |
SetGfxF: jsr RelativeEnemyPosition ;get new relative coordinates | |
lda Enemy_State,x ;if bowser's flame not in normal state, | |
bne ExFl ;branch to leave | |
lda #$51 ;otherwise, continue | |
sta $00 ;write first tile number | |
ldy #$02 ;load attributes without vertical flip by default | |
lda FrameCounter | |
and #%00000010 ;invert vertical flip bit every 2 frames | |
beq FlmeAt ;if d1 not set, write default value | |
ldy #$82 ;otherwise write value with vertical flip bit set | |
FlmeAt: sty $01 ;set bowser's flame sprite attributes here | |
ldy Enemy_SprDataOffset,x ;get OAM data offset | |
ldx #$00 | |
DrawFlameLoop: | |
lda Enemy_Rel_YPos ;get Y relative coordinate of current enemy object | |
sta Sprite_Y_Position,y ;write into Y coordinate of OAM data | |
lda $00 | |
sta Sprite_Tilenumber,y ;write current tile number into OAM data | |
inc $00 ;increment tile number to draw more bowser's flame | |
lda $01 | |
sta Sprite_Attributes,y ;write saved attributes into OAM data | |
lda Enemy_Rel_XPos | |
sta Sprite_X_Position,y ;write X relative coordinate of current enemy object | |
clc | |
adc #$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 | |
cpx #$03 ;have not yet been done | |
bcc DrawFlameLoop | |
ldx ObjectOffset ;reload original enemy offset | |
jsr GetEnemyOffscreenBits ;get offscreen information | |
ldy 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 | |
lda #$f8 ;otherwise move sprite offscreen, this part likely | |
sta 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 | |
lda #$f8 ;otherwise move third sprite offscreen | |
sta 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 | |
lda #$f8 ;otherwise move second sprite offscreen | |
sta 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 | |
lda #$f8 | |
sta Sprite_Y_Position,y ;otherwise move first sprite offscreen | |
ExFlmeD: rts ;leave | |
;-------------------------------- | |
RunFireworks: | |
dec ExplosionTimerCounter,x ;decrement explosion timing counter here | |
bne SetupExpl ;if not expired, skip this part | |
lda #$08 | |
sta ExplosionTimerCounter,x ;reset counter | |
inc ExplosionGfxCounter,x ;increment explosion graphics counter | |
lda ExplosionGfxCounter,x | |
cmp #$03 ;check explosion graphics counter | |
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 | |
ldy Enemy_SprDataOffset,x ;get OAM data offset | |
lda ExplosionGfxCounter,x ;get explosion graphics counter | |
jsr DrawExplosion_Fireworks ;do a sub to draw the explosion then leave | |
rts | |
FireworksSoundScore: | |
lda #$00 ;disable enemy buffer flag | |
sta Enemy_Flag,x | |
lda #Sfx_Blast ;play fireworks/gunfire sound | |
sta Square2SoundQueue | |
lda #$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: | |
lda #$00 ;initialize enemy frenzy buffer | |
sta EnemyFrenzyBuffer | |
lda StarFlagTaskControl ;check star flag object task number here | |
cmp #$05 ;if greater than 5, branch to exit | |
bcs StarFlagExit | |
jsr JumpEngine ;otherwise jump to appropriate sub | |
.dw StarFlagExit | |
.dw GameTimerFireworks | |
.dw AwardGameTimerPoints | |
.dw RaiseFlagSetoffFWorks | |
.dw DelayToAreaEnd | |
GameTimerFireworks: | |
ldy #$05 ;set default state for star flag object | |
lda GameTimerDisplay+2 ;get game timer's last digit | |
cmp #$01 | |
beq SetFWC ;if last digit of game timer set to 1, skip ahead | |
ldy #$03 ;otherwise load new value for state | |
cmp #$03 | |
beq SetFWC ;if last digit of game timer set to 3, skip ahead | |
ldy #$00 ;otherwise load one more potential value for state | |
cmp #$06 | |
beq SetFWC ;if last digit of game timer set to 6, skip ahead | |
lda #$ff ;otherwise set value for no fireworks | |
SetFWC: sta FireworksCounter ;set fireworks counter here | |
sty Enemy_State,x ;set whatever state we have in star flag object | |
IncrementSFTask1: | |
inc StarFlagTaskControl ;increment star flag object task number | |
StarFlagExit: | |
rts ;leave | |
AwardGameTimerPoints: | |
lda GameTimerDisplay ;check all game timer digits for any intervals left | |
ora GameTimerDisplay+1 | |
ora GameTimerDisplay+2 | |
beq IncrementSFTask1 ;if no time left on game timer at all, branch to next task | |
lda FrameCounter | |
and #%00000100 ;check frame counter for d2 set (skip ahead | |
beq NoTTick ;for four frames every four frames) branch if not set | |
lda #Sfx_TimerTick | |
sta Square2SoundQueue ;load timer tick sound | |
NoTTick: ldy #$23 ;set offset here to subtract from game timer's last digit | |
lda #$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 | |
lda #$05 ;set now to add 50 points | |
sta DigitModifier+5 ;per game timer interval subtracted | |
EndAreaPoints: | |
ldy #$0b ;load offset for mario's score by default | |
lda CurrentPlayer ;check player on the screen | |
beq ELPGive ;if mario, do not change | |
ldy #$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 | |
ora #%00000100 ;add four to set nybble for game timer | |
jmp UpdateNumber ;jump to print the new score and game timer | |
RaiseFlagSetoffFWorks: | |
lda Enemy_Y_Position,x ;check star flag's vertical position | |
cmp #$72 ;against preset value | |
bcc SetoffF ;if star flag higher vertically, branch to other code | |
dec Enemy_Y_Position,x ;otherwise, raise star flag by one pixel | |
jmp DrawStarFlag ;and skip this part here | |
SetoffF: lda FireworksCounter ;check fireworks counter | |
beq DrawFlagSetTimer ;if no fireworks left to go off, skip this part | |
bmi DrawFlagSetTimer ;if no fireworks set to go off, skip this part | |
lda #Fireworks | |
sta EnemyFrenzyBuffer ;otherwise set fireworks object in frenzy queue | |
DrawStarFlag: | |
jsr RelativeEnemyPosition ;get relative coordinates of star flag | |
ldy Enemy_SprDataOffset,x ;get OAM data offset | |
ldx #$03 ;do four sprites | |
DSFLoop: lda Enemy_Rel_YPos ;get relative vertical coordinate | |
clc | |
adc StarFlagYPosAdder,x ;add Y coordinate adder data | |
sta Sprite_Y_Position,y ;store as Y coordinate | |
lda StarFlagTileData,x ;get tile number | |
sta Sprite_Tilenumber,y ;store as tile number | |
lda #$22 ;set palette and background priority bits | |
sta Sprite_Attributes,y ;store as attributes | |
lda Enemy_Rel_XPos ;get relative horizontal coordinate | |
clc | |
adc StarFlagXPosAdder,x ;add X coordinate adder data | |
sta 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 | |
lda #$06 | |
sta EnemyIntervalTimer,x ;set interval timer here | |
IncrementSFTask2: | |
inc StarFlagTaskControl ;move onto next task | |
rts | |
DelayToAreaEnd: | |
jsr DrawStarFlag ;do sub to draw star flag | |
lda EnemyIntervalTimer,x ;if interval timer set in previous task | |
bne StarFlagExit2 ;not yet expired, branch to leave | |
lda EventMusicBuffer ;if event music buffer empty, | |
beq IncrementSFTask2 ;branch to increment task | |
StarFlagExit2: | |
rts ;otherwise leave | |
;-------------------------------- | |
;$00 - used to store horizontal difference between player and piranha plant | |
MovePiranhaPlant: | |
lda Enemy_State,x ;check enemy state | |
bne PutinPipe ;if set at all, branch to leave | |
lda EnemyFrameTimer,x ;check enemy's timer here | |
bne PutinPipe ;branch to end if not yet expired | |
lda PiranhaPlant_MoveFlag,x ;check movement flag | |
bne SetupToMovePPlant ;if moving, skip to part ahead | |
lda PiranhaPlant_Y_Speed,x ;if currently rising, branch | |
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 $00 ;otherwise get saved horizontal difference | |
eor #$ff | |
clc ;and change to two's compliment | |
adc #$01 | |
sta $00 ;save as new horizontal difference | |
ChkPlayerNearPipe: | |
lda $00 ;get saved horizontal difference | |
cmp #$21 | |
bcc PutinPipe ;if player within a certain distance, branch to leave | |
ReversePlantSpeed: | |
lda PiranhaPlant_Y_Speed,x ;get vertical speed | |
eor #$ff | |
clc ;change to two's compliment | |
adc #$01 | |
sta PiranhaPlant_Y_Speed,x ;save as new vertical speed | |
inc PiranhaPlant_MoveFlag,x ;increment to set movement flag | |
SetupToMovePPlant: | |
lda PiranhaPlantDownYPos,x ;get original vertical coordinate (lowest point) | |
ldy PiranhaPlant_Y_Speed,x ;get vertical speed | |
bpl RiseFallPiranhaPlant ;branch if moving downwards | |
lda PiranhaPlantUpYPos,x ;otherwise get other vertical coordinate (highest point) | |
RiseFallPiranhaPlant: | |
sta $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 | |
bne PutinPipe ;branch to leave if set (likely not necessary) | |
lda Enemy_Y_Position,x ;get current vertical coordinate | |
clc | |
adc PiranhaPlant_Y_Speed,x ;add vertical speed to move up or down | |
sta Enemy_Y_Position,x ;save as new vertical coordinate | |
cmp $00 ;compare against low or high coordinate | |
bne PutinPipe ;branch to leave if not yet reached | |
lda #$00 | |
sta PiranhaPlant_MoveFlag,x ;otherwise clear movement flag | |
lda #$40 | |
sta EnemyFrameTimer,x ;set timer to delay piranha plant movement | |
PutinPipe: | |
lda #%00100000 ;set background priority bit in sprite | |
sta Enemy_SprAttrib,x ;attributes to give illusion of being inside pipe | |
rts ;then leave | |
;------------------------------------------------------------------------------------- | |
;$07 - spinning speed | |
FirebarSpin: | |
sta $07 ;save spinning speed here | |
lda FirebarSpinDirection,x ;check spinning direction | |
bne SpinCounterClockwise ;if moving counter-clockwise, branch to other part | |
ldy #$18 ;possibly residual ldy | |
lda FirebarSpinState_Low,x | |
clc ;add spinning speed to what would normally be | |
adc $07 ;the horizontal speed | |
sta FirebarSpinState_Low,x | |
lda FirebarSpinState_High,x ;add carry to what would normally be the vertical speed | |
adc #$00 | |
rts | |
SpinCounterClockwise: | |
ldy #$08 ;possibly residual ldy | |
lda FirebarSpinState_Low,x | |
sec ;subtract spinning speed to what would normally be | |
sbc $07 ;the horizontal speed | |
sta FirebarSpinState_Low,x | |
lda FirebarSpinState_High,x ;add carry to what would normally be the vertical speed | |
sbc #$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: | |
lda Enemy_Y_HighPos,x ;check high byte of vertical position | |
cmp #$03 | |
bne DoBPl | |
jmp EraseEnemyObject ;if far below screen, kill the object | |
DoBPl: lda Enemy_State,x ;get object's state (set to $ff or other platform offset) | |
bpl CheckBalPlatform ;if doing other balance platform, branch to leave | |
rts | |
CheckBalPlatform: | |
tay ;save offset from state as Y | |
lda PlatformCollisionFlag,x ;get collision flag of platform | |
sta $00 ;store here | |
lda Enemy_MovingDir,x ;get moving direction | |
beq ChkForFall | |
jmp PlatformFall ;if set, jump here | |
ChkForFall: | |
lda #$2d ;check if platform is above a certain point | |
cmp Enemy_Y_Position,x | |
bcc ChkOtherForFall ;if not, branch elsewhere | |
cpy $00 ;if collision flag is set to same value as | |
beq MakePlatformFall ;enemy state, branch to make platforms fall | |
clc | |
adc #$02 ;otherwise add 2 pixels to vertical position | |
sta Enemy_Y_Position,x ;of current platform and branch elsewhere | |
jmp StopPlatforms ;to make platforms stop | |
MakePlatformFall: | |
jmp InitPlatformFall ;make platforms fall | |
ChkOtherForFall: | |
cmp Enemy_Y_Position,y ;check if other platform is above a certain point | |
bcc ChkToMoveBalPlat ;if not, branch elsewhere | |
cpx $00 ;if collision flag is set to same value as | |
beq MakePlatformFall ;enemy state, branch to make platforms fall | |
clc | |
adc #$02 ;otherwise add 2 pixels to vertical position | |
sta Enemy_Y_Position,y ;of other platform and branch elsewhere | |
jmp StopPlatforms ;jump to stop movement and do not return | |
ChkToMoveBalPlat: | |
lda Enemy_Y_Position,x ;save vertical position to stack | |
pha | |
lda PlatformCollisionFlag,x ;get collision flag | |
bpl ColFlg ;branch if collision | |
lda Enemy_Y_MoveForce,x | |
clc ;add $05 to contents of moveforce, whatever they be | |
adc #$05 | |
sta $00 ;store here | |
lda Enemy_Y_Speed,x | |
adc #$00 ;add carry to vertical speed | |
bmi PlatDn ;branch if moving downwards | |
bne PlatUp ;branch elsewhere if moving upwards | |
lda $00 | |
cmp #$0b ;check if there's still a little force left | |
bcc PlatSt ;if not enough, branch to stop movement | |
bcs PlatUp ;otherwise keep branch to move upwards | |
ColFlg: cmp 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: | |
ldy Enemy_State,x ;get offset of other platform | |
pla ;get old vertical coordinate from stack | |
sec | |
sbc Enemy_Y_Position,x ;get difference of old vs. new coordinate | |
clc | |
adc Enemy_Y_Position,y ;add difference to vertical coordinate of other | |
sta Enemy_Y_Position,y ;platform to move it in the opposite direction | |
lda PlatformCollisionFlag,x ;if no collision, skip this part here | |
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 | |
lda Enemy_Y_Speed,y ;check to see if current platform is | |
ora 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 | |
cpx #$20 ;if offset beyond a certain point, go ahead | |
bcs ExitRp ;and skip this, branch to leave | |
lda 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 $01 ;write name table address to vram buffer | |
sta VRAM_Buffer1,x ;first the high byte, then the low | |
lda $00 | |
sta VRAM_Buffer1+1,x | |
lda #$02 ;set length for 2 bytes | |
sta VRAM_Buffer1+2,x | |
lda Enemy_Y_Speed,y ;if platform moving upwards, branch | |
bmi EraseR1 ;to do something else | |
lda #$a2 | |
sta VRAM_Buffer1+3,x ;otherwise put tile numbers for left | |
lda #$a3 ;and right sides of rope in vram buffer | |
sta VRAM_Buffer1+4,x | |
jmp OtherRope ;jump to skip this part | |
EraseR1: lda #$24 ;put blank tiles in vram buffer | |
sta VRAM_Buffer1+3,x ;to erase rope | |
sta VRAM_Buffer1+4,x | |
OtherRope: | |
lda Enemy_State,y ;get offset of other platform from state | |
tay ;use as Y here | |
pla ;pull second copy of vertical speed from stack | |
eor #$ff ;invert bits to reverse speed | |
jsr SetupPlatformRope ;do sub again to figure out where to put bg tiles | |
lda $01 ;write name table address to vram buffer | |
sta VRAM_Buffer1+5,x ;this time we're doing putting tiles for | |
lda $00 ;the other platform | |
sta VRAM_Buffer1+6,x | |
lda #$02 | |
sta VRAM_Buffer1+7,x ;set length again for 2 bytes | |
pla ;pull first copy of vertical speed from stack | |
bpl EraseR2 ;if moving upwards (note inversion earlier), skip this | |
lda #$a2 | |
sta VRAM_Buffer1+8,x ;otherwise put tile numbers for left | |
lda #$a3 ;and right sides of rope in vram | |
sta VRAM_Buffer1+9,x ;transfer buffer | |
jmp EndRp ;jump to skip this part | |
EraseR2: lda #$24 ;put blank tiles in vram buffer | |
sta VRAM_Buffer1+8,x ;to erase rope | |
sta VRAM_Buffer1+9,x | |
EndRp: lda #$00 ;put null terminator at the end | |
sta VRAM_Buffer1+10,x | |
lda VRAM_Buffer1_Offset ;add ten bytes to the vram buffer offset | |
clc ;and store | |
adc #10 | |
sta VRAM_Buffer1_Offset | |
ExitRp: ldx ObjectOffset ;get enemy object buffer offset and leave | |
rts | |
SetupPlatformRope: | |
pha ;save second/third copy to stack | |
lda Enemy_X_Position,y ;get horizontal coordinate | |
clc | |
adc #$08 ;add eight pixels | |
ldx SecondaryHardMode ;if secondary hard mode flag set, | |
bne GetLRp ;use coordinate as-is | |
clc | |
adc #$10 ;otherwise add sixteen more pixels | |
GetLRp: pha ;save modified horizontal coordinate to stack | |
lda Enemy_PageLoc,y | |
adc #$00 ;add carry to page location | |
sta $02 ;and save here | |
pla ;pull modified horizontal coordinate | |
and #%11110000 ;from the stack, mask out low nybble | |
lsr ;and shift three bits to the right | |
lsr | |
lsr | |
sta $00 ;store result here as part of name table low byte | |
ldx Enemy_Y_Position,y ;get vertical coordinate | |
pla ;get second/third copy of vertical speed from stack | |
bpl GetHRp ;skip this part if moving downwards or not at all | |
txa | |
clc | |
adc #$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 | |
and #%00000011 ;mask out all bits but d7 and d6, then set | |
ora #%00100000 ;d5 to get appropriate high byte of name table | |
sta $01 ;address, then store | |
lda $02 ;get saved page location from earlier | |
and #$01 ;mask out all but LSB | |
asl | |
asl ;shift twice to the left and save with the | |
ora $01 ;rest of the bits of the high byte, to get | |
sta $01 ;the proper name table and the right place on it | |
pla ;get modified vertical coordinate from stack | |
and #%11100000 ;mask out low nybble and LSB of high nybble | |
clc | |
adc $00 ;add to horizontal part saved here | |
sta $00 ;save as name table low byte | |
lda Enemy_Y_Position,y | |
cmp #$e8 ;if vertical position not below the | |
bcc ExPRp ;bottom of the screen, we're done, branch to leave | |
lda $00 | |
and #%10111111 ;mask out d6 of low byte of name table address | |
sta $00 | |
ExPRp: rts ;leave! | |
InitPlatformFall: | |
tya ;move offset of other platform from Y to X | |
tax | |
jsr GetEnemyOffscreenBits ;get offscreen bits | |
lda #$06 | |
jsr SetupFloateyNumber ;award 1000 points to player | |
lda Player_Rel_XPos | |
sta FloateyNum_X_Pos,x ;put floatey number coordinates where player is | |
lda Player_Y_Position | |
sta FloateyNum_Y_Pos,x | |
lda #$01 ;set moving direction as flag for | |
sta Enemy_MovingDir,x ;falling platforms | |
StopPlatforms: | |
jsr InitVStf ;initialize vertical speed and low byte | |
sta Enemy_Y_Speed,y ;for both platforms and leave | |
sta 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 | |
lda PlatformCollisionFlag,x ;if player not standing on either platform, | |
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: | |
lda Enemy_Y_Speed,x ;if platform moving up or down, skip ahead to | |
ora Enemy_Y_MoveForce,x ;check on other position | |
bne ChkYCenterPos | |
sta Enemy_YMF_Dummy,x ;initialize dummy variable | |
lda Enemy_Y_Position,x | |
cmp YPlatformTopYPos,x ;if current vertical position => top position, branch | |
bcs ChkYCenterPos ;ahead of all this | |
lda FrameCounter | |
and #%00000111 ;check for every eighth frame | |
bne SkipIY | |
inc Enemy_Y_Position,x ;increase vertical position every eighth frame | |
SkipIY: jmp ChkYPCollision ;skip ahead to last part | |
ChkYCenterPos: | |
lda Enemy_Y_Position,x ;if current vertical position < central position, branch | |
cmp YPlatformCenterYPos,x ;to slow ascent/move downwards | |
bcc YMDown | |
jsr MovePlatformUp ;otherwise start slowing descent/moving upwards | |
jmp ChkYPCollision | |
YMDown: jsr MovePlatformDown ;start slowing ascent/moving downwards | |
ChkYPCollision: | |
lda PlatformCollisionFlag,x ;if collision flag not set here, branch | |
bmi ExYPl ;to leave | |
jsr PositionPlayerOnVPlat ;otherwise position player appropriately | |
ExYPl: rts ;leave | |
;-------------------------------- | |
;$00 - used as adder to position player hotizontally | |
XMovingPlatform: | |
lda #$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 | |
lda PlatformCollisionFlag,x ;if no collision with player, | |
bmi ExXMP ;branch ahead to leave | |
PositionPlayerOnHPlat: | |
lda Player_X_Position | |
clc ;add saved value from second subroutine to | |
adc $00 ;current player's position to position | |
sta Player_X_Position ;player accordingly in horizontal position | |
lda Player_PageLoc ;get player's page location | |
ldy $00 ;check to see if saved value here is positive or negative | |
bmi PPHSubt ;if negative, branch to subtract | |
adc #$00 ;otherwise add carry to page location | |
jmp SetPVar ;jump to skip subtraction | |
PPHSubt: sbc #$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: | |
lda PlatformCollisionFlag,x ;if no collision between platform and player | |
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 $00 ;store saved value here (residual code) | |
lda PlatformCollisionFlag,x ;check collision flag, if no collision between player | |
bmi ExRPl ;and platform, branch ahead, leave speed unaltered | |
lda #$10 | |
sta 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 | |
bne ExLiftP ;and branch to leave | |
lda Enemy_YMF_Dummy,x | |
clc ;add contents of movement amount to whatever's here | |
adc Enemy_Y_MoveForce,x | |
sta Enemy_YMF_Dummy,x | |
lda Enemy_Y_Position,x ;add whatever vertical speed is set to current | |
adc Enemy_Y_Speed,x ;vertical position plus carry to move up or down | |
sta Enemy_Y_Position,x ;and then leave | |
rts | |
ChkSmallPlatCollision: | |
lda PlatformCollisionFlag,x ;get bounding box counter saved in collision flag | |
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: | |
lda Enemy_ID,x ;check for cheep-cheep object | |
cmp #FlyingCheepCheep ;branch to leave if found | |
beq ExScrnBd | |
lda ScreenLeft_X_Pos ;get horizontal coordinate for left side of screen | |
ldy Enemy_ID,x | |
cpy #HammerBro ;check for hammer bro object | |
beq LimitB | |
cpy #PiranhaPlant ;check for piranha plant object | |
bne ExtendLB ;these two will be erased sooner than others if too far left | |
LimitB: adc #$38 ;add 56 pixels to coordinate if hammer bro or piranha plant | |
ExtendLB: sbc #$48 ;subtract 72 pixels regardless of enemy object | |
sta $01 ;store result here | |
lda ScreenLeft_PageLoc | |
sbc #$00 ;subtract borrow from page location of left side | |
sta $00 ;store result here | |
lda ScreenRight_X_Pos ;add 72 pixels to the right side horizontal coordinate | |
adc #$48 | |
sta $03 ;store result here | |
lda ScreenRight_PageLoc | |
adc #$00 ;then add the carry to the page location | |
sta $02 ;and store result here | |
lda Enemy_X_Position,x ;compare horizontal coordinate of the enemy object | |
cmp $01 ;to modified horizontal left edge coordinate to get carry | |
lda Enemy_PageLoc,x | |
sbc $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 | |
lda Enemy_X_Position,x ;compare horizontal coordinate of the enemy object | |
cmp $03 ;to modified horizontal right edge coordinate to get carry | |
lda Enemy_PageLoc,x | |
sbc $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 | |
lda Enemy_State,x ;if at this point, enemy is offscreen to the right, so check | |
cmp #HammerBro ;if in state used by spiny's egg, do not erase | |
beq ExScrnBd | |
cpy #PiranhaPlant ;if piranha plant, do not erase | |
beq ExScrnBd | |
cpy #FlagpoleFlagObject ;if flagpole flag, do not erase | |
beq ExScrnBd | |
cpy #StarFlagObject ;if star flag, do not erase | |
beq ExScrnBd | |
cpy #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: | |
lda Fireball_State,x ;check to see if fireball state is set at all | |
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 | |
adc #$1c ;then add $1c or 28 bytes to it | |
tay ;to use fireball's bounding box coordinates | |
ldx #$04 | |
FireballEnemyCDLoop: | |
stx $01 ;store enemy object offset here | |
tya | |
pha ;push fireball offset to the stack | |
lda Enemy_State,x | |
and #%00100000 ;check to see if d5 is set in enemy state | |
bne NoFToECol ;if so, skip to next enemy slot | |
lda Enemy_Flag,x ;check to see if buffer flag is set | |
beq NoFToECol ;if not, skip to next enemy slot | |
lda Enemy_ID,x ;check enemy identifier | |
cmp #$24 | |
bcc GoombaDie ;if < $24, branch to check further | |
cmp #$2b | |
bcc NoFToECol ;if in range $24-$2a, skip to next enemy slot | |
GoombaDie: cmp #Goomba ;check for goomba identifier | |
bne NotGoomba ;if not found, continue with code | |
lda Enemy_State,x ;otherwise check for defeated state | |
cmp #$02 ;if stomped or otherwise defeated, | |
bcs NoFToECol ;skip to next enemy slot | |
NotGoomba: lda EnemyOffscrBitsMasked,x ;if any masked offscreen bits set, | |
bne NoFToECol ;skip to next enemy slot | |
txa | |
asl ;otherwise multiply enemy offset by four | |
asl | |
clc | |
adc #$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 | |
lda #%10000000 | |
sta Fireball_State,x ;set d7 in enemy state | |
ldx $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 $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 $01 ;get current enemy object offset | |
lda Enemy_Flag,x ;check buffer flag for d7 set | |
bpl ChkBuzzyBeetle ;branch if not set to continue | |
and #%00001111 ;otherwise mask out high nybble and | |
tax ;use low nybble as enemy offset | |
lda Enemy_ID,x | |
cmp #Bowser ;check enemy identifier for bowser | |
beq HurtBowser ;branch if found | |
ldx $01 ;otherwise retrieve current enemy offset | |
ChkBuzzyBeetle: | |
lda Enemy_ID,x | |
cmp #BuzzyBeetle ;check for buzzy beetle | |
beq ExHCF ;branch if found to leave (buzzy beetles fireproof) | |
cmp #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: | |
dec 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 | |
sta Enemy_X_Speed,x ;initialize horizontal speed | |
sta EnemyFrenzyBuffer ;init enemy frenzy buffer | |
lda #$fe | |
sta Enemy_Y_Speed,x ;set vertical speed to make defeated bowser jump a little | |
ldy WorldNumber ;use world number as offset | |
lda BowserIdentities,y ;get enemy identifier to replace bowser with | |
sta Enemy_ID,x ;set as new enemy identifier | |
lda #$20 ;set A to use starting value for state | |
cpy #$03 ;check to see if using offset of 3 or more | |
bcs SetDBSte ;branch if so | |
ora #$03 ;otherwise add 3 to enemy state | |
SetDBSte: sta Enemy_State,x ;set defeated enemy state | |
lda #Sfx_BowserFall | |
sta Square2SoundQueue ;load bowser defeat sound | |
ldx $01 ;get enemy offset | |
lda #$09 ;award 5000 points to player for defeating bowser | |
bne EnemySmackScore ;unconditional branch to award points | |
ChkOtherEnemies: | |
cmp #BulletBill_FrenzyVar | |
beq ExHCF ;branch to leave if bullet bill (frenzy variant) | |
cmp #Podoboo | |
beq ExHCF ;branch to leave if podoboo | |
cmp #$15 | |
bcs ExHCF ;branch to leave if identifier => $15 | |
ShellOrBlockDefeat: | |
lda Enemy_ID,x ;check for piranha plant | |
cmp #PiranhaPlant | |
bne StnE ;branch if not found | |
lda Enemy_Y_Position,x | |
adc #$18 ;add 24 pixels to enemy object's vertical position | |
sta Enemy_Y_Position,x | |
StnE: jsr ChkToStunEnemies ;do yet another sub | |
lda Enemy_State,x | |
and #%00011111 ;mask out 2 MSB of enemy object's state | |
ora #%00100000 ;set d5 to defeat enemy and save as new state | |
sta Enemy_State,x | |
lda #$02 ;award 200 points by default | |
ldy Enemy_ID,x ;check for hammer bro | |
cpy #HammerBro | |
bne GoombaPoints ;branch if not found | |
lda #$06 ;award 1000 points for hammer bro | |
GoombaPoints: | |
cpy #Goomba ;check for goomba | |
bne EnemySmackScore ;branch if not found | |
lda #$01 ;award 100 points for goomba | |
EnemySmackScore: | |
jsr SetupFloateyNumber ;update necessary score variables | |
lda #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 | |
ora 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 | |
adc #$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 | |
lda Misc_Collision_Flag,x ;otherwise read collision flag | |
bne ExPHC ;if collision flag already set, branch to leave | |
lda #$01 | |
sta Misc_Collision_Flag,x ;otherwise set collision flag now | |
lda Misc_X_Speed,x | |
eor #$ff ;get two's compliment of | |
clc ;hammer's horizontal speed | |
adc #$01 | |
sta Misc_X_Speed,x ;set to send hammer flying the opposite direction | |
lda StarInvincibleTimer ;if star mario invincibility timer set, | |
bne ExPHC ;branch to leave | |
jmp InjurePlayer ;otherwise jump to hurt player, do not return | |
ClHCol: lda #$00 ;clear collision flag | |
sta Misc_Collision_Flag,x | |
ExPHC: rts | |
;------------------------------------------------------------------------------------- | |
HandlePowerUpCollision: | |
jsr EraseEnemyObject ;erase the power-up object | |
lda #$06 | |
jsr SetupFloateyNumber ;award 1000 points to player by default | |
lda #Sfx_PowerUpGrab | |
sta Square2SoundQueue ;play the power-up sound | |
lda PowerUpType ;check power-up type | |
cmp #$02 | |
bcc Shroom_Flower_PUp ;if mushroom or fire flower, branch | |
cmp #$03 | |
beq SetFor1Up ;if 1-up mushroom, branch | |
lda #$23 ;otherwise set star mario invincibility | |
sta StarInvincibleTimer ;timer, and load the star mario music | |
lda #StarPowerMusic ;into the area music queue, then leave | |
sta AreaMusicQueue | |
rts | |
Shroom_Flower_PUp: | |
lda PlayerStatus ;if player status = small, branch | |
beq UpToSuper | |
cmp #$01 ;if player status not super, leave | |
bne NoPUp | |
ldx ObjectOffset ;get enemy offset, not necessary | |
lda #$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 | |
lda #$0c ;set value to be used by subroutine tree (fiery) | |
jmp UpToFiery ;jump to set values accordingly | |
SetFor1Up: | |
lda #$0b ;change 1000 points into 1-up instead | |
sta FloateyNum_Control,x ;and then leave | |
rts | |
UpToSuper: | |
lda #$01 ;set player status to super | |
sta PlayerStatus | |
lda #$09 ;set value to be used by subroutine tree (super) | |
UpToFiery: | |
ldy #$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 | |
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 | |
lda EnemyOffscrBitsMasked,x ;if current enemy is offscreen by any amount, | |
bne NoPECol ;go ahead and branch to leave | |
lda GameEngineSubroutine | |
cmp #$08 ;if not set to run player control routine | |
bne NoPECol ;on next frame, branch to leave | |
lda Enemy_State,x | |
and #%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 | |
lda Enemy_CollisionBits,x | |
and #%11111110 ;otherwise, clear d0 of current enemy object's | |
sta Enemy_CollisionBits,x ;collision bit | |
NoPECol: rts | |
CheckForPUpCollision: | |
ldy Enemy_ID,x | |
cpy #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, | |
beq HandlePECollisions ;perform task here, otherwise kill enemy like | |
jmp ShellOrBlockDefeat ;hit with a shell, or from beneath | |
KickedShellPtsData: | |
.db $0a, $06, $04 | |
HandlePECollisions: | |
lda Enemy_CollisionBits,x ;check enemy collision bits for d0 set | |
and #%00000001 ;or for being offscreen at all | |
ora EnemyOffscrBitsMasked,x | |
bne ExPEC ;branch to leave if either is true | |
lda #$01 | |
ora Enemy_CollisionBits,x ;otherwise set d0 now | |
sta Enemy_CollisionBits,x | |
cpy #Spiny ;branch if spiny | |
beq ChkForPlayerInjury | |
cpy #PiranhaPlant ;branch if piranha plant | |
beq InjurePlayer | |
cpy #Podoboo ;branch if podoboo | |
beq InjurePlayer | |
cpy #BulletBill_CannonVar ;branch if bullet bill | |
beq ChkForPlayerInjury | |
cpy #$15 ;branch if object => $15 | |
bcs InjurePlayer | |
lda AreaType ;branch if water type level | |
beq InjurePlayer | |
lda Enemy_State,x ;branch if d7 of enemy state was set | |
asl | |
bcs ChkForPlayerInjury | |
lda Enemy_State,x ;mask out all but 3 LSB of enemy state | |
and #%00000111 | |
cmp #$02 ;branch if enemy is in normal or falling state | |
bcc ChkForPlayerInjury | |
lda Enemy_ID,x ;branch to leave if goomba in defeated state | |
cmp #Goomba | |
beq ExPEC | |
lda #Sfx_EnemySmack ;play smack enemy sound | |
sta Square1SoundQueue | |
lda Enemy_State,x ;set d7 in enemy state, thus become moving shell | |
ora #%10000000 | |
sta Enemy_State,x | |
jsr EnemyFacePlayer ;set moving direction and get offset | |
lda KickedShellXSpdData,y ;load and set horizontal speed data with offset | |
sta Enemy_X_Speed,x | |
lda #$03 ;add three to whatever the stomp counter contains | |
clc ;to give points for kicking the shell | |
adc StompChainCounter | |
ldy EnemyIntervalTimer,x ;check shell enemy's timer | |
cpy #$03 ;if above a certain point, branch using the points | |
bcs KSPts ;data obtained from the stomp counter + 3 | |
lda 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 | |
bmi ChkInj ;perform procedure below if player moving upwards | |
bne EnemyStomped ;or not at all, and branch elsewhere if moving downwards | |
ChkInj: lda Enemy_ID,x ;branch if enemy object < $07 | |
cmp #Bloober | |
bcc ChkETmrs | |
lda Player_Y_Position ;add 12 pixels to player's vertical position | |
clc | |
adc #$0c | |
cmp Enemy_Y_Position,x ;compare modified player's position to enemy's position | |
bcc EnemyStomped ;branch if this player's position above (less than) enemy's | |
ChkETmrs: lda StompTimer ;check stomp timer | |
bne EnemyStomped ;branch if set | |
lda InjuryTimer ;check to see if injured invincibility timer still | |
bne ExInjColRoutines ;counting down, and branch elsewhere to leave if so | |
lda Player_Rel_XPos | |
cmp Enemy_Rel_XPos ;if player's relative position to the left of enemy's | |
bcc TInjE ;relative position, branch here | |
jmp ChkEnemyFaceRight ;otherwise do a jump here | |
TInjE: lda Enemy_MovingDir,x ;if enemy moving towards the left, | |
cmp #$01 ;branch, otherwise do a jump here | |
bne InjurePlayer ;to turn the enemy around | |
jmp LInj | |
InjurePlayer: | |
lda InjuryTimer ;check again to see if injured invincibility timer is | |
bne ExInjColRoutines ;at zero, and branch to leave if so | |
ForceInjury: | |
ldx PlayerStatus ;check player's status | |
beq KillPlayer ;branch if small | |
sta PlayerStatus ;otherwise set player's status to small | |
lda #$08 | |
sta InjuryTimer ;set injured invincibility timer | |
asl | |
sta Square1SoundQueue ;play pipedown/injury sound | |
jsr GetPlayerColors ;change player's palette if necessary | |
lda #$0a ;set subroutine to run on next frame | |
SetKRout: ldy #$01 ;set new player state | |
SetPRout: sta GameEngineSubroutine ;load new value to run subroutine on next frame | |
sty Player_State ;store new player state | |
ldy #$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 | |
lda #$fc | |
sta Player_Y_Speed ;set new vertical speed | |
lda #$0b ;set subroutine to run on next frame | |
bne SetKRout ;branch to set player's state and other things | |
StompedEnemyPtsData: | |
.db $02, $06, $05, $06 | |
EnemyStomped: | |
lda Enemy_ID,x ;check for spiny, branch to hurt player | |
cmp #Spiny ;if found | |
beq InjurePlayer | |
lda #Sfx_EnemyStomp ;otherwise play stomp/swim sound | |
sta Square1SoundQueue | |
lda Enemy_ID,x | |
ldy #$00 ;initialize points data offset for stomped enemies | |
cmp #FlyingCheepCheep ;branch for cheep-cheep | |
beq EnemyStompedPts | |
cmp #BulletBill_FrenzyVar ;branch for either bullet bill object | |
beq EnemyStompedPts | |
cmp #BulletBill_CannonVar | |
beq EnemyStompedPts | |
cmp #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 | |
cmp #HammerBro ;branch for hammer bro | |
beq EnemyStompedPts | |
iny ;increment points data offset | |
cmp #Lakitu ;branch for lakitu | |
beq EnemyStompedPts | |
iny ;increment points data offset | |
cmp #Bloober ;branch if NOT bloober | |
bne ChkForDemoteKoopa | |
EnemyStompedPts: | |
lda StompedEnemyPtsData,y ;load points data using offset in Y | |
jsr SetupFloateyNumber ;run sub to set floatey number controls | |
lda Enemy_MovingDir,x | |
pha ;save enemy movement direction to stack | |
jsr SetStun ;run sub to kill enemy | |
pla | |
sta Enemy_MovingDir,x ;return enemy movement direction from stack | |
lda #%00100000 | |
sta Enemy_State,x ;set d5 in enemy state | |
jsr InitVStf ;nullify vertical speed, physics-related thing, | |
sta Enemy_X_Speed,x ;and horizontal speed | |
lda #$fd ;set player's vertical speed, to give bounce | |
sta Player_Y_Speed | |
rts | |
ChkForDemoteKoopa: | |
cmp #$09 ;branch elsewhere if enemy object < $09 | |
bcc HandleStompedShellE | |
and #%00000001 ;demote koopa paratroopas to ordinary troopas | |
sta Enemy_ID,x | |
ldy #$00 ;return enemy to normal state | |
sty Enemy_State,x | |
lda #$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 | |
lda DemotedKoopaXSpdData,y | |
sta Enemy_X_Speed,x ;set appropriate moving speed based on direction | |
jmp SBnce ;then move onto something else | |
RevivalRateData: | |
.db $10, $0b | |
HandleStompedShellE: | |
lda #$04 ;set defeated state for enemy | |
sta Enemy_State,x | |
inc StompChainCounter ;increment the stomp counter | |
lda StompChainCounter ;add whatever is in the stomp counter | |
clc ;to whatever is in the stomp timer | |
adc StompTimer | |
jsr SetupFloateyNumber ;award points accordingly | |
inc StompTimer ;increment stomp timer of some sort | |
ldy PrimaryHardMode ;check primary hard mode flag | |
lda RevivalRateData,y ;load timer setting according to flag | |
sta EnemyIntervalTimer,x ;set as enemy timer to revive stomped enemy | |
SBnce: lda #$fc ;set player's vertical speed for bounce | |
sta Player_Y_Speed ;and then leave!!! | |
rts | |
ChkEnemyFaceRight: | |
lda Enemy_MovingDir,x ;check to see if enemy is moving to the right | |
cmp #$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: | |
ldy #$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: sty Enemy_MovingDir,x ;set moving direction here | |
dey ;then decrement to use as a proper offset | |
rts | |
SetupFloateyNumber: | |
sta FloateyNum_Control,x ;set number of points control for floatey numbers | |
lda #$30 | |
sta FloateyNum_Timer,x ;set timer for floatey numbers | |
lda Enemy_Y_Position,x | |
sta FloateyNum_Y_Pos,x ;set vertical coordinate | |
lda Enemy_Rel_XPos | |
sta 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: | |
lda FrameCounter ;check counter for d0 set | |
lsr | |
bcc ExSFN ;if d0 not set, leave | |
lda AreaType | |
beq ExSFN ;if water area type, leave | |
lda Enemy_ID,x | |
cmp #$15 ;if enemy object => $15, branch to leave | |
bcs ExitECRoutine | |
cmp #Lakitu ;if lakitu, branch to leave | |
beq ExitECRoutine | |
cmp #PiranhaPlant ;if piranha plant, branch to leave | |
beq ExitECRoutine | |
lda EnemyOffscrBitsMasked,x ;if masked offscreen bits nonzero, branch to leave | |
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 $01 ;save enemy object buffer offset for second enemy here | |
tya ;save first enemy's bounding box offset to stack | |
pha | |
lda Enemy_Flag,x ;check enemy object enable flag | |
beq ReadyNextEnemy ;branch if flag not set | |
lda Enemy_ID,x | |
cmp #$15 ;check for enemy object => $15 | |
bcs ReadyNextEnemy ;branch if true | |
cmp #Lakitu | |
beq ReadyNextEnemy ;branch if enemy object is lakitu | |
cmp #PiranhaPlant | |
beq ReadyNextEnemy ;branch if enemy object is piranha plant | |
lda EnemyOffscrBitsMasked,x | |
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 | |
adc #$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 $01 ;use second enemy offset for Y | |
bcc NoEnemyCollision ;if carry clear, no collision, branch ahead of this | |
lda Enemy_State,x | |
ora Enemy_State,y ;check both enemy states for d7 set | |
and #%10000000 | |
bne YesEC ;branch if at least one of them is set | |
lda Enemy_CollisionBits,y ;load first enemy's collision-related bits | |
and SetBitsMask,x ;check to see if bit connected to second enemy is | |
bne ReadyNextEnemy ;already set, and move onto next enemy slot if set | |
lda Enemy_CollisionBits,y | |
ora SetBitsMask,x ;if the bit is not set, set it now | |
sta Enemy_CollisionBits,y | |
YesEC: jsr ProcEnemyCollisions ;react according to the nature of collision | |
jmp ReadyNextEnemy ;move onto next enemy slot | |
NoEnemyCollision: | |
lda Enemy_CollisionBits,y ;load first enemy's collision-related bits | |
and ClearBitsMask,x ;clear bit connected to second enemy | |
sta 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 $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 | |
ProcEnemyCollisions: | |
lda Enemy_State,y ;check both enemy states for d5 set | |
ora Enemy_State,x | |
and #%00100000 ;if d5 is set in either state, or both, branch | |
bne ExitProcessEColl ;to leave and do nothing else at this point | |
lda Enemy_State,x | |
cmp #$06 ;if second enemy state < $06, branch elsewhere | |
bcc ProcSecondEnemyColl | |
lda Enemy_ID,x ;check second enemy identifier for hammer bro | |
cmp #HammerBro ;if hammer bro found in alt state, branch to leave | |
beq ExitProcessEColl | |
lda Enemy_State,y ;check first enemy state for d7 set | |
asl | |
bcc ShellCollisions ;branch if d7 is clear | |
lda #$06 | |
jsr SetupFloateyNumber ;award 1000 points for killing enemy | |
jsr ShellOrBlockDefeat ;then kill enemy, then load | |
ldy $01 ;original offset of second enemy | |
ShellCollisions: | |
tya ;move Y to X | |
tax | |
jsr ShellOrBlockDefeat ;kill second enemy | |
ldx ObjectOffset | |
lda ShellChainCounter,x ;get chain counter for shell | |
clc | |
adc #$04 ;add four to get appropriate point offset | |
ldx $01 | |
jsr SetupFloateyNumber ;award appropriate number of points for second enemy | |
ldx ObjectOffset ;load original offset of first enemy | |
inc ShellChainCounter,x ;increment chain counter for additional enemies | |
ExitProcessEColl: | |
rts ;leave!!! | |
ProcSecondEnemyColl: | |
lda Enemy_State,y ;if first enemy state < $06, branch elsewhere | |
cmp #$06 | |
bcc MoveEOfs | |
lda Enemy_ID,y ;check first enemy identifier for hammer bro | |
cmp #HammerBro ;if hammer bro found in alt state, branch to leave | |
beq ExitProcessEColl | |
jsr ShellOrBlockDefeat ;otherwise, kill first enemy | |
ldy $01 | |
lda ShellChainCounter,y ;get chain counter for shell | |
clc | |
adc #$04 ;add four to get appropriate point offset | |
ldx ObjectOffset | |
jsr SetupFloateyNumber ;award appropriate number of points for first enemy | |
ldx $01 ;load original offset of second enemy | |
inc 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: | |
lda Enemy_ID,x ;check for specific enemies | |
cmp #PiranhaPlant | |
beq ExTA ;if piranha plant, leave | |
cmp #Lakitu | |
beq ExTA ;if lakitu, leave | |
cmp #HammerBro | |
beq ExTA ;if hammer bro, leave | |
cmp #Spiny | |
beq RXSpd ;if spiny, turn it around | |
cmp #GreenParatroopaJump | |
beq RXSpd ;if green paratroopa, turn it around | |
cmp #$07 | |
bcs ExTA ;if any OTHER enemy object => $07, leave | |
RXSpd: lda Enemy_X_Speed,x ;load horizontal speed | |
eor #$ff ;get two's compliment for horizontal speed | |
tay | |
iny | |
sty Enemy_X_Speed,x ;store as new horizontal speed | |
lda Enemy_MovingDir,x | |
eor #%00000011 ;invert moving direction and store, then leave | |
sta Enemy_MovingDir,x ;thus effectively turning the enemy around | |
ExTA: rts ;leave!!! | |
;------------------------------------------------------------------------------------- | |
;$00 - vertical position of platform | |
LargePlatformCollision: | |
lda #$ff ;save value here | |
sta PlatformCollisionFlag,x | |
lda TimerControl ;check master timer control | |
bne ExLPC ;if set, branch to leave | |
lda Enemy_State,x ;if d7 set in object state, | |
bmi ExLPC ;branch to leave | |
lda Enemy_ID,x | |
cmp #$24 ;check enemy object identifier for | |
bne ChkForPlayerC_LargeP ;balance platform, branch if not found | |
lda 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 | |
lda Enemy_Y_Position,x ;store vertical coordinate in | |
sta $00 ;temp variable for now | |
txa ;send offset we're on to the stack | |
pha | |
jsr PlayerCollisionCore ;do player-to-platform collision detection | |
pla ;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, | |
bne ExSPC ;branch to leave | |
sta 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 | |
lda #$02 | |
sta $00 ;load counter here for 2 bounding boxes | |
ChkSmallPlatLoop: | |
ldx ObjectOffset ;get enemy object offset | |
jsr GetEnemyBoundBoxOfs ;get bounding box offset in Y | |
and #%00000010 ;if d1 of offscreen lower nybble bits was set | |
bne ExSPC ;then branch to leave | |
lda BoundingBox_UL_YPos,y ;check top of platform's bounding box for being | |
cmp #$20 ;above a specific point | |
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: | |
lda BoundingBox_UL_YPos,y ;move bounding box vertical coordinates | |
clc ;128 pixels downwards | |
adc #$80 | |
sta BoundingBox_UL_YPos,y | |
lda BoundingBox_DR_YPos,y | |
clc | |
adc #$80 | |
sta BoundingBox_DR_YPos,y | |
dec $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: | |
lda BoundingBox_DR_YPos,y ;get difference by subtracting the top | |
sec ;of the player's bounding box from the bottom | |
sbc BoundingBox_UL_YPos ;of the platform's bounding box | |
cmp #$04 ;if difference too large or negative, | |
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 | |
bpl ChkForTopCollision ;if so, don't mess with it | |
lda #$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 | |
sec ;of the platform's bounding box from the bottom | |
sbc BoundingBox_UL_YPos,y ;of the player's bounding box | |
cmp #$06 | |
bcs PlatformSideCollisions ;if difference not close enough, skip all of this | |
lda Player_Y_Speed | |
bmi PlatformSideCollisions ;if player's vertical speed moving upwards, skip this | |
lda $00 ;get saved bounding box counter from earlier | |
ldy Enemy_ID,x | |
cpy #$2b ;if either of the two small platform objects are found, | |
beq SetCollisionFlag ;regardless of which one, branch to use bounding box counter | |
cpy #$2c ;as contents of collision flag | |
beq SetCollisionFlag | |
txa ;otherwise use enemy object buffer offset | |
SetCollisionFlag: | |
ldx ObjectOffset ;get enemy object buffer offset | |
sta PlatformCollisionFlag,x ;save either bounding box counter or enemy offset here | |
lda #$00 | |
sta Player_State ;set player state to normal then leave | |
rts | |
PlatformSideCollisions: | |
lda #$01 ;set value here to indicate possible horizontal | |
sta $00 ;collision on left side of platform | |
lda BoundingBox_DR_XPos ;get difference by subtracting platform's left edge | |
sec ;from player's right edge | |
sbc BoundingBox_UL_XPos,y | |
cmp #$08 ;if difference close enough, skip all of this | |
bcc SideC | |
inc $00 ;otherwise increment value set here for right side collision | |
lda BoundingBox_DR_XPos,y ;get difference by subtracting player's left edge | |
clc ;from platform's right edge | |
sbc BoundingBox_UL_XPos | |
cmp #$09 ;if difference not close enough, skip subroutine | |
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 | |
lda Enemy_Y_Position,x ;for offset | |
clc ;add positioning data using offset to the vertical | |
adc PlayerPosSPlatData-1,y ;coordinate | |
.db $2c ;BIT instruction opcode | |
PositionPlayerOnVPlat: | |
lda Enemy_Y_Position,x ;get vertical coordinate | |
ldy GameEngineSubroutine | |
cpy #$0b ;if certain routine being executed on this frame, | |
beq ExPlPos ;skip all of this | |
ldy Enemy_Y_HighPos,x | |
cpy #$01 ;if vertical high byte offscreen, skip this | |
bne ExPlPos | |
sec ;subtract 32 pixels from vertical coordinate | |
sbc #$20 ;for the player object's height | |
sta Player_Y_Position ;save as player's new vertical coordinate | |
tya | |
sbc #$00 ;subtract borrow and store as player's | |
sta Player_Y_HighPos ;new vertical high byte | |
lda #$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 | |
cmp #$f0 ;vertically, leave this routine | |
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 | |
cmp #$d0 ;the player is vertically | |
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 | |
adc #$04 | |
tay ;send to Y | |
lda Enemy_OffscreenBits ;get offscreen bits for enemy object | |
and #%00001111 ;save low nybble | |
cmp #%00001111 ;check for all bits set | |
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, | |
bne ExPBGCol ;branch to leave | |
lda GameEngineSubroutine | |
cmp #$0b ;if running routine #11 or $0b | |
beq ExPBGCol ;branch to leave | |
cmp #$04 | |
bcc ExPBGCol ;if running routines $00-$03 branch to leave | |
lda #$01 ;load default player state for swimming | |
ldy SwimmingFlag ;if swimming flag set, | |
bne SetPSte ;branch ahead to set default state | |
lda Player_State ;if player in normal state, | |
beq SetFallS ;branch to set default state for falling | |
cmp #$03 | |
bne ChkOnScr ;if in any other state besides climbing, skip to next part | |
SetFallS: lda #$02 ;load default player state for falling | |
SetPSte: sta Player_State ;set whatever player state is appropriate | |
ChkOnScr: lda Player_Y_HighPos | |
cmp #$01 ;check player's vertical high byte for still on the screen | |
bne ExPBGCol ;branch to leave if not | |
lda #$ff | |
sta Player_CollisionBits ;initialize player's collision flag | |
lda Player_Y_Position | |
cmp #$cf ;check player's vertical coordinate | |
bcc ChkCollSize ;if not too close to the bottom of screen, continue | |
ExPBGCol: rts ;otherwise leave | |
ChkCollSize: | |
ldy #$02 ;load default offset | |
lda CrouchingFlag | |
bne GBBAdr ;if player crouching, skip ahead | |
lda PlayerSize | |
bne GBBAdr ;if player small, skip ahead | |
dey ;otherwise decrement offset for big player not crouching | |
lda SwimmingFlag | |
bne GBBAdr ;if swimming flag set, skip ahead | |
dey ;otherwise decrement offset | |
GBBAdr: lda BlockBufferAdderData,y ;get value using offset | |
sta $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 | |
beq HeadChk ;if player not crouching, branch ahead | |
inx ;otherwise increment size as offset | |
HeadChk: lda Player_Y_Position ;get player's vertical coordinate | |
cmp PlayerBGUpperExtent,x ;compare with upper extent value based on offset | |
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 | |
bpl DoFootCheck ;if player not moving upwards, branch elsewhere | |
ldy $04 ;check lower nybble of vertical coordinate returned | |
cpy #$04 ;from collision detection routine | |
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 | |
beq NYSpd ;if water level, branch ahead | |
ldy BlockBounceTimer ;if block bounce timer not expired, | |
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: | |
cmp #$26 ;if climbing metatile, | |
beq NYSpd ;branch ahead and do not play sound | |
lda #Sfx_Bump | |
sta Square1SoundQueue ;otherwise load bump sound | |
NYSpd: lda #$01 ;set player's vertical speed to nullify | |
sta Player_Y_Speed ;jump or swim | |
DoFootCheck: | |
ldy $eb ;get block buffer adder offset | |
lda Player_Y_Position | |
cmp #$cf ;check to see how low player is | |
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 $00 ;save bottom right metatile here | |
pla | |
sta $01 ;pull bottom left metatile and save here | |
bne ChkFootMTile ;if anything here, skip this part | |
lda $00 ;otherwise check for anything in bottom right metatile | |
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 | |
bmi DoPlayerSideCheck ;if player moving upwards, branch | |
cmp #$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, | |
bne InitSteP ;branch ahead | |
ldy $04 ;check lower nybble of vertical coordinate returned | |
cpy #$05 ;from collision detection routine | |
bcc LandPlyr ;if lower nybble < 5, branch | |
lda Player_MovingDir | |
sta $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 | |
lda #$f0 | |
and 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 | |
lda #$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: lda #$00 | |
sta Player_State ;set player's state to normal | |
DoPlayerSideCheck: | |
ldy $eb ;get block buffer adder offset | |
iny | |
iny ;increment offset 2 bytes to use adders for side collisions | |
lda #$02 ;set value here to be used as counter | |
sta $00 | |
SideCheckLoop: | |
iny ;move onto the next one | |
sty $eb ;store it | |
lda Player_Y_Position | |
cmp #$20 ;check player's vertical position | |
bcc BHalf ;if player is in status bar area, branch ahead to skip this part | |
cmp #$e4 | |
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 | |
cmp #$1c ;otherwise check for pipe metatiles | |
beq BHalf ;if collided with sideways pipe (top), branch ahead | |
cmp #$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 $eb ;load block adder offset | |
iny ;increment it | |
lda Player_Y_Position ;get player's vertical position | |
cmp #$08 | |
bcc ExSCH ;if too high, branch to leave | |
cmp #$d0 | |
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 | |
dec $00 ;otherwise decrement counter | |
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 | |
bne ExCSM ;branch to leave if set | |
jmp StopPlayerMove ;otherwise jump to impede player's movement | |
ChkPBtm: ldy Player_State ;get player's state | |
cpy #$00 ;check for player's state set to normal | |
bne StopPlayerMove ;if not, branch to impede player's movement | |
ldy PlayerFacingDir ;get player's facing direction | |
dey | |
bne StopPlayerMove ;if facing left, branch to impede movement | |
cmp #$6c ;otherwise check for pipe metatiles | |
beq PipeDwnS ;if collided with sideways pipe (bottom), branch | |
cmp #$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 | |
bne PlyrPipe ;if already set, branch, do not play sound again | |
ldy #Sfx_PipeDown_Injury | |
sty Square1SoundQueue ;otherwise load pipedown/injury sound | |
PlyrPipe: ora #%00100000 | |
sta Player_SprAttrib ;set background priority bit in player attributes | |
lda Player_X_Position | |
and #%00001111 ;get lower nybble of player's horizontal coordinate | |
beq ChkGERtn ;if at zero, branch ahead to skip this part | |
ldy #$00 ;set default offset for timer setting data | |
lda ScreenLeft_PageLoc ;load page location for left side of screen | |
beq SetCATmr ;if at page zero, use default offset | |
iny ;otherwise increment offset | |
SetCATmr: lda AreaChangeTimerData,y ;set timer for change of area as appropriate | |
sta ChangeAreaTimer | |
ChkGERtn: lda GameEngineSubroutine ;get number of game engine routine running | |
cmp #$07 | |
beq ExCSM ;if running player entrance routine or | |
cmp #$08 ;player control routine, go ahead and branch to leave | |
bne ExCSM | |
lda #$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: | |
jsr ImpedePlayerMove ;stop player's movement | |
ExCSM: rts ;leave | |
AreaChangeTimerData: | |
.db $a0, $34 | |
HandleCoinMetatile: | |
jsr ErACM ;do sub to erase coin metatile from block buffer | |
inc CoinTallyFor1Ups ;increment coin tally used for 1-up blocks | |
jmp GiveOneCoin ;update coin amount and tally on the screen | |
HandleAxeMetatile: | |
lda #$00 | |
sta OperMode_Task ;reset secondary mode | |
lda #$02 | |
sta OperMode ;set primary mode to autoctrl mode | |
lda #$18 | |
sta Player_X_Speed ;set horizontal speed and continue to erase axe metatile | |
ErACM: ldy $02 ;load vertical high nybble offset for block buffer | |
lda #$00 ;load blank metatile | |
sta ($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 $04 ;check low nybble of horizontal coordinate returned from | |
cpy #$06 ;collision detection routine against certain values, this | |
bcc ExHC ;makes actual physical part of vine or flagpole thinner | |
cpy #$0a ;than 16 pixels | |
bcc ChkForFlagpole | |
ExHC: rts ;leave if too far left or too far right | |
ChkForFlagpole: | |
cmp #$24 ;check climbing metatiles | |
beq FlagpoleCollision ;branch if flagpole ball found | |
cmp #$25 | |
bne VineCollision ;branch to alternate code if flagpole shaft not found | |
FlagpoleCollision: | |
lda GameEngineSubroutine | |
cmp #$05 ;check for end-of-level routine running | |
beq PutPlayerOnVine ;if running, branch to end of climbing code | |
lda #$01 | |
sta PlayerFacingDir ;set player's facing direction to right | |
inc ScrollLock ;set scroll lock flag | |
lda GameEngineSubroutine | |
cmp #$04 ;check for flagpole slide routine running | |
beq RunFR ;if running, branch to end of flagpole code here | |
lda #BulletBill_CannonVar ;load identifier for bullet bills (cannon variant) | |
jsr KillEnemies ;get rid of them | |
lda #Silence | |
sta EventMusicQueue ;silence music | |
lsr | |
sta FlagpoleSoundQueue ;load flagpole sound into flagpole sound queue | |
ldx #$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: | |
cmp FlagpoleYPosData,x ;compare with current vertical coordinate data | |
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: lda #$04 | |
sta GameEngineSubroutine ;set value to run flagpole slide routine | |
jmp PutPlayerOnVine ;jump to end of climbing code | |
VineCollision: | |
cmp #$26 ;check for climbing metatile used on vines | |
bne PutPlayerOnVine | |
lda Player_Y_Position ;check player's vertical coordinate | |
cmp #$20 ;for being in status bar area | |
bcs PutPlayerOnVine ;branch if not that far up | |
lda #$01 | |
sta GameEngineSubroutine ;otherwise set to run autoclimb routine next frame | |
PutPlayerOnVine: | |
lda #$03 ;set player state to climbing | |
sta Player_State | |
lda #$00 ;nullify player's horizontal speed | |
sta Player_X_Speed ;and fractional horizontal movement force | |
sta Player_X_MoveForce | |
lda Player_X_Position ;get player's horizontal coordinate | |
sec | |
sbc ScreenLeft_X_Pos ;subtract from left side horizontal coordinate | |
cmp #$10 | |
bcs SetVXPl ;if 16 or more pixels difference, do not alter facing direction | |
lda #$02 | |
sta PlayerFacingDir ;otherwise force player to face left | |
SetVXPl: ldy PlayerFacingDir ;get current facing direction, use as offset | |
lda $06 ;get low byte of block buffer address | |
asl | |
asl ;move low nybble to high | |
asl | |
asl | |
clc | |
adc ClimbXPosAdder-1,y ;add pixels depending on facing direction | |
sta Player_X_Position ;store as player's horizontal coordinate | |
lda $06 ;get low byte of block buffer address again | |
bne ExPVne ;if not zero, branch | |
lda ScreenRight_PageLoc ;load page location of right side of screen | |
clc | |
adc ClimbPLocAdder-1,y ;add depending on facing location | |
sta Player_PageLoc ;store as player's page location | |
ExPVne: rts ;finally, we're done! | |
;-------------------------------- | |
ChkInvisibleMTiles: | |
cmp #$5f ;check for hidden coin block | |
beq ExCInvT ;branch to leave if found | |
cmp #$60 ;check for hidden 1-up block | |
ExCInvT: 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 | |
lda #$70 | |
sta VerticalForce ;otherwise set vertical movement force for player | |
lda #$f9 | |
sta JumpspringForce ;set default jumpspring force | |
lda #$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: | |
cmp #$67 ;check for top jumpspring metatile | |
beq JSFnd ;branch to set carry if found | |
cmp #$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 | |
and #%00000100 ;for pressing down | |
beq ExPipeE ;if not pressing down, branch to leave | |
lda $00 | |
cmp #$11 ;check right foot metatile for warp pipe right metatile | |
bne ExPipeE ;branch to leave if not found | |
lda $01 | |
cmp #$10 ;check left foot metatile for warp pipe left metatile | |
bne ExPipeE ;branch to leave if not found | |
lda #$30 | |
sta ChangeAreaTimer ;set timer for change of area | |
lda #$03 | |
sta GameEngineSubroutine ;set to run vertical pipe entry routine on next frame | |
lda #Sfx_PipeDown_Injury | |
sta Square1SoundQueue ;load pipedown/injury sound | |
lda #%00100000 | |
sta Player_SprAttrib ;set background priority bit in player's attributes | |
lda WarpZoneControl ;check warp zone control | |
beq ExPipeE ;branch to leave if none found | |
and #%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 | |
cmp #$60 | |
bcc GetWNum ;if player at left, not near middle, use offset and skip ahead | |
inx ;otherwise increment for middle pipe | |
cmp #$a0 | |
bcc GetWNum ;if player at middle, but not too far right, use offset and skip | |
inx ;otherwise increment for last pipe | |
GetWNum: ldy WarpZoneNumbers,x ;get warp zone numbers | |
dey ;decrement for use as world number | |
sty WorldNumber ;store as world number and offset | |
ldx WorldAddrOffsets,y ;get offset to where this world's area offsets are | |
lda AreaAddrOffsets,x ;get area offset based on world offset | |
sta AreaPointer ;store area offset here to be used to change areas | |
lda #Silence | |
sta EventMusicQueue ;silence music | |
lda #$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 | |
inc Hidden1UpFlag ;set flag for hidden 1-up blocks | |
inc FetchNewGameTimerFlag ;set flag to load new game timer | |
ExPipeE: rts ;leave!!! | |
ImpedePlayerMove: | |
lda #$00 ;initialize value here | |
ldy Player_X_Speed ;get player's horizontal speed | |
ldx $00 ;check value set earlier for | |
dex ;left side collision | |
bne RImpd ;if right side collision, skip this part | |
inx ;return value to X | |
cpy #$00 ;if player moving to the left, | |
bmi ExIPM ;branch to invert bit and leave | |
lda #$ff ;otherwise load A with value to be used later | |
jmp NXSpd ;and jump to affect movement | |
RImpd: ldx #$02 ;return $02 to X | |
cpy #$01 ;if player moving to the right, | |
bpl ExIPM ;branch to invert bit and leave | |
lda #$01 ;otherwise load A with value to be used here | |
NXSpd: ldy #$10 | |
sty SideCollisionTimer ;set timer of some sort | |
ldy #$00 | |
sty Player_X_Speed ;nullify player's horizontal speed | |
cmp #$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 $00 ;store Y as high bits of horizontal adder | |
clc | |
adc 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 | |
adc $00 ;add high bits and carry to | |
sta Player_PageLoc ;page location if necessary | |
ExIPM: txa ;invert contents of X | |
eor #$ff | |
and Player_CollisionBits ;mask out bit that was set here | |
sta Player_CollisionBits ;store to clear bit | |
rts | |
;-------------------------------- | |
SolidMTileUpperExt: | |
.db $10, $61, $88, $c4 | |
CheckForSolidMTiles: | |
jsr GetMTileAttrib ;find appropriate offset based on metatile's 2 MSB | |
cmp SolidMTileUpperExt,x ;compare current metatile with solid metatiles | |
rts | |
ClimbMTileUpperExt: | |
.db $24, $6d, $8a, $c6 | |
CheckForClimbMTiles: | |
jsr GetMTileAttrib ;find appropriate offset based on metatile's 2 MSB | |
cmp ClimbMTileUpperExt,x ;compare current metatile with climbable metatiles | |
rts | |
CheckForCoinMTiles: | |
cmp #$c2 ;check for regular coin | |
beq CoinSd ;branch if found | |
cmp #$c3 ;check for underwater coin | |
beq CoinSd ;branch if found | |
clc ;otherwise clear carry and leave | |
rts | |
CoinSd: lda #Sfx_CoinGrab | |
sta Square2SoundQueue ;load coin grab sound and leave | |
rts | |
GetMTileAttrib: | |
tay ;save metatile value into Y | |
and #%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: | |
lda Enemy_State,x ;check enemy state for d6 set | |
and #%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 | |
ldy Enemy_ID,x | |
cpy #Spiny ;if enemy object is not spiny, branch elsewhere | |
bne DoIDCheckBGColl | |
lda Enemy_Y_Position,x | |
cmp #$25 ;if enemy vertical coordinate < 36 branch to leave | |
bcc ExEBG | |
DoIDCheckBGColl: | |
cpy #GreenParatroopaJump ;check for some other enemy object | |
bne HBChk ;branch if not found | |
jmp EnemyJump ;otherwise jump elsewhere | |
HBChk: cpy #HammerBro ;check for hammer bro | |
bne CInvu ;branch if not found | |
jmp HammerBroBGColl ;otherwise jump elsewhere | |
CInvu: cpy #Spiny ;if enemy object is spiny, branch | |
beq YesIn | |
cpy #PowerUpObject ;if special power-up object, branch | |
beq YesIn | |
cpy #$07 ;if enemy object =>$07, branch to leave | |
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 | |
cmp #$23 | |
bne LandEnemyProperly ;check for blank metatile $23 and branch if not found | |
ldy $02 ;get vertical coordinate used to find block | |
lda #$00 ;store default blank metatile in that spot so we won't | |
sta ($06),y ;trigger this routine accidentally again | |
lda Enemy_ID,x | |
cmp #$15 ;if enemy object => $15, branch ahead | |
bcs ChkToStunEnemies | |
cmp #Goomba ;if enemy object not goomba, branch ahead of this routine | |
bne GiveOEPoints | |
jsr KillEnemyAboveBlock ;if enemy object IS goomba, do this sub | |
GiveOEPoints: | |
lda #$01 ;award 100 points for hitting block beneath enemy | |
jsr SetupFloateyNumber | |
ChkToStunEnemies: | |
cmp #$09 ;perform many comparisons on enemy object identifier | |
bcc SetStun | |
cmp #$11 ;if the enemy object identifier is equal to the values | |
bcs SetStun ;$09, $0e, $0f or $10, it will be modified, and not | |
cmp #$0a ;modified if not any of those values, note that piranha plant will | |
bcc Demote ;always fail this test because A will still have vertical | |
cmp #PiranhaPlant ;coordinate from previous addition, also these comparisons | |
bcc SetStun ;are only necessary if branching from $d7a1 | |
Demote: and #%00000001 ;erase all but LSB, essentially turning enemy object | |
sta Enemy_ID,x ;into green or red koopa troopa to demote them | |
SetStun: lda Enemy_State,x ;load enemy state | |
and #%11110000 ;save high nybble | |
ora #%00000010 | |
sta Enemy_State,x ;set d1 of enemy state | |
dec Enemy_Y_Position,x | |
dec Enemy_Y_Position,x ;subtract two pixels from enemy's vertical position | |
lda Enemy_ID,x | |
cmp #Bloober ;check for bloober object | |
beq SetWYSpd | |
lda #$fd ;set default vertical speed | |
ldy AreaType | |
bne SetNotW ;if area type not water, set as speed, otherwise | |
SetWYSpd: lda #$ff ;change the vertical speed | |
SetNotW: sta Enemy_Y_Speed,x ;set vertical speed now | |
ldy #$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: lda Enemy_ID,x | |
cmp #BulletBill_CannonVar ;check for bullet bill (cannon variant) | |
beq NoCDirF | |
cmp #BulletBill_FrenzyVar ;check for bullet bill (frenzy variant) | |
beq NoCDirF ;branch if either found, direction does not change | |
sty Enemy_MovingDir,x ;store as moving direction | |
NoCDirF: dey ;decrement and use as offset | |
lda EnemyBGCXSpdData,y ;get proper horizontal speed | |
sta Enemy_X_Speed,x ;and store, then leave | |
ExEBGChk: rts | |
;-------------------------------- | |
;$04 - low nybble of vertical coordinate from block buffer routine | |
LandEnemyProperly: | |
lda $04 ;check lower nybble of vertical coordinate saved earlier | |
sec | |
sbc #$08 ;subtract eight pixels | |
cmp #$05 ;used to determine whether enemy landed from falling | |
bcs ChkForRedKoopa ;branch if lower nybble in range of $0d-$0f before subtract | |
lda Enemy_State,x | |
and #%01000000 ;branch if d6 in enemy state is set | |
bne LandEnemyInitState | |
lda Enemy_State,x | |
asl ;branch if d7 in enemy state is not set | |
bcc ChkLandedEnemyState | |
SChkA: jmp DoEnemySideCheck ;if lower nybble < $0d, d7 set but d6 not set, jump here | |
ChkLandedEnemyState: | |
lda Enemy_State,x ;if enemy in normal state, branch back to jump here | |
beq SChkA | |
cmp #$05 ;if in state used by spiny's egg | |
beq ProcEnemyDirection ;then branch elsewhere | |
cmp #$03 ;if already in state used by koopas and buzzy beetles | |
bcs ExSteChk ;or in higher numbered state, branch to leave | |
lda Enemy_State,x ;load enemy state again (why?) | |
cmp #$02 ;if not in $02 state (used by koopas and buzzy beetles) | |
bne ProcEnemyDirection ;then branch elsewhere | |
lda #$10 ;load default timer here | |
ldy Enemy_ID,x ;check enemy identifier for spiny | |
cpy #Spiny | |
bne SetForStn ;branch if not found | |
lda #$00 ;set timer for $00 if spiny | |
SetForStn: sta EnemyIntervalTimer,x ;set timer here | |
lda #$03 ;set state here, apparently used to render | |
sta Enemy_State,x ;upside-down koopas and buzzy beetles | |
jsr EnemyLanding ;then land it properly | |
ExSteChk: rts ;then leave | |
ProcEnemyDirection: | |
lda Enemy_ID,x ;check enemy identifier for goomba | |
cmp #Goomba ;branch if found | |
beq LandEnemyInitState | |
cmp #Spiny ;check for spiny | |
bne InvtD ;branch if not found | |
lda #$01 | |
sta Enemy_MovingDir,x ;send enemy moving to the right by default | |
lda #$08 | |
sta Enemy_X_Speed,x ;set horizontal speed accordingly | |
lda FrameCounter | |
and #%00000111 ;if timed appropriately, spiny will skip over | |
beq LandEnemyInitState ;trying to face the player | |
InvtD: ldy #$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 | |
cmp 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 | |
lda Enemy_State,x | |
and #%10000000 ;if d7 of enemy state is set, branch | |
bne NMovShellFallBit | |
lda #$00 ;otherwise initialize enemy state and leave | |
sta Enemy_State,x ;note this will also turn spiny's egg into spiny | |
rts | |
NMovShellFallBit: | |
lda Enemy_State,x ;nullify d6 of enemy state, save other bits | |
and #%10111111 ;and store, then leave | |
sta Enemy_State,x | |
rts | |
;-------------------------------- | |
ChkForRedKoopa: | |
lda Enemy_ID,x ;check for red koopa troopa $03 | |
cmp #RedKoopa | |
bne Chk2MSBSt ;branch if not found | |
lda Enemy_State,x | |
beq ChkForBump_HammerBroJ ;if enemy found and in normal state, branch | |
Chk2MSBSt: lda Enemy_State,x ;save enemy state into Y | |
tay | |
asl ;check for d7 set | |
bcc GetSteFromD ;branch if not set | |
lda Enemy_State,x | |
ora #%01000000 ;set d6 | |
jmp SetD6Ste ;jump ahead of this part | |
GetSteFromD: lda EnemyBGCStateData,y ;load new enemy state with old as offset | |
SetD6Ste: sta 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: | |
lda Enemy_Y_Position,x ;if enemy within status bar, branch to leave | |
cmp #$20 ;because there's nothing there that impedes movement | |
bcc ExESdeC | |
ldy #$16 ;start by finding block to the left of enemy ($00,$14) | |
lda #$02 ;set value here in what is also used as | |
sta $eb ;OAM data offset | |
SdeCLoop: lda $eb ;check value | |
cmp Enemy_MovingDir,x ;compare value against moving direction | |
bne NextSdeC ;branch if different and do not seek block there | |
lda #$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: dec $eb ;move to the next direction | |
iny | |
cpy #$18 ;increment Y, loop only if Y < $18, thus we check | |
bcc SdeCLoop ;enemy ($00, $14) and ($10, $14) pixel coordinates | |
ExESdeC: rts | |
ChkForBump_HammerBroJ: | |
cpx #$05 ;check if we're on the special use slot | |
beq NoBump ;and if so, branch ahead and do not play sound | |
lda Enemy_State,x ;if enemy state d7 not set, branch | |
asl ;ahead and do not play sound | |
bcc NoBump | |
lda #Sfx_Bump ;otherwise, play bump sound | |
sta Square1SoundQueue ;sound will never be played if branching from ChkForRedKoopa | |
NoBump: lda Enemy_ID,x ;check for hammer bro | |
cmp #$05 | |
bne InvEnemyDir ;branch if not found | |
lda #$00 | |
sta $00 ;initialize value here for bitmask | |
ldy #$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: | |
lda Enemy_X_Position,x ;get distance between enemy object's | |
sec ;horizontal coordinate and the player's | |
sbc Player_X_Position ;horizontal coordinate | |
sta $00 ;and store here | |
lda Enemy_PageLoc,x | |
sbc Player_PageLoc ;subtract borrow, then leave | |
rts | |
;-------------------------------- | |
EnemyLanding: | |
jsr InitVStf ;do something here to vertical speed and something else | |
lda Enemy_Y_Position,x | |
and #%11110000 ;save high nybble of vertical coordinate, and | |
ora #%00001000 ;set d3, then store, probably used to set enemy object | |
sta Enemy_Y_Position,x ;neatly on whatever it's landing on | |
rts | |
SubtEnemyYPos: | |
lda Enemy_Y_Position,x ;add 62 pixels to enemy object's | |
clc ;vertical coordinate | |
adc #$3e | |
cmp #$44 ;compare against a certain range | |
rts ;and leave with flags set for conditional branch | |
EnemyJump: | |
jsr SubtEnemyYPos ;do a sub here | |
bcc DoSide ;if enemy vertical coord + 62 < 68, branch to leave | |
lda Enemy_Y_Speed,x | |
clc ;add two to vertical speed | |
adc #$02 | |
cmp #$03 ;if green paratroopa not falling, branch ahead | |
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 | |
lda #$fd | |
sta 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 | |
cmp #$23 ;check for blank metatile $23 and branch if not found | |
bne UnderHammerBro | |
KillEnemyAboveBlock: | |
jsr ShellOrBlockDefeat ;do this sub to kill enemy | |
lda #$fc ;alter vertical speed of enemy and leave | |
sta Enemy_Y_Speed,x | |
rts | |
UnderHammerBro: | |
lda EnemyFrameTimer,x ;check timer used by hammer bro | |
bne NoUnderHammerBro ;branch if not expired | |
lda Enemy_State,x | |
and #%10001000 ;save d7 and d3 from enemy state, nullify other bits | |
sta Enemy_State,x ;and store | |
jsr EnemyLanding ;modify vertical coordinate, speed and something else | |
jmp DoEnemySideCheck ;then check for horizontal blockage and leave | |
NoUnderHammerBro: | |
lda Enemy_State,x ;if hammer bro is not standing on anything, set d0 | |
ora #$01 ;in the enemy state to indicate jumping or falling, then leave | |
sta Enemy_State,x | |
rts | |
ChkUnderEnemy: | |
lda #$00 ;set flag in A for save vertical coordinate | |
ldy #$15 ;set Y to check the bottom middle (8,18) of enemy object | |
jmp BlockBufferChk_Enemy ;hop to it! | |
ChkForNonSolids: | |
cmp #$26 ;blank metatile used for vines? | |
beq NSFnd | |
cmp #$c2 ;regular coin? | |
beq NSFnd | |
cmp #$c3 ;underwater coin? | |
beq NSFnd | |
cmp #$5f ;hidden coin block? | |
beq NSFnd | |
cmp #$60 ;hidden 1-up block? | |
NSFnd: rts | |
;------------------------------------------------------------------------------------- | |
FireballBGCollision: | |
lda Fireball_Y_Position,x ;check fireball's vertical coordinate | |
cmp #$18 | |
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 | |
lda Fireball_Y_Speed,x ;if fireball's vertical speed set to move upwards, | |
bmi InitFireballExplode ;branch to set exploding bit in fireball's state | |
lda FireballBouncingFlag,x ;if bouncing flag already set, | |
bne InitFireballExplode ;branch to set exploding bit in fireball's state | |
lda #$fd | |
sta Fireball_Y_Speed,x ;otherwise set vertical speed to move upwards (give it bounce) | |
lda #$01 | |
sta FireballBouncingFlag,x ;set bouncing flag | |
lda Fireball_Y_Position,x | |
and #$f8 ;modify vertical coordinate to land it properly | |
sta Fireball_Y_Position,x ;store as new vertical coordinate | |
rts ;leave | |
ClearBounceFlag: | |
lda #$00 | |
sta FireballBouncingFlag,x ;clear bouncing flag by default | |
rts ;leave | |
InitFireballExplode: | |
lda #$80 | |
sta Fireball_State,x ;set exploding flag in fireball's state | |
lda #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 | |
adc #$07 | |
tax | |
ldy #$02 ;set offset for relative coordinates | |
bne FBallB ;unconditional branch | |
GetMiscBoundBox: | |
txa ;add nine bytes to offset | |
clc ;to use in routines as offset for misc object | |
adc #$09 | |
tax | |
ldy #$06 ;set offset for relative coordinates | |
FBallB: jsr BoundingBoxCore ;get bounding box coordinates | |
jmp CheckRightScreenBBox ;jump to handle any offscreen coordinates | |
GetEnemyBoundBox: | |
ldy #$48 ;store bitmask here for now | |
sty $00 | |
ldy #$44 ;store another bitmask here for now and jump | |
jmp GetMaskedOffScrBits | |
SmallPlatformBoundBox: | |
ldy #$08 ;store bitmask here for now | |
sty $00 | |
ldy #$04 ;store another bitmask here for now | |
GetMaskedOffScrBits: | |
lda Enemy_X_Position,x ;get enemy object position relative | |
sec ;to the left side of the screen | |
sbc ScreenLeft_X_Pos | |
sta $01 ;store here | |
lda Enemy_PageLoc,x ;subtract borrow from current page location | |
sbc ScreenLeft_PageLoc ;of left side | |
bmi CMBits ;if enemy object is beyond left edge, branch | |
ora $01 | |
beq CMBits ;if precisely at the left edge, branch | |
ldy $00 ;if to the right of left edge, use value in $00 for A | |
CMBits: tya ;otherwise use contents of Y | |
and Enemy_OffscreenBits ;preserve bitwise whatever's in here | |
sta EnemyOffscrBitsMasked,x ;save masked offscreen bits here | |
bne MoveBoundBoxOffscreen ;if anything set here, branch | |
jmp SetupEOffsetFBBox ;otherwise, do something else | |
LargePlatformBoundBox: | |
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 | |
cmp #$fe ;if completely offscreen, branch to put entire bounding | |
bcs MoveBoundBoxOffscreen ;box offscreen, otherwise start getting coordinates | |
SetupEOffsetFBBox: | |
txa ;add 1 to offset to properly address | |
clc ;the enemy object memory locations | |
adc #$01 | |
tax | |
ldy #$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 | |
lda #$ff | |
sta EnemyBoundingBoxCoord,y ;load value into four locations here and leave | |
sta EnemyBoundingBoxCoord+1,y | |
sta EnemyBoundingBoxCoord+2,y | |
sta EnemyBoundingBoxCoord+3,y | |
rts | |
BoundingBoxCore: | |
stx $00 ;save offset here | |
lda SprObject_Rel_YPos,y ;store object coordinates relative to screen | |
sta $02 ;vertically and horizontally, respectively | |
lda SprObject_Rel_XPos,y | |
sta $01 | |
txa ;multiply offset by four and save to stack | |
asl | |
asl | |
pha | |
tay ;use as offset for Y, X is left alone | |
lda 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 $01 ;add the first number in the bounding box data to the | |
clc ;relative horizontal coordinate using enemy object offset | |
adc BoundBoxCtrlData,x ;and store somewhere using same offset * 4 | |
sta BoundingBox_UL_Corner,y ;store here | |
lda $01 | |
clc | |
adc BoundBoxCtrlData+2,x ;add the third number in the bounding box data to the | |
sta BoundingBox_LR_Corner,y ;relative horizontal coordinate and store | |
inx ;increment both offsets | |
iny | |
lda $02 ;add the second number to the relative vertical coordinate | |
clc ;using incremented offset and store using the other | |
adc BoundBoxCtrlData,x ;incremented offset | |
sta BoundingBox_UL_Corner,y | |
lda $02 | |
clc | |
adc BoundBoxCtrlData+2,x ;add the fourth number to the relative vertical coordinate | |
sta BoundingBox_LR_Corner,y ;and store | |
pla ;get original offset loaded into $00 * y from stack | |
tay ;use as Y | |
ldx $00 ;get original offset and use as X again | |
rts | |
CheckRightScreenBBox: | |
lda ScreenLeft_X_Pos ;add 128 pixels to left side of screen | |
clc ;and store as horizontal coordinate of middle | |
adc #$80 | |
sta $02 | |
lda ScreenLeft_PageLoc ;add carry to page location of left side of screen | |
adc #$00 ;and store as page location of middle | |
sta $01 | |
lda SprObject_X_Position,x ;get horizontal coordinate | |
cmp $02 ;compare against middle horizontal coordinate | |
lda SprObject_PageLoc,x ;get page location | |
sbc $01 ;subtract from middle page location | |
bcc CheckLeftScreenBBox ;if object is on the left side of the screen, branch | |
lda BoundingBox_DR_XPos,y ;check right-side edge of bounding box for offscreen | |
bmi NoOfs ;coordinates, branch if still on the screen | |
lda #$ff ;load offscreen value here to use on one or both horizontal sides | |
ldx BoundingBox_UL_XPos,y ;check left-side edge of bounding box for offscreen | |
bmi SORte ;coordinates, and branch if still on the screen | |
sta BoundingBox_UL_XPos,y ;store offscreen value for left side | |
SORte: sta BoundingBox_DR_XPos,y ;store offscreen value for right side | |
NoOfs: ldx ObjectOffset ;get object offset and leave | |
rts | |
CheckLeftScreenBBox: | |
lda BoundingBox_UL_XPos,y ;check left-side edge of bounding box for offscreen | |
bpl NoOfs2 ;coordinates, and branch if still on the screen | |
cmp #$a0 ;check to see if left-side edge is in the middle of the | |
bcc NoOfs2 ;screen or really offscreen, and branch if still on | |
lda #$00 | |
ldx BoundingBox_DR_XPos,y ;check right-side edge of bounding box for offscreen | |
bpl SOLft ;coordinates, branch if still onscreen | |
sta BoundingBox_DR_XPos,y ;store offscreen value for right side | |
SOLft: sta BoundingBox_UL_XPos,y ;store offscreen value for left side | |
NoOfs2: ldx ObjectOffset ;get object offset and leave | |
rts | |
;------------------------------------------------------------------------------------- | |
;$06 - second object's offset | |
;$07 - counter | |
PlayerCollisionCore: | |
ldx #$00 ;initialize X to use player's bounding box for comparison | |
SprObjectCollisionCore: | |
sty $06 ;save contents of Y here | |
lda #$01 | |
sta $07 ;save value 1 here as counter, compare horizontal coordinates first | |
CollisionCoreLoop: | |
lda BoundingBox_UL_Corner,y ;compare left/top coordinates | |
cmp BoundingBox_UL_Corner,x ;of first and second objects' bounding boxes | |
bcs FirstBoxGreater ;if first left/top => second, branch | |
cmp BoundingBox_LR_Corner,x ;otherwise compare to right/bottom of second | |
bcc SecondBoxVerticalChk ;if first left/top < second right/bottom, branch elsewhere | |
beq CollisionFound ;if somehow equal, collision, thus branch | |
lda BoundingBox_LR_Corner,y ;if somehow greater, check to see if bottom of | |
cmp BoundingBox_UL_Corner,y ;first object's bounding box is greater than its top | |
bcc CollisionFound ;if somehow less, vertical wrap collision, thus branch | |
cmp BoundingBox_UL_Corner,x ;otherwise compare bottom of first bounding box to the top | |
bcs CollisionFound ;of second box, and if equal or greater, collision, thus branch | |
ldy $06 ;otherwise return with carry clear and Y = $0006 | |
rts ;note horizontal wrapping never occurs | |
SecondBoxVerticalChk: | |
lda BoundingBox_LR_Corner,x ;check to see if the vertical bottom of the box | |
cmp BoundingBox_UL_Corner,x ;is greater than the vertical top | |
bcc CollisionFound ;if somehow less, vertical wrap collision, thus branch | |
lda BoundingBox_LR_Corner,y ;otherwise compare horizontal right or vertical bottom | |
cmp BoundingBox_UL_Corner,x ;of first box with horizontal left or vertical top of second box | |
bcs CollisionFound ;if equal or greater, collision, thus branch | |
ldy $06 ;otherwise return with carry clear and Y = $0006 | |
rts | |
FirstBoxGreater: | |
cmp BoundingBox_UL_Corner,x ;compare first and second box horizontal left/vertical top again | |
beq CollisionFound ;if first coordinate = second, collision, thus branch | |
cmp BoundingBox_LR_Corner,x ;if not, compare with second object right or bottom edge | |
bcc CollisionFound ;if left/top of first less than or equal to right/bottom of second | |
beq CollisionFound ;then collision, thus branch | |
cmp BoundingBox_LR_Corner,y ;otherwise check to see if top of first box is greater than bottom | |
bcc NoCollisionFound ;if less than or equal, no collision, branch to end | |
beq NoCollisionFound | |
lda BoundingBox_LR_Corner,y ;otherwise compare bottom of first to top of second | |
cmp BoundingBox_UL_Corner,x ;if bottom of first is greater than top of second, vertical wrap | |
bcs CollisionFound ;collision, and branch, otherwise, proceed onwards here | |
NoCollisionFound: | |
clc ;clear carry, then load value set earlier, then leave | |
ldy $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 | |
dec $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 $06 ;load original value set here earlier, then leave | |
rts | |
;------------------------------------------------------------------------------------- | |
;$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 | |
adc #$01 | |
tax | |
pla ;pull A from stack and jump elsewhere | |
jmp BBChk_E | |
ResidualMiscObjectCode: | |
txa | |
clc ;supposedly used once to set offset for | |
adc #$0d ;miscellaneous objects | |
tax | |
ldy #$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: | |
ldy #$1a ;set offset for block buffer adder data | |
txa | |
clc | |
adc #$07 ;add seven bytes to use | |
tax | |
ResJmpM: lda #$00 ;set A to return vertical coordinate | |
BBChk_E: jsr BlockBufferCollision ;do collision detection subroutine for sprite object | |
ldx ObjectOffset ;get object offset | |
cmp #$00 ;check to see if object bumped into anything | |
rts | |
BlockBufferAdderData: | |
.db $00, $07, $0e | |
BlockBuffer_X_Adder: | |
.db $08, $03, $0c, $02, $02, $0d, $0d, $08 | |
.db $03, $0c, $02, $02, $0d, $0d, $08, $03 | |
.db $0c, $02, $02, $0d, $0d, $08, $00, $10 | |
.db $04, $14, $04, $04 | |
BlockBuffer_Y_Adder: | |
.db $04, $20, $20, $08, $18, $08, $18, $02 | |
.db $20, $20, $08, $18, $08, $18, $12, $20 | |
.db $20, $18, $18, $18, $18, $18, $14, $14 | |
.db $06, $06, $08, $10 | |
BlockBufferColli_Feet: | |
iny ;if branched here, increment to next set of adders | |
BlockBufferColli_Head: | |
lda #$00 ;set flag to return vertical coordinate | |
.db $2c ;BIT instruction opcode | |
BlockBufferColli_Side: | |
lda #$01 ;set flag to return horizontal coordinate | |
ldx #$00 ;set offset for player object | |
BlockBufferCollision: | |
pha ;save contents of A to stack | |
sty $04 ;save contents of Y here | |
lda BlockBuffer_X_Adder,y ;add horizontal coordinate | |
clc ;of object to value obtained using Y as offset | |
adc SprObject_X_Position,x | |
sta $05 ;store here | |
lda SprObject_PageLoc,x | |
adc #$00 ;add carry to page location | |
and #$01 ;get LSB, mask out all other bits | |
lsr ;move to carry | |
ora $05 ;get stored value | |
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 $04 ;get old contents of Y | |
lda SprObject_Y_Position,x ;get vertical coordinate of object | |
clc | |
adc BlockBuffer_Y_Adder,y ;add it to value obtained using Y as offset | |
and #%11110000 ;mask out low nybble | |
sec | |
sbc #$20 ;subtract 32 pixels for the status bar | |
sta $02 ;store result here | |
tay ;use as offset for block buffer | |
lda ($06),y ;check current content of block buffer | |
sta $03 ;and store here | |
ldy $04 ;get old contents of Y again | |
pla ;pull A from stack | |
bne RetXC ;if A = 1, branch | |
lda SprObject_Y_Position,x ;if A = 0, load vertical coordinate | |
jmp RetYC ;and jump | |
RetXC: lda SprObject_X_Position,x ;otherwise load horizontal coordinate | |
RetYC: and #%00001111 ;and mask out high nybble | |
sta $04 ;store masked out result here | |
lda $03 ;get saved content of block buffer | |
rts ;and leave | |
;------------------------------------------------------------------------------------- | |
;unused byte | |
.db $ff | |
;------------------------------------------------------------------------------------- | |
;$00 - offset to vine Y coordinate adder | |
;$02 - offset to sprite data | |
VineYPosAdder: | |
.db $00, $30 | |
DrawVine: | |
sty $00 ;save offset here | |
lda Enemy_Rel_YPos ;get relative vertical coordinate | |
clc | |
adc VineYPosAdder,y ;add value using offset in Y to get value | |
ldx VineObjOffset,y ;get offset to vine | |
ldy Enemy_SprDataOffset,x ;get sprite data offset | |
sty $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 | |
sta Sprite_X_Position,y ;store in first, third and fifth sprites | |
sta Sprite_X_Position+8,y | |
sta Sprite_X_Position+16,y | |
clc | |
adc #$06 ;add six pixels to second, fourth and sixth sprites | |
sta Sprite_X_Position+4,y ;to give characteristic staggered vine shape to | |
sta Sprite_X_Position+12,y ;our vertical stack of sprites | |
sta Sprite_X_Position+20,y | |
lda #%00100001 ;set bg priority and palette attribute bits | |
sta Sprite_Attributes,y ;set in first, third and fifth sprites | |
sta Sprite_Attributes+8,y | |
sta Sprite_Attributes+16,y | |
ora #%01000000 ;additionally, set horizontal flip bit | |
sta Sprite_Attributes+4,y ;for second, fourth and sixth sprites | |
sta Sprite_Attributes+12,y | |
sta Sprite_Attributes+20,y | |
ldx #$05 ;set tiles for six sprites | |
VineTL: lda #$e1 ;set tile number for sprite | |
sta 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 $02 ;get original offset | |
lda $00 ;get offset to vine adding data | |
bne SkpVTop ;if offset not zero, skip this part | |
lda #$e0 | |
sta Sprite_Tilenumber,y ;set other tile number for top of vine | |
SkpVTop: ldx #$00 ;start with the first sprite again | |
ChkFTop: lda VineStart_Y_Position ;get original starting vertical coordinate | |
sec | |
sbc Sprite_Y_Position,y ;subtract top-most sprite's Y coordinate | |
cmp #$64 ;if two coordinates are less than 100/$64 pixels | |
bcc NextVSp ;apart, skip this to leave sprite alone | |
lda #$f8 | |
sta Sprite_Y_Position,y ;otherwise move sprite offscreen | |
NextVSp: iny ;move offset to next OAM data | |
iny | |
iny | |
iny | |
inx ;move onto next sprite | |
cpx #$06 ;do this until all sprites are checked | |
bne ChkFTop | |
ldy $00 ;return offset set earlier | |
rts | |
SixSpriteStacker: | |
ldx #$06 ;do six sprites | |
StkLp: sta Sprite_Data,y ;store X or Y coordinate into OAM data | |
clc | |
adc #$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 $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: | |
ldy Misc_SprDataOffset,x ;get misc object OAM data offset | |
lda TimerControl | |
bne ForceHPose ;if master timer control set, skip this part | |
lda Misc_State,x ;otherwise get hammer's state | |
and #%01111111 ;mask out d7 | |
cmp #$01 ;check to see if set to 1 yet | |
beq GetHPose ;if so, branch | |
ForceHPose: ldx #$00 ;reset offset here | |
beq RenderH ;do unconditional branch to rendering part | |
GetHPose: lda FrameCounter ;get frame counter | |
lsr ;move d3-d2 to d1-d0 | |
lsr | |
and #%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 | |
adc FirstSprYPos,x ;add first sprite vertical adder based on offset | |
sta Sprite_Y_Position,y ;store as sprite Y coordinate for first sprite | |
clc | |
adc SecondSprYPos,x ;add second sprite vertical adder based on offset | |
sta Sprite_Y_Position+4,y ;store as sprite Y coordinate for second sprite | |
lda Misc_Rel_XPos ;get relative horizontal coordinate | |
clc | |
adc FirstSprXPos,x ;add first sprite horizontal adder based on offset | |
sta Sprite_X_Position,y ;store as sprite X coordinate for first sprite | |
clc | |
adc SecondSprXPos,x ;add second sprite horizontal adder based on offset | |
sta Sprite_X_Position+4,y ;store as sprite X coordinate for second sprite | |
lda FirstSprTilenum,x | |
sta Sprite_Tilenumber,y ;get and store tile number of first sprite | |
lda SecondSprTilenum,x | |
sta Sprite_Tilenumber+4,y ;get and store tile number of second sprite | |
lda HammerSprAttrib,x | |
sta Sprite_Attributes,y ;get and store attribute bytes for both | |
sta Sprite_Attributes+4,y ;note in this case they use the same data | |
ldx ObjectOffset ;get misc object offset | |
lda Misc_OffscreenBits | |
and #%11111100 ;check offscreen bits | |
beq NoHOffscr ;if all bits clear, leave object alone | |
lda #$00 | |
sta Misc_State,x ;otherwise nullify misc object state | |
lda #$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: | |
ldy Enemy_SprDataOffset,x ;get sprite data offset for flagpole flag | |
lda Enemy_Rel_XPos ;get relative horizontal coordinate | |
sta Sprite_X_Position,y ;store as X coordinate for first sprite | |
clc | |
adc #$08 ;add eight pixels and store | |
sta Sprite_X_Position+4,y ;as X coordinate for second and third sprites | |
sta Sprite_X_Position+8,y | |
clc | |
adc #$0c ;add twelve more pixels and | |
sta $05 ;store here to be used later by floatey number | |
lda Enemy_Y_Position,x ;get vertical coordinate | |
jsr DumpTwoSpr ;and do sub to dump into first and second sprites | |
adc #$08 ;add eight pixels | |
sta Sprite_Y_Position+8,y ;and store into third sprite | |
lda FlagpoleFNum_Y_Pos ;get vertical coordinate for floatey number | |
sta $02 ;store it here | |
lda #$01 | |
sta $03 ;set value for flip which will not be used, and | |
sta $04 ;attribute byte for floatey number | |
sta Sprite_Attributes,y ;set attribute bytes for all three sprites | |
sta Sprite_Attributes+4,y | |
sta Sprite_Attributes+8,y | |
lda #$7e | |
sta Sprite_Tilenumber,y ;put triangle shaped tile | |
sta Sprite_Tilenumber+8,y ;into first and third sprites | |
lda #$7f | |
sta Sprite_Tilenumber+4,y ;put skull tile into second sprite | |
lda FlagpoleCollisionYPos ;get vertical coordinate at time of collision | |
beq ChkFlagOffscreen ;if zero, branch ahead | |
tya | |
clc ;add 12 bytes to sprite data offset | |
adc #$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 | |
lda FlagpoleScoreNumTiles,x ;get appropriate tile data | |
sta $00 | |
lda FlagpoleScoreNumTiles+1,x | |
jsr DrawOneSpriteRow ;use it to render floatey number | |
ChkFlagOffscreen: | |
ldx ObjectOffset ;get object offset for flag | |
ldy Enemy_SprDataOffset,x ;get OAM data offset | |
lda Enemy_OffscreenBits ;get offscreen bits | |
and #%00001110 ;mask out all but d3-d1 | |
beq ExitDumpSpr ;if none of these bits set, branch to leave | |
;------------------------------------------------------------------------------------- | |
MoveSixSpritesOffscreen: | |
lda #$f8 ;set offscreen coordinate if jumping here | |
DumpSixSpr: | |
sta Sprite_Data+20,y ;dump A contents | |
sta Sprite_Data+16,y ;into third row sprites | |
DumpFourSpr: | |
sta Sprite_Data+12,y ;into second row sprites | |
DumpThreeSpr: | |
sta Sprite_Data+8,y | |
DumpTwoSpr: | |
sta Sprite_Data+4,y ;and into first row sprites | |
sta Sprite_Data,y | |
ExitDumpSpr: | |
rts | |
;------------------------------------------------------------------------------------- | |
DrawLargePlatform: | |
ldy Enemy_SprDataOffset,x ;get OAM data offset | |
sty $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 | |
lda Enemy_Y_Position,x ;get vertical coordinate | |
jsr DumpFourSpr ;dump into first four sprites as Y coordinate | |
ldy AreaType | |
cpy #$03 ;check for castle-type level | |
beq ShrinkPlatform | |
ldy SecondaryHardMode ;check for secondary hard mode flag set | |
beq SetLast2Platform ;branch if not set elsewhere | |
ShrinkPlatform: | |
lda #$f8 ;load offscreen coordinate if flag set or castle-type level | |
SetLast2Platform: | |
ldy Enemy_SprDataOffset,x ;get OAM data offset | |
sta Sprite_Y_Position+16,y ;store vertical coordinate or offscreen | |
sta Sprite_Y_Position+20,y ;coordinate into last two sprites as Y coordinate | |
lda #$5b ;load default tile for platform (girder) | |
ldx CloudTypeOverride | |
beq SetPlatformTilenum ;if cloud level override flag not set, use | |
lda #$75 ;otherwise load other tile for platform (puff) | |
SetPlatformTilenum: | |
ldx ObjectOffset ;get enemy object buffer offset | |
iny ;increment Y for tile offset | |
jsr DumpSixSpr ;dump tile number into all six sprites | |
lda #$02 ;set palette controls | |
iny ;increment Y for sprite attributes | |
jsr DumpSixSpr ;dump attributes into all six sprites | |
inx ;increment X for enemy objects | |
jsr GetXOffscreenBits ;get offscreen bits again | |
dex | |
ldy Enemy_SprDataOffset,x ;get OAM data offset | |
asl ;rotate d7 into carry, save remaining | |
pha ;bits to the stack | |
bcc SChk2 | |
lda #$f8 ;if d7 was set, move first sprite offscreen | |
sta Sprite_Y_Position,y | |
SChk2: pla ;get bits from stack | |
asl ;rotate d6 into carry | |
pha ;save to stack | |
bcc SChk3 | |
lda #$f8 ;if d6 was set, move second sprite offscreen | |
sta Sprite_Y_Position+4,y | |
SChk3: pla ;get bits from stack | |
asl ;rotate d5 into carry | |
pha ;save to stack | |
bcc SChk4 | |
lda #$f8 ;if d5 was set, move third sprite offscreen | |
sta Sprite_Y_Position+8,y | |
SChk4: pla ;get bits from stack | |
asl ;rotate d4 into carry | |
pha ;save to stack | |
bcc SChk5 | |
lda #$f8 ;if d4 was set, move fourth sprite offscreen | |
sta Sprite_Y_Position+12,y | |
SChk5: pla ;get bits from stack | |
asl ;rotate d3 into carry | |
pha ;save to stack | |
bcc SChk6 | |
lda #$f8 ;if d3 was set, move fifth sprite offscreen | |
sta Sprite_Y_Position+16,y | |
SChk6: pla ;get bits from stack | |
asl ;rotate d2 into carry | |
bcc SLChk ;save to stack | |
lda #$f8 | |
sta 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 | |
dec Misc_Y_Position,x ;otherwise, decrement vertical coordinate | |
NotRsNum: lda Misc_Y_Position,x ;get vertical coordinate | |
jsr DumpTwoSpr ;dump into both sprites | |
lda Misc_Rel_XPos ;get relative horizontal coordinate | |
sta Sprite_X_Position,y ;store as X coordinate for first sprite | |
clc | |
adc #$08 ;add eight pixels | |
sta Sprite_X_Position+4,y ;store as X coordinate for second sprite | |
lda #$02 | |
sta Sprite_Attributes,y ;store attribute byte in both sprites | |
sta Sprite_Attributes+4,y | |
lda #$f7 | |
sta Sprite_Tilenumber,y ;put tile numbers into both sprites | |
lda #$fb ;that resemble "200" | |
sta Sprite_Tilenumber+4,y | |
jmp ExJCGfx ;then jump to leave (why not an rts here instead?) | |
JumpingCoinTiles: | |
.db $60, $61, $62, $63 | |
JCoinGfxHandler: | |
ldy Misc_SprDataOffset,x ;get coin/floatey number's OAM data offset | |
lda Misc_State,x ;get state of misc object | |
cmp #$02 ;if 2 or greater, | |
bcs DrawFloateyNumber_Coin ;branch to draw floatey number | |
lda Misc_Y_Position,x ;store vertical coordinate as | |
sta Sprite_Y_Position,y ;Y coordinate for first sprite | |
clc | |
adc #$08 ;add eight pixels | |
sta Sprite_Y_Position+4,y ;store as Y coordinate for second sprite | |
lda Misc_Rel_XPos ;get relative horizontal coordinate | |
sta Sprite_X_Position,y | |
sta 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 | |
and #%00000011 ;mask out d2-d1 | |
tax ;use as graphical offset | |
lda 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 | |
lda #$02 | |
sta Sprite_Attributes,y ;set attribute byte in first sprite | |
lda #$82 | |
sta 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 | |
adc #$08 ;add eight pixels | |
sta $02 ;store result here | |
lda Enemy_Rel_XPos ;get relative horizontal coordinate | |
sta $05 ;store here | |
ldx PowerUpType ;get power-up type | |
lda PowerUpAttributes,x ;get attribute data for power-up type | |
ora Enemy_SprAttrib+5 ;add background priority bit if set | |
sta $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 | |
lda #$01 | |
sta $07 ;set counter here to draw two rows of sprite object | |
sta $03 ;init d1 of flip control | |
PUpDrawLoop: | |
lda PowerUpGfxTable,x ;load left tile of power-up object | |
sta $00 | |
lda PowerUpGfxTable+1,x ;load right tile | |
jsr DrawOneSpriteRow ;branch to draw one row of our power-up object | |
dec $07 ;decrement counter | |
bpl PUpDrawLoop ;branch until two rows are drawn | |
ldy Enemy_SprDataOffset+5 ;get sprite data offset again | |
pla ;pull saved power-up type from the stack | |
beq PUpOfs ;if regular mushroom, branch, do not change colors or flip | |
cmp #$03 | |
beq PUpOfs ;if 1-up mushroom, branch, do not change colors or flip | |
sta $00 ;store power-up type here now | |
lda FrameCounter ;get frame counter | |
lsr ;divide by 2 to change colors every two frames | |
and #%00000011 ;mask out all but d1 and d0 (previously d2 and d1) | |
ora Enemy_SprAttrib+5 ;add background priority bit if any set | |
sta Sprite_Attributes,y ;set as new palette bits for top left and | |
sta Sprite_Attributes+4,y ;top right sprites for fire flower and star | |
ldx $00 | |
dex ;check power-up type for fire flower | |
beq FlipPUpRightSide ;if found, skip this part | |
sta Sprite_Attributes+8,y ;otherwise set new palette bits for bottom left | |
sta Sprite_Attributes+12,y ;and bottom right sprites as well for star only | |
FlipPUpRightSide: | |
lda Sprite_Attributes+4,y | |
ora #%01000000 ;set horizontal flip bit for top right sprite | |
sta Sprite_Attributes+4,y | |
lda Sprite_Attributes+12,y | |
ora #%01000000 ;set horizontal flip bit for bottom right sprite | |
sta 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: | |
lda Enemy_Y_Position,x ;get enemy object vertical position | |
sta $02 | |
lda Enemy_Rel_XPos ;get enemy object horizontal position | |
sta $05 ;relative to screen | |
ldy Enemy_SprDataOffset,x | |
sty $eb ;get sprite data offset | |
lda #$00 | |
sta VerticalFlipFlag ;initialize vertical flip flag by default | |
lda Enemy_MovingDir,x | |
sta $03 ;get enemy object moving direction | |
lda Enemy_SprAttrib,x | |
sta $04 ;get enemy object sprite attributes | |
lda Enemy_ID,x | |
cmp #PiranhaPlant ;is enemy object piranha plant? | |
bne CheckForRetainerObj ;if not, branch | |
ldy PiranhaPlant_Y_Speed,x | |
bmi CheckForRetainerObj ;if piranha plant moving upwards, branch | |
ldy EnemyFrameTimer,x | |
beq CheckForRetainerObj ;if timer for movement expired, branch | |
rts ;if all conditions fail, leave | |
CheckForRetainerObj: | |
lda Enemy_State,x ;store enemy state | |
sta $ed | |
and #%00011111 ;nullify all but 5 LSB and use as Y | |
tay | |
lda Enemy_ID,x ;check for mushroom retainer/princess object | |
cmp #RetainerObject | |
bne CheckForBulletBillCV ;if not found, branch | |
ldy #$00 ;if found, nullify saved state in Y | |
lda #$01 ;set value that will not be used | |
sta $03 | |
lda #$15 ;set value $15 as code for mushroom retainer/princess object | |
CheckForBulletBillCV: | |
cmp #BulletBill_CannonVar ;otherwise check for bullet bill object | |
bne CheckForJumpspring ;if not found, branch again | |
dec $02 ;decrement saved vertical position | |
lda #$03 | |
ldy EnemyFrameTimer,x ;get timer for enemy object | |
beq SBBAt ;if expired, do not set priority bit | |
ora #%00100000 ;otherwise do so | |
SBBAt: sta $04 ;set new sprite attributes | |
ldy #$00 ;nullify saved enemy state both in Y and in | |
sty $ed ;memory location here | |
lda #$08 ;set specific value to unconditionally branch once | |
CheckForJumpspring: | |
cmp #JumpspringObject ;check for jumpspring object | |
bne CheckForPodoboo | |
ldy #$03 ;set enemy state -2 MSB here for jumpspring object | |
ldx JumpspringAnimCtrl ;get current frame number for jumpspring object | |
lda JumpspringFrameOffsets,x ;load data using frame number as offset | |
CheckForPodoboo: | |
sta $ef ;store saved enemy object value here | |
sty $ec ;and Y here (enemy state -2 MSB if not changed) | |
ldx ObjectOffset ;get enemy object offset | |
cmp #$0c ;check for podoboo object | |
bne CheckBowserGfxFlag ;branch if not found | |
lda Enemy_Y_Speed,x ;if moving upwards, branch | |
bmi CheckBowserGfxFlag | |
inc VerticalFlipFlag ;otherwise, set flag for vertical flip | |
CheckBowserGfxFlag: | |
lda BowserGfxFlag ;if not drawing bowser at all, skip to something else | |
beq CheckForGoomba | |
ldy #$16 ;if set to 1, draw bowser's front | |
cmp #$01 | |
beq SBwsrGfxOfs | |
iny ;otherwise draw bowser's rear | |
SBwsrGfxOfs: sty $ef | |
CheckForGoomba: | |
ldy $ef ;check value for goomba object | |
cpy #Goomba | |
bne CheckBowserFront ;branch if not found | |
lda Enemy_State,x | |
cmp #$02 ;check for defeated state | |
bcc GmbaAnim ;if not defeated, go ahead and animate | |
ldx #$04 ;if defeated, write new value here | |
stx $ec | |
GmbaAnim: and #%00100000 ;check for d5 set in enemy object state | |
ora TimerControl ;or timer disable flag set | |
bne CheckBowserFront ;if either condition true, do not animate goomba | |
lda FrameCounter | |
and #%00001000 ;check for every eighth frame | |
bne CheckBowserFront | |
lda $03 | |
eor #%00000011 ;invert bits to flip horizontally every eight frames | |
sta $03 ;leave alone otherwise | |
CheckBowserFront: | |
lda EnemyAttributeData,y ;load sprite attribute using enemy object | |
ora $04 ;as offset, and add to bits already loaded | |
sta $04 | |
lda EnemyGfxTableOffsets,y ;load value based on enemy object as offset | |
tax ;save as X | |
ldy $ec ;get previously saved value | |
lda BowserGfxFlag | |
beq CheckForSpiny ;if not drawing bowser object at all, skip all of this | |
cmp #$01 | |
bne CheckBowserRear ;if not drawing front part, branch to draw the rear part | |
lda BowserBodyControls ;check bowser's body control bits | |
bpl ChkFrontSte ;branch if d7 not set (control's bowser's mouth) | |
ldx #$de ;otherwise load offset for second frame | |
ChkFrontSte: lda $ed ;check saved enemy state | |
and #%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 | |
and #$01 | |
beq ChkRearSte ;branch if d0 not set (control's bowser's feet) | |
ldx #$e4 ;otherwise load offset for second frame | |
ChkRearSte: lda $ed ;check saved enemy state | |
and #%00100000 ;if bowser not defeated, do not set flag | |
beq DrawBowser | |
lda $02 ;subtract 16 pixels from | |
sec ;saved vertical coordinate | |
sbc #$10 | |
sta $02 | |
jmp FlipBowserOver ;jump to set vertical flip flag | |
CheckForSpiny: | |
cpx #$24 ;check if value loaded is for spiny | |
bne CheckForLakitu ;if not found, branch | |
cpy #$05 ;if enemy state set to $05, do this, | |
bne NotEgg ;otherwise branch | |
ldx #$30 ;set to spiny egg offset | |
lda #$02 | |
sta $03 ;set enemy direction to reverse sprites horizontally | |
lda #$05 | |
sta $ec ;set enemy state | |
NotEgg: jmp CheckForHammerBro ;skip a big chunk of this if we found spiny but not in egg | |
CheckForLakitu: | |
cpx #$90 ;check value for lakitu's offset loaded | |
bne CheckUpsideDownShell ;branch if not loaded | |
lda $ed | |
and #%00100000 ;check for d5 set in enemy state | |
bne NoLAFr ;branch if set | |
lda FrenzyEnemyTimer | |
cmp #$10 ;check timer to see if we've reached a certain range | |
bcs NoLAFr ;branch if not | |
ldx #$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 $ef ;check for enemy object => $04 | |
cmp #$04 | |
bcs CheckRightSideUpShell ;branch if true | |
cpy #$02 | |
bcc CheckRightSideUpShell ;branch if enemy state < $02 | |
ldx #$5a ;set for upside-down koopa shell by default | |
ldy $ef | |
cpy #BuzzyBeetle ;check for buzzy beetle object | |
bne CheckRightSideUpShell | |
ldx #$7e ;set for upside-down buzzy beetle shell if found | |
inc $02 ;increment vertical position by one pixel | |
CheckRightSideUpShell: | |
lda $ec ;check for value set here | |
cmp #$04 ;if enemy state < $02, do not change to shell, if | |
bne CheckForHammerBro ;enemy state => $02 but not = $04, leave shell upside-down | |
ldx #$72 ;set right-side up buzzy beetle shell by default | |
inc $02 ;increment saved vertical position by one pixel | |
ldy $ef | |
cpy #BuzzyBeetle ;check for buzzy beetle object | |
beq CheckForDefdGoomba ;branch if found | |
ldx #$66 ;change to right-side up koopa shell if not found | |
inc $02 ;and increment saved vertical position again | |
CheckForDefdGoomba: | |
cpy #Goomba ;check for goomba object (necessary if previously | |
bne CheckForHammerBro ;failed buzzy beetle object test) | |
ldx #$54 ;load for regular goomba | |
lda $ed ;note that this only gets performed if enemy state => $02 | |
and #%00100000 ;check saved enemy state for d5 set | |
bne CheckForHammerBro ;branch if set | |
ldx #$8a ;load offset for defeated goomba | |
dec $02 ;set different value and decrement saved vertical position | |
CheckForHammerBro: | |
ldy ObjectOffset | |
lda $ef ;check for hammer bro object | |
cmp #HammerBro | |
bne CheckForBloober ;branch if not found | |
lda $ed | |
beq CheckToAnimateEnemy ;branch if not in normal enemy state | |
and #%00001000 | |
beq CheckDefeatedState ;if d3 not set, branch further away | |
ldx #$b4 ;otherwise load offset for different frame | |
bne CheckToAnimateEnemy ;unconditional branch | |
CheckForBloober: | |
cpx #$48 ;check for cheep-cheep offset loaded | |
beq CheckToAnimateEnemy ;branch if found | |
lda EnemyIntervalTimer,y | |
cmp #$05 | |
bcs CheckDefeatedState ;branch if some timer is above a certain point | |
cpx #$3c ;check for bloober offset loaded | |
bne CheckToAnimateEnemy ;branch if not found this time | |
cmp #$01 | |
beq CheckDefeatedState ;branch if timer is set to certain point | |
inc $02 ;increment saved vertical coordinate three pixels | |
inc $02 | |
inc $02 | |
jmp CheckAnimationStop ;and do something else | |
CheckToAnimateEnemy: | |
lda $ef ;check for specific enemy objects | |
cmp #Goomba | |
beq CheckDefeatedState ;branch if goomba | |
cmp #$08 | |
beq CheckDefeatedState ;branch if bullet bill (note both variants use $08 here) | |
cmp #Podoboo | |
beq CheckDefeatedState ;branch if podoboo | |
cmp #$18 ;branch if => $18 | |
bcs CheckDefeatedState | |
ldy #$00 | |
cmp #$15 ;check for mushroom retainer/princess object | |
bne CheckForSecondFrame ;which uses different code here, branch if not found | |
iny ;residual instruction | |
lda WorldNumber ;are we on world 8? | |
cmp #World8 | |
bcs CheckDefeatedState ;if so, leave the offset alone (use princess) | |
ldx #$a2 ;otherwise, set for mushroom retainer object instead | |
lda #$03 ;set alternate state here | |
sta $ec | |
bne CheckDefeatedState ;unconditional branch | |
CheckForSecondFrame: | |
lda FrameCounter ;load frame counter | |
and EnemyAnimTimingBMask,y ;mask it (partly residual, one byte not ever used) | |
bne CheckDefeatedState ;branch if timing is off | |
CheckAnimationStop: | |
lda $ed ;check saved enemy state | |
and #%10100000 ;for d7 or d5, or check for timers stopped | |
ora TimerControl | |
bne CheckDefeatedState ;if either condition true, branch | |
txa | |
clc | |
adc #$06 ;add $06 to current enemy offset | |
tax ;to animate various enemy objects | |
CheckDefeatedState: | |
lda $ed ;check saved enemy state | |
and #%00100000 ;for d5 set | |
beq DrawEnemyObject ;branch if not set | |
lda $ef | |
cmp #$04 ;check for saved enemy object => $04 | |
bcc DrawEnemyObject ;branch if less | |
ldy #$01 | |
sty VerticalFlipFlag ;set vertical flip flag | |
dey | |
sty $ec ;init saved value here | |
DrawEnemyObject: | |
ldy $eb ;load sprite data offset | |
jsr DrawEnemyObjRow ;draw six tiles of data | |
jsr DrawEnemyObjRow ;into sprite data | |
jsr DrawEnemyObjRow | |
ldx ObjectOffset ;get enemy object offset | |
ldy Enemy_SprDataOffset,x ;get sprite data offset | |
lda $ef | |
cmp #$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 | |
beq CheckForESymmetry ;branch if not | |
lda Sprite_Attributes,y ;get attributes of first sprite we dealt with | |
ora #%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 $ef | |
cmp #HammerBro ;check saved enemy object for hammer bro | |
beq FlipEnemyVertically | |
cmp #Lakitu ;check saved enemy object for lakitu | |
beq FlipEnemyVertically ;branch for hammer bro or lakitu | |
cmp #$15 | |
bcs FlipEnemyVertically ;also branch if enemy object => $15 | |
txa | |
clc | |
adc #$08 ;if not selected objects or => $15, set | |
tax ;offset in X for next row | |
FlipEnemyVertically: | |
lda Sprite_Tilenumber,x ;load first or second row tiles | |
pha ;and save tiles to the stack | |
lda Sprite_Tilenumber+4,x | |
pha | |
lda Sprite_Tilenumber+16,y ;exchange third row tiles | |
sta Sprite_Tilenumber,x ;with first or second row tiles | |
lda Sprite_Tilenumber+20,y | |
sta Sprite_Tilenumber+4,x | |
pla ;pull first or second row tiles from stack | |
sta Sprite_Tilenumber+20,y ;and save in third row | |
pla | |
sta Sprite_Tilenumber+16,y | |
CheckForESymmetry: | |
lda BowserGfxFlag ;are we drawing bowser at all? | |
bne SkipToOffScrChk ;branch if so | |
lda $ef | |
ldx $ec ;get alternate enemy state | |
cmp #$05 ;check for hammer bro object | |
bne ContES | |
jmp SprObjectOffscrChk ;jump if found | |
ContES: cmp #Bloober ;check for bloober object | |
beq MirrorEnemyGfx | |
cmp #PiranhaPlant ;check for piranha plant object | |
beq MirrorEnemyGfx | |
cmp #Podoboo ;check for podoboo object | |
beq MirrorEnemyGfx ;branch if either of three are found | |
cmp #Spiny ;check for spiny object | |
bne ESRtnr ;branch closer if not found | |
cpx #$05 ;check spiny's state | |
bne CheckToMirrorLakitu ;branch if not an egg, otherwise | |
ESRtnr: cmp #$15 ;check for princess/mushroom retainer object | |
bne SpnySC | |
lda #$42 ;set horizontal flip on bottom right sprite | |
sta Sprite_Attributes+20,y ;note that palette bits were already set earlier | |
SpnySC: cpx #$02 ;if alternate enemy state set to 1 or 0, branch | |
bcc CheckToMirrorLakitu | |
MirrorEnemyGfx: | |
lda BowserGfxFlag ;if enemy object is bowser, skip all of this | |
bne CheckToMirrorLakitu | |
lda Sprite_Attributes,y ;load attribute bits of first sprite | |
and #%10100011 | |
sta Sprite_Attributes,y ;save vertical flip, priority, and palette bits | |
sta Sprite_Attributes+8,y ;in left sprite column of enemy object OAM data | |
sta Sprite_Attributes+16,y | |
ora #%01000000 ;set horizontal flip | |
cpx #$05 ;check for state used by spiny's egg | |
bne EggExc ;if alternate state not set to $05, branch | |
ora #%10000000 ;otherwise set vertical flip | |
EggExc: sta Sprite_Attributes+4,y ;set bits of right sprite column | |
sta Sprite_Attributes+12,y ;of enemy object sprite data | |
sta Sprite_Attributes+20,y | |
cpx #$04 ;check alternate enemy state | |
bne CheckToMirrorLakitu ;branch if not $04 | |
lda Sprite_Attributes+8,y ;get second row left sprite attributes | |
ora #%10000000 | |
sta Sprite_Attributes+8,y ;store bits with vertical flip in | |
sta Sprite_Attributes+16,y ;second and third row left sprites | |
ora #%01000000 | |
sta Sprite_Attributes+12,y ;store with horizontal and vertical flip in | |
sta Sprite_Attributes+20,y ;second and third row right sprites | |
CheckToMirrorLakitu: | |
lda $ef ;check for lakitu enemy object | |
cmp #Lakitu | |
bne CheckToMirrorJSpring ;branch if not found | |
lda VerticalFlipFlag | |
bne NVFLak ;branch if vertical flip flag not set | |
lda Sprite_Attributes+16,y ;save vertical flip and palette bits | |
and #%10000001 ;in third row left sprite | |
sta Sprite_Attributes+16,y | |
lda Sprite_Attributes+20,y ;set horizontal flip and palette bits | |
ora #%01000001 ;in third row right sprite | |
sta Sprite_Attributes+20,y | |
ldx FrenzyEnemyTimer ;check timer | |
cpx #$10 | |
bcs SprObjectOffscrChk ;branch if timer has not reached a certain range | |
sta Sprite_Attributes+12,y ;otherwise set same for second row right sprite | |
and #%10000001 | |
sta Sprite_Attributes+8,y ;preserve vertical flip and palette bits for left sprite | |
bcc SprObjectOffscrChk ;unconditional branch | |
NVFLak: lda Sprite_Attributes,y ;get first row left sprite attributes | |
and #%10000001 | |
sta Sprite_Attributes,y ;save vertical flip and palette bits | |
lda Sprite_Attributes+4,y ;get first row right sprite attributes | |
ora #%01000001 ;set horizontal flip and palette bits | |
sta Sprite_Attributes+4,y ;note that vertical flip is left as-is | |
CheckToMirrorJSpring: | |
lda $ef ;check for jumpspring object (any frame) | |
cmp #$18 | |
bcc SprObjectOffscrChk ;branch if not jumpspring object at all | |
lda #$82 | |
sta Sprite_Attributes+8,y ;set vertical flip and palette bits of | |
sta Sprite_Attributes+16,y ;second and third row left sprites | |
ora #%01000000 | |
sta Sprite_Attributes+12,y ;set, in addition to those, horizontal flip | |
sta Sprite_Attributes+20,y ;for second and third row right sprites | |
SprObjectOffscrChk: | |
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 | |
lda #$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 | |
lda #$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 | |
lda #$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 | |
lda #$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) | |
lda Enemy_ID,x | |
cmp #Podoboo ;check enemy identifier for podoboo | |
beq ExEGHandler ;skip this part if found, we do not want to erase podoboo! | |
lda Enemy_Y_HighPos,x ;check high byte of vertical position | |
cmp #$02 ;if not yet past the bottom of the screen, branch | |
bne ExEGHandler | |
jsr EraseEnemyObject ;what it says | |
ExEGHandler: | |
rts | |
DrawEnemyObjRow: | |
lda EnemyGraphicsTable,x ;load two tiles of enemy graphics | |
sta $00 | |
lda EnemyGraphicsTable+1,x | |
DrawOneSpriteRow: | |
sta $01 | |
jmp DrawSpriteObject ;draw them | |
MoveESprRowOffscreen: | |
clc ;add A to enemy object OAM data offset | |
adc Enemy_SprDataOffset,x | |
tay ;use as offset | |
lda #$f8 | |
jmp DumpTwoSpr ;move first row of sprites offscreen | |
MoveESprColOffscreen: | |
clc ;add A to enemy object OAM data offset | |
adc Enemy_SprDataOffset,x | |
tay ;use as offset | |
jsr MoveColOffscreen ;move first and second row sprites in column offscreen | |
sta 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 $02 ;store here | |
lda Block_Rel_XPos ;get relative horizontal coordinate of block object | |
sta $05 ;store here | |
lda #$03 | |
sta $04 ;set attribute byte here | |
lsr | |
sta $03 ;set horizontal flip bit here (will not be used) | |
ldy Block_SprDataOffset,x ;get sprite data offset | |
ldx #$00 ;reset X for use as offset to tile data | |
DBlkLoop: lda DefaultBlockObjTiles,x ;get left tile number | |
sta $00 ;set here | |
lda DefaultBlockObjTiles+1,x ;get right tile number | |
jsr DrawOneSpriteRow ;do sub to write tile numbers to first row of sprites | |
cpx #$04 ;check incremented offset | |
bne DBlkLoop ;and loop back until all four sprites are done | |
ldx ObjectOffset ;get block object offset | |
ldy Block_SprDataOffset,x ;get sprite data offset | |
lda AreaType | |
cmp #$01 ;check for ground level type area | |
beq ChkRep ;if found, branch to next part | |
lda #$86 | |
sta Sprite_Tilenumber,y ;otherwise remove brick tiles with lines | |
sta Sprite_Tilenumber+4,y ;and replace then with lineless brick tiles | |
ChkRep: lda Block_Metatile,x ;check replacement metatile | |
cmp #$c4 ;if not used block metatile, then | |
bne BlkOffscr ;branch ahead to use current graphics | |
lda #$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 | |
lda #$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 | |
sta Sprite_Attributes,y ;store attribute byte as-is in first sprite | |
ora #%01000000 | |
sta Sprite_Attributes+4,y ;set horizontal flip bit for second sprite | |
ora #%10000000 | |
sta Sprite_Attributes+12,y ;set both flip bits for fourth sprite | |
and #%10000011 | |
sta 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 | |
and #%00000100 ;check to see if d2 in offscreen bits are set | |
beq PullOfsB ;if not set, branch, otherwise move sprites offscreen | |
lda #$f8 ;move offscreen two OAMs | |
sta Sprite_Y_Position+4,y ;on the right side | |
sta Sprite_Y_Position+12,y | |
PullOfsB: pla ;pull offscreen bits from stack | |
ChkLeftCo: and #%00001000 ;check to see if d3 in offscreen bits are set | |
beq ExDBlk ;if not set, branch, otherwise move sprites offscreen | |
MoveColOffscreen: | |
lda #$f8 ;move offscreen two OAMs | |
sta Sprite_Y_Position,y ;on the left side (or two rows of enemy on either side | |
sta 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: | |
lda #$02 ;set palette bits here | |
sta $00 | |
lda #$75 ;set tile number for ball (something residual, likely) | |
ldy GameEngineSubroutine | |
cpy #$05 ;if end-of-level routine running, | |
beq DChunks ;use palette and tile number assigned | |
lda #$03 ;otherwise set different palette bits | |
sta $00 | |
lda #$84 ;and set tile number for brick chunks | |
DChunks: ldy 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 | |
and #$c0 ;get what was originally d3-d2 of low nybble | |
ora $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 | |
sta Sprite_X_Position,y ;save into X coordinate of first sprite | |
lda Block_Orig_XPos,x ;get original horizontal coordinate | |
sec | |
sbc ScreenLeft_X_Pos ;subtract coordinate of left side from original coordinate | |
sta $00 ;store result as relative horizontal coordinate of original | |
sec | |
sbc Block_Rel_XPos ;get difference of relative positions of original - current | |
adc $00 ;add original relative position to result | |
adc #$06 ;plus 6 pixels to position second brick chunk correctly | |
sta 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 | |
sta Sprite_Y_Position+8,y | |
sta 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 | |
sta Sprite_X_Position+8,y ;save into X coordinate of third sprite | |
lda $00 ;use original relative horizontal position | |
sec | |
sbc Block_Rel_XPos+1 ;get difference of relative positions of original - current | |
adc $00 ;add original relative position to result | |
adc #$06 ;plus 6 pixels to position fourth brick chunk correctly | |
sta 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 | |
lda #$f8 | |
jsr DumpTwoSpr ;otherwise move top sprites offscreen | |
ChnkOfs: lda $00 ;if relative position on left side of screen, | |
bpl ExBCDr ;go ahead and leave | |
lda Sprite_X_Position,y ;otherwise compare left-side X coordinate | |
cmp Sprite_X_Position+4,y ;to right-side X coordinate | |
bcc ExBCDr ;branch to leave if less | |
lda #$f8 ;otherwise move right half of sprites offscreen | |
sta Sprite_Y_Position+4,y | |
sta Sprite_Y_Position+12,y | |
ExBCDr: rts ;leave | |
;------------------------------------------------------------------------------------- | |
DrawFireball: | |
ldy FBall_SprDataOffset,x ;get fireball's sprite data offset | |
lda Fireball_Rel_YPos ;get relative vertical coordinate | |
sta Sprite_Y_Position,y ;store as sprite Y coordinate | |
lda Fireball_Rel_XPos ;get relative horizontal coordinate | |
sta 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 | |
and #$01 ;mask out all but last bit | |
eor #$64 ;set either tile $64 or $65 as fireball tile | |
sta Sprite_Tilenumber,y ;thus tile changes every four frames | |
pla ;get from stack | |
lsr ;divide by four again | |
lsr | |
lda #$02 ;load value $02 to set palette in attrib byte | |
bcc FireA ;if last bit shifted out was not set, skip this | |
ora #%11000000 ;otherwise flip both ways every eight frames | |
FireA: sta Sprite_Attributes,y ;store attribute byte and leave | |
rts | |
;------------------------------------------------------------------------------------- | |
ExplosionTiles: | |
.db $68, $67, $66 | |
DrawExplosion_Fireball: | |
ldy Alt_SprDataOffset,x ;get OAM data offset of alternate sort for fireball's explosion | |
lda Fireball_State,x ;load fireball state | |
inc Fireball_State,x ;increment state for next frame | |
lsr ;divide by 2 | |
and #%00000111 ;mask out all but d3-d1 | |
cmp #$03 ;check to see if time to kill fireball | |
bcs KillFireBall ;branch if so, otherwise continue to draw explosion | |
DrawExplosion_Fireworks: | |
tax ;use whatever's in A for offset | |
lda 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 | |
sec ;subtract four pixels vertically | |
sbc #$04 ;for first and third sprites | |
sta Sprite_Y_Position,y | |
sta Sprite_Y_Position+8,y | |
clc ;add eight pixels vertically | |
adc #$08 ;for second and fourth sprites | |
sta Sprite_Y_Position+4,y | |
sta Sprite_Y_Position+12,y | |
lda Fireball_Rel_XPos ;get relative horizontal coordinate | |
sec ;subtract four pixels horizontally | |
sbc #$04 ;for first and second sprites | |
sta Sprite_X_Position,y | |
sta Sprite_X_Position+4,y | |
clc ;add eight pixels horizontally | |
adc #$08 ;for third and fourth sprites | |
sta Sprite_X_Position+8,y | |
sta Sprite_X_Position+12,y | |
lda #$02 ;set palette attributes for all sprites, but | |
sta Sprite_Attributes,y ;set no flip at all for first sprite | |
lda #$82 | |
sta Sprite_Attributes+4,y ;set vertical flip for second sprite | |
lda #$42 | |
sta Sprite_Attributes+8,y ;set horizontal flip for third sprite | |
lda #$c2 | |
sta Sprite_Attributes+12,y ;set both flips for fourth sprite | |
rts ;we are done | |
KillFireBall: | |
lda #$00 ;clear fireball state to kill it | |
sta Fireball_State,x | |
rts | |
;------------------------------------------------------------------------------------- | |
DrawSmallPlatform: | |
ldy Enemy_SprDataOffset,x ;get OAM data offset | |
lda #$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 | |
lda #$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 | |
sta Sprite_X_Position,y | |
sta Sprite_X_Position+12,y ;dump as X coordinate into first and fourth sprites | |
clc | |
adc #$08 ;add eight pixels | |
sta Sprite_X_Position+4,y ;dump into second and fifth sprites | |
sta Sprite_X_Position+16,y | |
clc | |
adc #$08 ;add eight more pixels | |
sta Sprite_X_Position+8,y ;dump into third and sixth sprites | |
sta Sprite_X_Position+20,y | |
lda Enemy_Y_Position,x ;get vertical coordinate | |
tax | |
pha ;save to stack | |
cpx #$20 ;if vertical coordinate below status bar, | |
bcs TopSP ;do not mess with it | |
lda #$f8 ;otherwise move first three sprites offscreen | |
TopSP: jsr DumpThreeSpr ;dump vertical coordinate into Y coordinates | |
pla ;pull from stack | |
clc | |
adc #$80 ;add 128 pixels | |
tax | |
cpx #$20 ;if below status bar (taking wrap into account) | |
bcs BotSP ;then do not change altered coordinate | |
lda #$f8 ;otherwise move last three sprites offscreen | |
BotSP: sta Sprite_Y_Position+12,y ;dump vertical coordinate + 128 pixels | |
sta Sprite_Y_Position+16,y ;into Y coordinates | |
sta Sprite_Y_Position+20,y | |
lda Enemy_OffscreenBits ;get offscreen bits | |
pha ;save to stack | |
and #%00001000 ;check d3 | |
beq SOfs | |
lda #$f8 ;if d3 was set, move first and | |
sta Sprite_Y_Position,y ;fourth sprites offscreen | |
sta Sprite_Y_Position+12,y | |
SOfs: pla ;move out and back into stack | |
pha | |
and #%00000100 ;check d2 | |
beq SOfs2 | |
lda #$f8 ;if d2 was set, move second and | |
sta Sprite_Y_Position+4,y ;fifth sprites offscreen | |
sta Sprite_Y_Position+16,y | |
SOfs2: pla ;get from stack | |
and #%00000010 ;check d1 | |
beq ExSPl | |
lda #$f8 ;if d1 was set, move third and | |
sta Sprite_Y_Position+8,y ;sixth sprites offscreen | |
sta 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 | |
and #%00001000 | |
bne ExDBub ;if bit set, branch to leave | |
ldy Bubble_SprDataOffset,x ;get air bubble's OAM data offset | |
lda Bubble_Rel_XPos ;get relative horizontal coordinate | |
sta Sprite_X_Position,y ;store as X coordinate here | |
lda Bubble_Rel_YPos ;get relative vertical coordinate | |
sta Sprite_Y_Position,y ;store as Y coordinate here | |
lda #$74 | |
sta Sprite_Tilenumber,y ;put air bubble tile into OAM data | |
lda #$02 | |
sta 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 | |
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, | |
cmp #$0b ;branch ahead to some other part | |
beq PlayerKilled | |
lda PlayerChangeSizeFlag ;if grow/shrink flag set | |
bne DoChangeSize ;then branch to some other code | |
ldy SwimmingFlag ;if swimming flag set, branch to | |
beq FindPlayerAction ;different part, do not return | |
lda Player_State | |
cmp #$00 ;if player status normal, | |
beq FindPlayerAction ;branch and do not return | |
jsr FindPlayerAction ;otherwise jump and return | |
lda FrameCounter | |
and #%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 | |
beq BigKTS ;if big, use first tile | |
lda Sprite_Tilenumber+24,y ;check tile number of seventh/eighth sprite | |
cmp 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: lda SwimKickTileNum,x ;overwrite tile number in sprite 7/8 | |
sta 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: | |
ldy #$0e ;load offset for player killed | |
lda PlayerGfxTblOffsets,y ;get offset to graphics table | |
PlayerGfxProcessing: | |
sta PlayerGfxOffset ;store offset to graphics table here | |
lda #$04 | |
jsr RenderPlayerSub ;draw player based on offset loaded | |
jsr ChkForPlayerAttrib ;set horizontal flip bits as necessary | |
lda FireballThrowingTimer | |
beq PlayerOffscreenChk ;if fireball throw timer not set, skip to the end | |
ldy #$00 ;set value to initialize by default | |
lda PlayerAnimTimer ;get animation frame timer | |
cmp FireballThrowingTimer ;compare to fireball throw timer | |
sty FireballThrowingTimer ;initialize fireball throw timer | |
bcs PlayerOffscreenChk ;if animation frame timer => fireball throw timer skip to end | |
sta FireballThrowingTimer ;otherwise store animation timer into fireball throw timer | |
ldy #$07 ;load offset for throwing | |
lda PlayerGfxTblOffsets,y ;get offset to graphics table | |
sta PlayerGfxOffset ;store it for use later | |
ldy #$04 ;set to update four sprite rows by default | |
lda Player_X_Speed | |
ora 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 $00 ;store here | |
ldx #$03 ;check all four rows of player sprites | |
lda Player_SprDataOffset ;get player's sprite data offset | |
clc | |
adc #$18 ;add 24 bytes to start at bottom row | |
tay ;set as offset here | |
PROfsLoop: lda #$f8 ;load offscreen Y coordinate just in case | |
lsr $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 | |
sec ;subtract eight bytes to do | |
sbc #$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: | |
ldx #$05 ;store data into zero page memory | |
PIntLoop: lda IntermediatePlayerData,x ;load data to display player as he always | |
sta $02,x ;appears on world/lives display | |
dex | |
bpl PIntLoop ;do this until all data is loaded | |
ldx #$b8 ;load offset for small standing | |
ldy #$04 ;load sprite data offset | |
jsr DrawPlayerLoop ;draw player accordingly | |
lda Sprite_Attributes+36 ;get empty sprite attributes | |
ora #%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 $07 ;store number of rows of sprites to draw | |
lda Player_Rel_XPos | |
sta Player_Pos_ForScroll ;store player's relative horizontal position | |
sta $05 ;store it here also | |
lda Player_Rel_YPos | |
sta $02 ;store player's vertical position | |
lda PlayerFacingDir | |
sta $03 ;store player's facing direction | |
lda Player_SprAttrib | |
sta $04 ;store player's sprite attributes | |
ldx PlayerGfxOffset ;load graphics table offset | |
ldy Player_SprDataOffset ;get player's sprite data offset | |
DrawPlayerLoop: | |
lda PlayerGraphicsTable,x ;load player's left side | |
sta $00 | |
lda PlayerGraphicsTable+1,x ;now load right side | |
jsr DrawOneSpriteRow | |
dec $07 ;decrement rows of sprites to draw | |
bne DrawPlayerLoop ;do this until all rows are drawn | |
rts | |
ProcessPlayerAction: | |
lda Player_State ;get player's state | |
cmp #$03 | |
beq ActionClimbing ;if climbing, branch here | |
cmp #$02 | |
beq ActionFalling ;if falling, branch here | |
cmp #$01 | |
bne ProcOnGroundActs ;if not jumping, branch here | |
lda SwimmingFlag | |
bne ActionSwimming ;if swimming flag set, branch elsewhere | |
ldy #$06 ;load offset for crouching | |
lda CrouchingFlag ;get crouching flag | |
bne NonAnimatedActs ;if set, branch to get offset for graphics table | |
ldy #$00 ;otherwise load offset for jumping | |
jmp NonAnimatedActs ;go to get offset to graphics table | |
ProcOnGroundActs: | |
ldy #$06 ;load offset for crouching | |
lda CrouchingFlag ;get crouching flag | |
bne NonAnimatedActs ;if set, branch to get offset for graphics table | |
ldy #$02 ;load offset for standing | |
lda Player_X_Speed ;check player's horizontal speed | |
ora 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 | |
cmp #$09 | |
bcc ActionWalkRun ;if less than a certain amount, branch, too slow to skid | |
lda Player_MovingDir ;otherwise check to see if moving direction | |
and 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 | |
lda #$00 | |
sta PlayerAnimCtrl ;initialize animation frame control | |
lda PlayerGfxTblOffsets,y ;load offset to graphics table using size as offset | |
rts | |
ActionFalling: | |
ldy #$04 ;load offset for walking/running | |
jsr GetGfxOffsetAdder ;get offset to graphics table | |
jmp GetCurrentAnimOffset ;execute instructions for falling state | |
ActionWalkRun: | |
ldy #$04 ;load offset for walking/running | |
jsr GetGfxOffsetAdder ;get offset to graphics table | |
jmp FourFrameExtent ;execute instructions for normal state | |
ActionClimbing: | |
ldy #$05 ;load offset for climbing | |
lda Player_Y_Speed ;check player's vertical speed | |
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: | |
ldy #$01 ;load offset for swimming | |
jsr GetGfxOffsetAdder | |
lda JumpSwimTimer ;check jump/swim timer | |
ora 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: | |
lda #$03 ;load upper extent for frame control | |
jmp AnimationControl ;jump to get offset and animate player object | |
ThreeFrameExtent: | |
lda #$02 ;load upper extent for frame control for climbing | |
AnimationControl: | |
sta $00 ;store upper extent here | |
jsr GetCurrentAnimOffset ;get proper offset to graphics table | |
pha ;save offset to stack | |
lda PlayerAnimTimer ;load animation frame timer | |
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 | |
adc #$01 | |
cmp $00 ;compare to upper extent | |
bcc SetAnimC ;if frame control + 1 < upper extent, use as next | |
lda #$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 | |
beq SzOfs ;if player big, use current offset as-is | |
tya ;for big player | |
clc ;otherwise add eight bytes to offset | |
adc #$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 | |
and #%00000011 ;get frame counter and execute this code every | |
bne GorSLog ;fourth frame, otherwise branch ahead | |
iny ;increment frame control | |
cpy #$0a ;check for preset upper extent | |
bcc CSzNext ;if not there yet, skip ahead to use | |
ldy #$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 | |
bne ShrinkPlayer ;if player small, skip ahead to next part | |
lda ChangeSizeOffsetAdder,y ;get offset adder based on frame control as offset | |
ldy #$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 | |
adc 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 | |
adc #$0a ;this thing apparently uses two of the swimming frames | |
tax ;to draw the player shrinking | |
ldy #$09 ;load offset for small player swimming | |
lda ChangeSizeOffsetAdder,x ;get what would normally be offset adder | |
bne ShrPlF ;and branch to use offset if nonzero | |
ldy #$01 ;otherwise load offset for big player swimming | |
ShrPlF: lda PlayerGfxTblOffsets,y ;get offset to graphics table based on offset loaded | |
rts ;and leave | |
ChkForPlayerAttrib: | |
ldy Player_SprDataOffset ;get sprite data offset | |
lda GameEngineSubroutine | |
cmp #$0b ;if executing specific game engine routine, | |
beq KilledAtt ;branch to change third and fourth row OAM attributes | |
lda PlayerGfxOffset ;get graphics table offset | |
cmp #$50 | |
beq C_S_IGAtt ;if crouch offset, either standing offset, | |
cmp #$b8 ;or intermediate growing offset, | |
beq C_S_IGAtt ;go ahead and execute code to change | |
cmp #$c0 ;fourth row OAM attributes only | |
beq C_S_IGAtt | |
cmp #$c8 | |
bne ExPlyrAt ;if none of these, branch to leave | |
KilledAtt: lda Sprite_Attributes+16,y | |
and #%00111111 ;mask out horizontal and vertical flip bits | |
sta Sprite_Attributes+16,y ;for third row sprites and save | |
lda Sprite_Attributes+20,y | |
and #%00111111 | |
ora #%01000000 ;set horizontal flip bit for second | |
sta Sprite_Attributes+20,y ;sprite in the third row | |
C_S_IGAtt: lda Sprite_Attributes+24,y | |
and #%00111111 ;mask out horizontal and vertical flip bits | |
sta Sprite_Attributes+24,y ;for fourth row sprites and save | |
lda Sprite_Attributes+28,y | |
and #%00111111 | |
ora #%01000000 ;set horizontal flip bit for second | |
sta Sprite_Attributes+28,y ;sprite in the fourth row | |
ExPlyrAt: rts ;leave | |
;------------------------------------------------------------------------------------- | |
;$00 - used in adding to get proper offset | |
RelativePlayerPosition: | |
ldx #$00 ;set offsets for relative cooordinates | |
ldy #$00 ;routine to correspond to player object | |
jmp RelWOfs ;get the coordinates | |
RelativeBubblePosition: | |
ldy #$01 ;set for air bubble offsets | |
jsr GetProperObjOffset ;modify X to get proper air bubble offset | |
ldy #$03 | |
jmp RelWOfs ;get the coordinates | |
RelativeFireballPosition: | |
ldy #$00 ;set for fireball offsets | |
jsr GetProperObjOffset ;modify X to get proper fireball offset | |
ldy #$02 | |
RelWOfs: jsr GetObjRelativePosition ;get the coordinates | |
ldx ObjectOffset ;return original offset | |
rts ;leave | |
RelativeMiscPosition: | |
ldy #$02 ;set for misc object offsets | |
jsr GetProperObjOffset ;modify X to get proper misc object offset | |
ldy #$06 | |
jmp RelWOfs ;get the coordinates | |
RelativeEnemyPosition: | |
lda #$01 ;get coordinates of enemy object | |
ldy #$01 ;relative to the screen | |
jmp VariableObjOfsRelPos | |
RelativeBlockPosition: | |
lda #$09 ;get coordinates of one block object | |
ldy #$04 ;relative to the screen | |
jsr VariableObjOfsRelPos | |
inx ;adjust offset for other block object if any | |
inx | |
lda #$09 | |
iny ;adjust other and get coordinates for other one | |
VariableObjOfsRelPos: | |
stx $00 ;store value to add to A here | |
clc | |
adc $00 ;add A to value stored | |
tax ;use as enemy offset | |
jsr GetObjRelativePosition | |
ldx ObjectOffset ;reload old object offset and leave | |
rts | |
GetObjRelativePosition: | |
lda SprObject_Y_Position,x ;load vertical coordinate low | |
sta SprObject_Rel_YPos,y ;store here | |
lda SprObject_X_Position,x ;load horizontal coordinate | |
sec ;subtract left edge coordinate | |
sbc ScreenLeft_X_Pos | |
sta SprObject_Rel_XPos,y ;store result here | |
rts | |
;------------------------------------------------------------------------------------- | |
;$00 - used as temp variable to hold offscreen bits | |
GetPlayerOffscreenBits: | |
ldx #$00 ;set offsets for player-specific variables | |
ldy #$00 ;and get offscreen information about player | |
jmp GetOffScreenBitsSet | |
GetFireballOffscreenBits: | |
ldy #$00 ;set for fireball offsets | |
jsr GetProperObjOffset ;modify X to get proper fireball offset | |
ldy #$02 ;set other offset for fireball's offscreen bits | |
jmp GetOffScreenBitsSet ;and get offscreen information about fireball | |
GetBubbleOffscreenBits: | |
ldy #$01 ;set for air bubble offsets | |
jsr GetProperObjOffset ;modify X to get proper air bubble offset | |
ldy #$03 ;set other offset for airbubble's offscreen bits | |
jmp GetOffScreenBitsSet ;and get offscreen information about air bubble | |
GetMiscOffscreenBits: | |
ldy #$02 ;set for misc object offsets | |
jsr GetProperObjOffset ;modify X to get proper misc object offset | |
ldy #$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 | |
adc ObjOffsetData,y ;add amount of bytes to offset depending on setting in Y | |
tax ;put back in X and leave | |
rts | |
GetEnemyOffscreenBits: | |
lda #$01 ;set A to add 1 byte in order to get enemy offset | |
ldy #$01 ;set Y to put offscreen bits in Enemy_OffscreenBits | |
jmp SetOffscrBitsOffset | |
GetBlockOffscreenBits: | |
lda #$09 ;set A to add 9 bytes in order to get block obj offset | |
ldy #$04 ;set Y to put offscreen bits in Block_OffscreenBits | |
SetOffscrBitsOffset: | |
stx $00 | |
clc ;add contents of X to A to get | |
adc $00 ;appropriate offset, then give back to X | |
tax | |
GetOffScreenBitsSet: | |
tya ;save offscreen bits offset to stack for now | |
pha | |
jsr RunOffscrBitsSubs | |
asl ;move low nybble to high nybble | |
asl | |
asl | |
asl | |
ora $00 ;mask together with previously saved low nybble | |
sta $00 ;store both here | |
pla ;get offscreen bits offset from stack | |
tay | |
lda $00 ;get value here and store elsewhere | |
sta SprObject_OffscrBits,y | |
ldx ObjectOffset | |
rts | |
RunOffscrBitsSubs: | |
jsr GetXOffscreenBits ;do subroutine here | |
lsr ;move high nybble to low | |
lsr | |
lsr | |
lsr | |
sta $00 ;store here | |
jmp GetYOffscreenBits | |
;-------------------------------- | |
;(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 | |
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: | |
stx $04 ;save position in buffer to here | |
ldy #$01 ;start with right side of screen | |
XOfsLoop: lda ScreenEdge_X_Pos,y ;get pixel coordinate of edge | |
sec ;get difference between pixel coordinate of edge | |
sbc SprObject_X_Position,x ;and pixel coordinate of object position | |
sta $07 ;store here | |
lda ScreenEdge_PageLoc,y ;get page location of edge | |
sbc SprObject_PageLoc,x ;subtract from page location of object position | |
ldx DefaultXOnscreenOfs,y ;load offset value here | |
cmp #$00 | |
bmi XLdBData ;if beyond right edge or in front of left edge, branch | |
ldx DefaultXOnscreenOfs+1,y ;if not, load alternate offset value here | |
cmp #$01 | |
bpl XLdBData ;if one page or more to the left of either edge, branch | |
lda #$38 ;if no branching, load value here and store | |
sta $06 | |
lda #$08 ;load some other value and execute subroutine | |
jsr DividePDiff | |
XLdBData: lda XOffscreenBitsData,x ;get bits here | |
ldx $04 ;reobtain position in buffer | |
cmp #$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 | |
ExXOfsBS: rts | |
;-------------------------------- | |
YOffscreenBitsData: | |
.db $00, $08, $0c, $0e | |
.db $0f, $07, $03, $01 | |
.db $00 | |
DefaultYOnscreenOfs: | |
.db $04, $00, $04 | |
HighPosUnitData: | |
.db $ff, $00 | |
GetYOffscreenBits: | |
stx $04 ;save position in buffer to here | |
ldy #$01 ;start with top of screen | |
YOfsLoop: lda HighPosUnitData,y ;load coordinate for edge of vertical unit | |
sec | |
sbc SprObject_Y_Position,x ;subtract from vertical coordinate of object | |
sta $07 ;store here | |
lda #$01 ;subtract one from vertical high byte of object | |
sbc SprObject_Y_HighPos,x | |
ldx DefaultYOnscreenOfs,y ;load offset value here | |
cmp #$00 | |
bmi YLdBData ;if under top of the screen or beyond bottom, branch | |
ldx DefaultYOnscreenOfs+1,y ;if not, load alternate offset value here | |
cmp #$01 | |
bpl YLdBData ;if one vertical unit or more above the screen, branch | |
lda #$20 ;if no branching, load value here and store | |
sta $06 | |
lda #$04 ;load some other value and execute subroutine | |
jsr DividePDiff | |
YLdBData: lda YOffscreenBitsData,x ;get offscreen data bits using offset | |
ldx $04 ;reobtain position in buffer | |
cmp #$00 | |
bne ExYOfsBS ;if bits not zero, branch to leave | |
dey ;otherwise, do bottom of the screen now | |
bpl YOfsLoop | |
ExYOfsBS: rts | |
;-------------------------------- | |
DividePDiff: | |
sta $05 ;store current value in A here | |
lda $07 ;get pixel difference | |
cmp $06 ;compare to preset value | |
bcs ExDivPD ;if pixel difference >= preset value, branch | |
lsr ;divide by eight | |
lsr | |
lsr | |
and #$07 ;mask out all but 3 LSB | |
cpy #$01 ;right side of the screen or top? | |
bcs SetOscrO ;if so, branch, use difference / 8 as offset | |
adc $05 ;if not, add value to difference / 8 | |
SetOscrO: tax ;use as offset | |
ExDivPD: rts ;leave | |
;------------------------------------------------------------------------------------- | |
;$00-$01 - tile numbers | |
;$02 - Y coordinate | |
;$03 - flip control | |
;$04 - sprite attributes | |
;$05 - X coordinate | |
DrawSpriteObject: | |
lda $03 ;get saved flip control bits | |
lsr | |
lsr ;move d1 into carry | |
lda $00 | |
bcc NoHFlip ;if d1 not set, branch | |
sta Sprite_Tilenumber+4,y ;store first tile into second sprite | |
lda $01 ;and second into first sprite | |
sta Sprite_Tilenumber,y | |
lda #$40 ;activate horizontal flip OAM attribute | |
bne SetHFAt ;and unconditionally branch | |
NoHFlip: sta Sprite_Tilenumber,y ;store first tile into first sprite | |
lda $01 ;and second into second sprite | |
sta Sprite_Tilenumber+4,y | |
lda #$00 ;clear bit for horizontal flip | |
SetHFAt: ora $04 ;add other OAM attributes if necessary | |
sta Sprite_Attributes,y ;store sprite attributes | |
sta Sprite_Attributes+4,y | |
lda $02 ;now the y coordinates | |
sta Sprite_Y_Position,y ;note because they are | |
sta Sprite_Y_Position+4,y ;side by side, they are the same | |
lda $05 | |
sta Sprite_X_Position,y ;store x coordinate, then | |
clc ;add 8 pixels and store another to | |
adc #$08 ;put them side by side | |
sta Sprite_X_Position+4,y | |
lda $02 ;add eight pixels to the next y | |
clc ;coordinate | |
adc #$08 | |
sta $02 | |
tya ;add eight to the offset in Y to | |
clc ;move to the next two sprites | |
adc #$08 | |
tay | |
inx ;increment offset to return it to the | |
inx ;routine that called this subroutine | |
rts | |
;------------------------------------------------------------------------------------- | |
;unused space | |
.db $ff, $ff, $ff, $ff, $ff, $ff | |
;------------------------------------------------------------------------------------- | |
SoundEngine: | |
lda OperMode ;are we in title screen mode? | |
bne SndOn | |
sta SND_MASTERCTRL_REG ;if so, disable sound and leave | |
rts | |
SndOn: lda #$ff | |
sta JOYPAD_PORT2 ;disable irqs and set frame counter mode??? | |
lda #$0f | |
sta SND_MASTERCTRL_REG ;enable first four channels | |
lda PauseModeFlag ;is sound already in pause mode? | |
bne InPause | |
lda PauseSoundQueue ;if not, check pause sfx queue | |
cmp #$01 | |
bne RunSoundSubroutines ;if queue is empty, skip pause mode routine | |
InPause: lda PauseSoundBuffer ;check pause sfx buffer | |
bne ContPau | |
lda PauseSoundQueue ;check pause queue | |
beq SkipSoundSubroutines | |
sta PauseSoundBuffer ;if queue full, store in buffer and activate | |
sta PauseModeFlag ;pause mode to interrupt game sounds | |
lda #$00 ;disable sound and clear sfx buffers | |
sta SND_MASTERCTRL_REG | |
sta Square1SoundBuffer | |
sta Square2SoundBuffer | |
sta NoiseSoundBuffer | |
lda #$0f | |
sta SND_MASTERCTRL_REG ;enable sound again | |
lda #$2a ;store length of sound in pause counter | |
sta Squ1_SfxLenCounter | |
PTone1F: lda #$44 ;play first tone | |
bne PTRegC ;unconditional branch | |
ContPau: lda Squ1_SfxLenCounter ;check pause length left | |
cmp #$24 ;time to play second? | |
beq PTone2F | |
cmp #$1e ;time to play first again? | |
beq PTone1F | |
cmp #$18 ;time to play second again? | |
bne DecPauC ;only load regs during times, otherwise skip | |
PTone2F: lda #$64 ;store reg contents and play the pause sfx | |
PTRegC: ldx #$84 | |
ldy #$7f | |
jsr PlaySqu1Sfx | |
DecPauC: dec Squ1_SfxLenCounter ;decrement pause sfx counter | |
bne SkipSoundSubroutines | |
lda #$00 ;disable sound if in pause mode and | |
sta SND_MASTERCTRL_REG ;not currently playing the pause sfx | |
lda PauseSoundBuffer ;if no longer playing pause sfx, check to see | |
cmp #$02 ;if we need to be playing sound again | |
bne SkipPIn | |
lda #$00 ;clear pause mode to allow game sounds again | |
sta PauseModeFlag | |
SkipPIn: lda #$00 ;clear pause sfx buffer | |
sta PauseSoundBuffer | |
beq SkipSoundSubroutines | |
RunSoundSubroutines: | |
jsr Square1SfxHandler ;play sfx on square channel 1 | |
jsr Square2SfxHandler ; '' '' '' square channel 2 | |
jsr NoiseSfxHandler ; '' '' '' noise channel | |
jsr MusicHandler ;play music on all channels | |
lda #$00 ;clear the music queues | |
sta AreaMusicQueue | |
sta EventMusicQueue | |
SkipSoundSubroutines: | |
lda #$00 ;clear the sound effects queues | |
sta Square1SoundQueue | |
sta Square2SoundQueue | |
sta NoiseSoundQueue | |
sta PauseSoundQueue | |
ldy DAC_Counter ;load some sort of counter | |
lda AreaMusicBuffer | |
and #%00000011 ;check for specific music | |
beq NoIncDAC | |
inc DAC_Counter ;increment and check counter | |
cpy #$30 | |
bcc StrWave ;if not there yet, just store it | |
NoIncDAC: tya | |
beq StrWave ;if we are at zero, do not decrement | |
dec DAC_Counter ;decrement counter | |
StrWave: sty SND_DELTA_REG+1 ;store into DMC load register (??) | |
rts ;we are done here | |
;-------------------------------- | |
Dump_Squ1_Regs: | |
sty SND_SQUARE1_REG+1 ;dump the contents of X and Y into square 1's control regs | |
stx SND_SQUARE1_REG | |
rts | |
PlaySqu1Sfx: | |
jsr Dump_Squ1_Regs ;do sub to set ctrl regs for square 1, then set frequency regs | |
SetFreq_Squ1: | |
ldx #$00 ;set frequency reg offset for square 1 sound channel | |
Dump_Freq_Regs: | |
tay | |
lda FreqRegLookupTbl+1,y ;use previous contents of A for sound reg offset | |
beq NoTone ;if zero, then do not load | |
sta SND_REGISTER+2,x ;first byte goes into LSB of frequency divider | |
lda FreqRegLookupTbl,y ;second byte goes into 3 MSB plus extra bit for | |
ora #%00001000 ;length counter | |
sta SND_REGISTER+3,x | |
NoTone: rts | |
Dump_Sq2_Regs: | |
stx SND_SQUARE2_REG ;dump the contents of X and Y into square 2's control regs | |
sty SND_SQUARE2_REG+1 | |
rts | |
PlaySqu2Sfx: | |
jsr Dump_Sq2_Regs ;do sub to set ctrl regs for square 2, then set frequency regs | |
SetFreq_Squ2: | |
ldx #$04 ;set frequency reg offset for square 2 sound channel | |
bne Dump_Freq_Regs ;unconditional branch | |
SetFreq_Tri: | |
ldx #$08 ;set frequency reg offset for triangle sound channel | |
bne Dump_Freq_Regs ;unconditional branch | |
;-------------------------------- | |
SwimStompEnvelopeData: | |
.db $9f, $9b, $98, $96, $95, $94, $92, $90 | |
.db $90, $9a, $97, $95, $93, $92 | |
PlayFlagpoleSlide: | |
lda #$40 ;store length of flagpole sound | |
sta Squ1_SfxLenCounter | |
lda #$62 ;load part of reg contents for flagpole sound | |
jsr SetFreq_Squ1 | |
ldx #$99 ;now load the rest | |
bne FPS2nd | |
PlaySmallJump: | |
lda #$26 ;branch here for small mario jumping sound | |
bne JumpRegContents | |
PlayBigJump: | |
lda #$18 ;branch here for big mario jumping sound | |
JumpRegContents: | |
ldx #$82 ;note that small and big jump borrow each others' reg contents | |
ldy #$a7 ;anyway, this loads the first part of mario's jumping sound | |
jsr PlaySqu1Sfx | |
lda #$28 ;store length of sfx for both jumping sounds | |
sta Squ1_SfxLenCounter ;then continue on here | |
ContinueSndJump: | |
lda Squ1_SfxLenCounter ;jumping sounds seem to be composed of three parts | |
cmp #$25 ;check for time to play second part yet | |
bne N2Prt | |
ldx #$5f ;load second part | |
ldy #$f6 | |
bne DmpJpFPS ;unconditional branch | |
N2Prt: cmp #$20 ;check for third part | |
bne DecJpFPS | |
ldx #$48 ;load third part | |
FPS2nd: ldy #$bc ;the flagpole slide sound shares part of third part | |
DmpJpFPS: jsr Dump_Squ1_Regs | |
bne DecJpFPS ;unconditional branch outta here | |
PlayFireballThrow: | |
lda #$05 | |
ldy #$99 ;load reg contents for fireball throw sound | |
bne Fthrow ;unconditional branch | |
PlayBump: | |
lda #$0a ;load length of sfx and reg contents for bump sound | |
ldy #$93 | |
Fthrow: ldx #$9e ;the fireball sound shares reg contents with the bump sound | |
sta Squ1_SfxLenCounter | |
lda #$0c ;load offset for bump sound | |
jsr PlaySqu1Sfx | |
ContinueBumpThrow: | |
lda Squ1_SfxLenCounter ;check for second part of bump sound | |
cmp #$06 | |
bne DecJpFPS | |
lda #$bb ;load second part directly | |
sta SND_SQUARE1_REG+1 | |
DecJpFPS: bne BranchToDecLength1 ;unconditional branch | |
Square1SfxHandler: | |
ldy Square1SoundQueue ;check for sfx in queue | |
beq CheckSfx1Buffer | |
sty Square1SoundBuffer ;if found, put in buffer | |
bmi PlaySmallJump ;small jump | |
lsr Square1SoundQueue | |
bcs PlayBigJump ;big jump | |
lsr Square1SoundQueue | |
bcs PlayBump ;bump | |
lsr Square1SoundQueue | |
bcs PlaySwimStomp ;swim/stomp | |
lsr Square1SoundQueue | |
bcs PlaySmackEnemy ;smack enemy | |
lsr Square1SoundQueue | |
bcs PlayPipeDownInj ;pipedown/injury | |
lsr Square1SoundQueue | |
bcs PlayFireballThrow ;fireball throw | |
lsr Square1SoundQueue | |
bcs PlayFlagpoleSlide ;slide flagpole | |
CheckSfx1Buffer: | |
lda Square1SoundBuffer ;check for sfx in buffer | |
beq ExS1H ;if not found, exit sub | |
bmi ContinueSndJump ;small mario jump | |
lsr | |
bcs ContinueSndJump ;big mario jump | |
lsr | |
bcs ContinueBumpThrow ;bump | |
lsr | |
bcs ContinueSwimStomp ;swim/stomp | |
lsr | |
bcs ContinueSmackEnemy ;smack enemy | |
lsr | |
bcs ContinuePipeDownInj ;pipedown/injury | |
lsr | |
bcs ContinueBumpThrow ;fireball throw | |
lsr | |
bcs DecrementSfx1Length ;slide flagpole | |
ExS1H: rts | |
PlaySwimStomp: | |
lda #$0e ;store length of swim/stomp sound | |
sta Squ1_SfxLenCounter | |
ldy #$9c ;store reg contents for swim/stomp sound | |
ldx #$9e | |
lda #$26 | |
jsr PlaySqu1Sfx | |
ContinueSwimStomp: | |
ldy Squ1_SfxLenCounter ;look up reg contents in data section based on | |
lda SwimStompEnvelopeData-1,y ;length of sound left, used to control sound's | |
sta SND_SQUARE1_REG ;envelope | |
cpy #$06 | |
bne BranchToDecLength1 | |
lda #$9e ;when the length counts down to a certain point, put this | |
sta SND_SQUARE1_REG+2 ;directly into the LSB of square 1's frequency divider | |
BranchToDecLength1: | |
bne DecrementSfx1Length ;unconditional branch (regardless of how we got here) | |
PlaySmackEnemy: | |
lda #$0e ;store length of smack enemy sound | |
ldy #$cb | |
ldx #$9f | |
sta Squ1_SfxLenCounter | |
lda #$28 ;store reg contents for smack enemy sound | |
jsr PlaySqu1Sfx | |
bne DecrementSfx1Length ;unconditional branch | |
ContinueSmackEnemy: | |
ldy Squ1_SfxLenCounter ;check about halfway through | |
cpy #$08 | |
bne SmSpc | |
lda #$a0 ;if we're at the about-halfway point, make the second tone | |
sta SND_SQUARE1_REG+2 ;in the smack enemy sound | |
lda #$9f | |
bne SmTick | |
SmSpc: lda #$90 ;this creates spaces in the sound, giving it its distinct noise | |
SmTick: sta SND_SQUARE1_REG | |
DecrementSfx1Length: | |
dec Squ1_SfxLenCounter ;decrement length of sfx | |
bne ExSfx1 | |
StopSquare1Sfx: | |
ldx #$00 ;if end of sfx reached, clear buffer | |
stx $f1 ;and stop making the sfx | |
ldx #$0e | |
stx SND_MASTERCTRL_REG | |
ldx #$0f | |
stx SND_MASTERCTRL_REG | |
ExSfx1: rts | |
PlayPipeDownInj: | |
lda #$2f ;load length of pipedown sound | |
sta Squ1_SfxLenCounter | |
ContinuePipeDownInj: | |
lda Squ1_SfxLenCounter ;some bitwise logic, forces the regs | |
lsr ;to be written to only during six specific times | |
bcs NoPDwnL ;during which d3 must be set and d1-0 must be clear | |
lsr | |
bcs NoPDwnL | |
and #%00000010 | |
beq NoPDwnL | |
ldy #$91 ;and this is where it actually gets written in | |
ldx #$9a | |
lda #$44 | |
jsr PlaySqu1Sfx | |
NoPDwnL: jmp DecrementSfx1Length | |
;-------------------------------- | |
ExtraLifeFreqData: | |
.db $58, $02, $54, $56, $4e, $44 | |
PowerUpGrabFreqData: | |
.db $4c, $52, $4c, $48, $3e, $36, $3e, $36, $30 | |
.db $28, $4a, $50, $4a, $64, $3c, $32, $3c, $32 | |
.db $2c, $24, $3a, $64, $3a, $34, $2c, $22, $2c | |
;residual frequency data | |
.db $22, $1c, $14 | |
PUp_VGrow_FreqData: | |
.db $14, $04, $22, $24, $16, $04, $24, $26 ;used by both | |
.db $18, $04, $26, $28, $1a, $04, $28, $2a | |
.db $1c, $04, $2a, $2c, $1e, $04, $2c, $2e ;used by vinegrow | |
.db $20, $04, $2e, $30, $22, $04, $30, $32 | |
PlayCoinGrab: | |
lda #$35 ;load length of coin grab sound | |
ldx #$8d ;and part of reg contents | |
bne CGrab_TTickRegL | |
PlayTimerTick: | |
lda #$06 ;load length of timer tick sound | |
ldx #$98 ;and part of reg contents | |
CGrab_TTickRegL: | |
sta Squ2_SfxLenCounter | |
ldy #$7f ;load the rest of reg contents | |
lda #$42 ;of coin grab and timer tick sound | |
jsr PlaySqu2Sfx | |
ContinueCGrabTTick: | |
lda Squ2_SfxLenCounter ;check for time to play second tone yet | |
cmp #$30 ;timer tick sound also executes this, not sure why | |
bne N2Tone | |
lda #$54 ;if so, load the tone directly into the reg | |
sta SND_SQUARE2_REG+2 | |
N2Tone: bne DecrementSfx2Length | |
PlayBlast: | |
lda #$20 ;load length of fireworks/gunfire sound | |
sta Squ2_SfxLenCounter | |
ldy #$94 ;load reg contents of fireworks/gunfire sound | |
lda #$5e | |
bne SBlasJ | |
ContinueBlast: | |
lda Squ2_SfxLenCounter ;check for time to play second part | |
cmp #$18 | |
bne DecrementSfx2Length | |
ldy #$93 ;load second part reg contents then | |
lda #$18 | |
SBlasJ: bne BlstSJp ;unconditional branch to load rest of reg contents | |
PlayPowerUpGrab: | |
lda #$36 ;load length of power-up grab sound | |
sta Squ2_SfxLenCounter | |
ContinuePowerUpGrab: | |
lda Squ2_SfxLenCounter ;load frequency reg based on length left over | |
lsr ;divide by 2 | |
bcs DecrementSfx2Length ;alter frequency every other frame | |
tay | |
lda PowerUpGrabFreqData-1,y ;use length left over / 2 for frequency offset | |
ldx #$5d ;store reg contents of power-up grab sound | |
ldy #$7f | |
LoadSqu2Regs: | |
jsr PlaySqu2Sfx | |
DecrementSfx2Length: | |
dec Squ2_SfxLenCounter ;decrement length of sfx | |
bne ExSfx2 | |
EmptySfx2Buffer: | |
ldx #$00 ;initialize square 2's sound effects buffer | |
stx Square2SoundBuffer | |
StopSquare2Sfx: | |
ldx #$0d ;stop playing the sfx | |
stx SND_MASTERCTRL_REG | |
ldx #$0f | |
stx SND_MASTERCTRL_REG | |
ExSfx2: rts | |
Square2SfxHandler: | |
lda Square2SoundBuffer ;special handling for the 1-up sound to keep it | |
and #Sfx_ExtraLife ;from being interrupted by other sounds on square 2 | |
bne ContinueExtraLife | |
ldy Square2SoundQueue ;check for sfx in queue | |
beq CheckSfx2Buffer | |
sty Square2SoundBuffer ;if found, put in buffer and check for the following | |
bmi PlayBowserFall ;bowser fall | |
lsr Square2SoundQueue | |
bcs PlayCoinGrab ;coin grab | |
lsr Square2SoundQueue | |
bcs PlayGrowPowerUp ;power-up reveal | |
lsr Square2SoundQueue | |
bcs PlayGrowVine ;vine grow | |
lsr Square2SoundQueue | |
bcs PlayBlast ;fireworks/gunfire | |
lsr Square2SoundQueue | |
bcs PlayTimerTick ;timer tick | |
lsr Square2SoundQueue | |
bcs PlayPowerUpGrab ;power-up grab | |
lsr Square2SoundQueue | |
bcs PlayExtraLife ;1-up | |
CheckSfx2Buffer: | |
lda Square2SoundBuffer ;check for sfx in buffer | |
beq ExS2H ;if not found, exit sub | |
bmi ContinueBowserFall ;bowser fall | |
lsr | |
bcs Cont_CGrab_TTick ;coin grab | |
lsr | |
bcs ContinueGrowItems ;power-up reveal | |
lsr | |
bcs ContinueGrowItems ;vine grow | |
lsr | |
bcs ContinueBlast ;fireworks/gunfire | |
lsr | |
bcs Cont_CGrab_TTick ;timer tick | |
lsr | |
bcs ContinuePowerUpGrab ;power-up grab | |
lsr | |
bcs ContinueExtraLife ;1-up | |
ExS2H: rts | |
Cont_CGrab_TTick: | |
jmp ContinueCGrabTTick | |
JumpToDecLength2: | |
jmp DecrementSfx2Length | |
PlayBowserFall: | |
lda #$38 ;load length of bowser defeat sound | |
sta Squ2_SfxLenCounter | |
ldy #$c4 ;load contents of reg for bowser defeat sound | |
lda #$18 | |
BlstSJp: bne PBFRegs | |
ContinueBowserFall: | |
lda Squ2_SfxLenCounter ;check for almost near the end | |
cmp #$08 | |
bne DecrementSfx2Length | |
ldy #$a4 ;if so, load the rest of reg contents for bowser defeat sound | |
lda #$5a | |
PBFRegs: ldx #$9f ;the fireworks/gunfire sound shares part of reg contents here | |
EL_LRegs: bne LoadSqu2Regs ;this is an unconditional branch outta here | |
PlayExtraLife: | |
lda #$30 ;load length of 1-up sound | |
sta Squ2_SfxLenCounter | |
ContinueExtraLife: | |
lda Squ2_SfxLenCounter | |
ldx #$03 ;load new tones only every eight frames | |
DivLLoop: lsr | |
bcs JumpToDecLength2 ;if any bits set here, branch to dec the length | |
dex | |
bne DivLLoop ;do this until all bits checked, if none set, continue | |
tay | |
lda ExtraLifeFreqData-1,y ;load our reg contents | |
ldx #$82 | |
ldy #$7f | |
bne EL_LRegs ;unconditional branch | |
PlayGrowPowerUp: | |
lda #$10 ;load length of power-up reveal sound | |
bne GrowItemRegs | |
PlayGrowVine: | |
lda #$20 ;load length of vine grow sound | |
GrowItemRegs: | |
sta Squ2_SfxLenCounter | |
lda #$7f ;load contents of reg for both sounds directly | |
sta SND_SQUARE2_REG+1 | |
lda #$00 ;start secondary counter for both sounds | |
sta Sfx_SecondaryCounter | |
ContinueGrowItems: | |
inc Sfx_SecondaryCounter ;increment secondary counter for both sounds | |
lda Sfx_SecondaryCounter ;this sound doesn't decrement the usual counter | |
lsr ;divide by 2 to get the offset | |
tay | |
cpy Squ2_SfxLenCounter ;have we reached the end yet? | |
beq StopGrowItems ;if so, branch to jump, and stop playing sounds | |
lda #$9d ;load contents of other reg directly | |
sta SND_SQUARE2_REG | |
lda PUp_VGrow_FreqData,y ;use secondary counter / 2 as offset for frequency regs | |
jsr SetFreq_Squ2 | |
rts | |
StopGrowItems: | |
jmp EmptySfx2Buffer ;branch to stop playing sounds | |
;-------------------------------- | |
BrickShatterFreqData: | |
.db $01, $0e, $0e, $0d, $0b, $06, $0c, $0f | |
.db $0a, $09, $03, $0d, $08, $0d, $06, $0c | |
PlayBrickShatter: | |
lda #$20 ;load length of brick shatter sound | |
sta Noise_SfxLenCounter | |
ContinueBrickShatter: | |
lda Noise_SfxLenCounter | |
lsr ;divide by 2 and check for bit set to use offset | |
bcc DecrementSfx3Length | |
tay | |
ldx BrickShatterFreqData,y ;load reg contents of brick shatter sound | |
lda BrickShatterEnvData,y | |
PlayNoiseSfx: | |
sta SND_NOISE_REG ;play the sfx | |
stx SND_NOISE_REG+2 | |
lda #$18 | |
sta SND_NOISE_REG+3 | |
DecrementSfx3Length: | |
dec Noise_SfxLenCounter ;decrement length of sfx | |
bne ExSfx3 | |
lda #$f0 ;if done, stop playing the sfx | |
sta SND_NOISE_REG | |
lda #$00 | |
sta NoiseSoundBuffer | |
ExSfx3: rts | |
NoiseSfxHandler: | |
ldy NoiseSoundQueue ;check for sfx in queue | |
beq CheckNoiseBuffer | |
sty NoiseSoundBuffer ;if found, put in buffer | |
lsr NoiseSoundQueue | |
bcs PlayBrickShatter ;brick shatter | |
lsr NoiseSoundQueue | |
bcs PlayBowserFlame ;bowser flame | |
CheckNoiseBuffer: | |
lda NoiseSoundBuffer ;check for sfx in buffer | |
beq ExNH ;if not found, exit sub | |
lsr | |
bcs ContinueBrickShatter ;brick shatter | |
lsr | |
bcs ContinueBowserFlame ;bowser flame | |
ExNH: rts | |
PlayBowserFlame: | |
lda #$40 ;load length of bowser flame sound | |
sta Noise_SfxLenCounter | |
ContinueBowserFlame: | |
lda Noise_SfxLenCounter | |
lsr | |
tay | |
ldx #$0f ;load reg contents of bowser flame sound | |
lda BowserFlameEnvData-1,y | |
bne PlayNoiseSfx ;unconditional branch here | |
;-------------------------------- | |
ContinueMusic: | |
jmp HandleSquare2Music ;if we have music, start with square 2 channel | |
MusicHandler: | |
lda EventMusicQueue ;check event music queue | |
bne LoadEventMusic | |
lda AreaMusicQueue ;check area music queue | |
bne LoadAreaMusic | |
lda EventMusicBuffer ;check both buffers | |
ora AreaMusicBuffer | |
bne ContinueMusic | |
rts ;no music, then leave | |
LoadEventMusic: | |
sta EventMusicBuffer ;copy event music queue contents to buffer | |
cmp #DeathMusic ;is it death music? | |
bne NoStopSfx ;if not, jump elsewhere | |
jsr StopSquare1Sfx ;stop sfx in square 1 and 2 | |
jsr StopSquare2Sfx ;but clear only square 1's sfx buffer | |
NoStopSfx: ldx AreaMusicBuffer | |
stx AreaMusicBuffer_Alt ;save current area music buffer to be re-obtained later | |
ldy #$00 | |
sty NoteLengthTblAdder ;default value for additional length byte offset | |
sty AreaMusicBuffer ;clear area music buffer | |
cmp #TimeRunningOutMusic ;is it time running out music? | |
bne FindEventMusicHeader | |
ldx #$08 ;load offset to be added to length byte of header | |
stx NoteLengthTblAdder | |
bne FindEventMusicHeader ;unconditional branch | |
LoadAreaMusic: | |
cmp #$04 ;is it underground music? | |
bne NoStop1 ;no, do not stop square 1 sfx | |
jsr StopSquare1Sfx | |
NoStop1: ldy #$10 ;start counter used only by ground level music | |
GMLoopB: sty GroundMusicHeaderOfs | |
HandleAreaMusicLoopB: | |
ldy #$00 ;clear event music buffer | |
sty EventMusicBuffer | |
sta AreaMusicBuffer ;copy area music queue contents to buffer | |
cmp #$01 ;is it ground level music? | |
bne FindAreaMusicHeader | |
inc GroundMusicHeaderOfs ;increment but only if playing ground level music | |
ldy GroundMusicHeaderOfs ;is it time to loopback ground level music? | |
cpy #$32 | |
bne LoadHeader ;branch ahead with alternate offset | |
ldy #$11 | |
bne GMLoopB ;unconditional branch | |
FindAreaMusicHeader: | |
ldy #$08 ;load Y for offset of area music | |
sty MusicOffset_Square2 ;residual instruction here | |
FindEventMusicHeader: | |
iny ;increment Y pointer based on previously loaded queue contents | |
lsr ;bit shift and increment until we find a set bit for music | |
bcc FindEventMusicHeader | |
LoadHeader: | |
lda MusicHeaderOffsetData,y ;load offset for header | |
tay | |
lda MusicHeaderData,y ;now load the header | |
sta NoteLenLookupTblOfs | |
lda MusicHeaderData+1,y | |
sta MusicDataLow | |
lda MusicHeaderData+2,y | |
sta MusicDataHigh | |
lda MusicHeaderData+3,y | |
sta MusicOffset_Triangle | |
lda MusicHeaderData+4,y | |
sta MusicOffset_Square1 | |
lda MusicHeaderData+5,y | |
sta MusicOffset_Noise | |
sta NoiseDataLoopbackOfs | |
lda #$01 ;initialize music note counters | |
sta Squ2_NoteLenCounter | |
sta Squ1_NoteLenCounter | |
sta Tri_NoteLenCounter | |
sta Noise_BeatLenCounter | |
lda #$00 ;initialize music data offset for square 2 | |
sta MusicOffset_Square2 | |
sta AltRegContentFlag ;initialize alternate control reg data used by square 1 | |
lda #$0b ;disable triangle channel and reenable it | |
sta SND_MASTERCTRL_REG | |
lda #$0f | |
sta SND_MASTERCTRL_REG | |
HandleSquare2Music: | |
dec Squ2_NoteLenCounter ;decrement square 2 note length | |
bne MiscSqu2MusicTasks ;is it time for more data? if not, branch to end tasks | |
ldy MusicOffset_Square2 ;increment square 2 music offset and fetch data | |
inc MusicOffset_Square2 | |
lda (MusicData),y | |
beq EndOfMusicData ;if zero, the data is a null terminator | |
bpl Squ2NoteHandler ;if non-negative, data is a note | |
bne Squ2LengthHandler ;otherwise it is length data | |
EndOfMusicData: | |
lda EventMusicBuffer ;check secondary buffer for time running out music | |
cmp #TimeRunningOutMusic | |
bne NotTRO | |
lda AreaMusicBuffer_Alt ;load previously saved contents of primary buffer | |
bne MusicLoopBack ;and start playing the song again if there is one | |
NotTRO: and #VictoryMusic ;check for victory music (the only secondary that loops) | |
bne VictoryMLoopBack | |
lda AreaMusicBuffer ;check primary buffer for any music except pipe intro | |
and #%01011111 | |
bne MusicLoopBack ;if any area music except pipe intro, music loops | |
lda #$00 ;clear primary and secondary buffers and initialize | |
sta AreaMusicBuffer ;control regs of square and triangle channels | |
sta EventMusicBuffer | |
sta SND_TRIANGLE_REG | |
lda #$90 | |
sta SND_SQUARE1_REG | |
sta SND_SQUARE2_REG | |
rts | |
MusicLoopBack: | |
jmp HandleAreaMusicLoopB | |
VictoryMLoopBack: | |
jmp LoadEventMusic | |
Squ2LengthHandler: | |
jsr ProcessLengthData ;store length of note | |
sta Squ2_NoteLenBuffer | |
ldy MusicOffset_Square2 ;fetch another byte (MUST NOT BE LENGTH BYTE!) | |
inc MusicOffset_Square2 | |
lda (MusicData),y | |
Squ2NoteHandler: | |
ldx Square2SoundBuffer ;is there a sound playing on this channel? | |
bne SkipFqL1 | |
jsr SetFreq_Squ2 ;no, then play the note | |
beq Rest ;check to see if note is rest | |
jsr LoadControlRegs ;if not, load control regs for square 2 | |
Rest: sta Squ2_EnvelopeDataCtrl ;save contents of A | |
jsr Dump_Sq2_Regs ;dump X and Y into square 2 control regs | |
SkipFqL1: lda Squ2_NoteLenBuffer ;save length in square 2 note counter | |
sta Squ2_NoteLenCounter | |
MiscSqu2MusicTasks: | |
lda Square2SoundBuffer ;is there a sound playing on square 2? | |
bne HandleSquare1Music | |
lda EventMusicBuffer ;check for death music or d4 set on secondary buffer | |
and #%10010001 ;note that regs for death music or d4 are loaded by default | |
bne HandleSquare1Music | |
ldy Squ2_EnvelopeDataCtrl ;check for contents saved from LoadControlRegs | |
beq NoDecEnv1 | |
dec Squ2_EnvelopeDataCtrl ;decrement unless already zero | |
NoDecEnv1: jsr LoadEnvelopeData ;do a load of envelope data to replace default | |
sta SND_SQUARE2_REG ;based on offset set by first load unless playing | |
ldx #$7f ;death music or d4 set on secondary buffer | |
stx SND_SQUARE2_REG+1 | |
HandleSquare1Music: | |
ldy MusicOffset_Square1 ;is there a nonzero offset here? | |
beq HandleTriangleMusic ;if not, skip ahead to the triangle channel | |
dec Squ1_NoteLenCounter ;decrement square 1 note length | |
bne MiscSqu1MusicTasks ;is it time for more data? | |
FetchSqu1MusicData: | |
ldy MusicOffset_Square1 ;increment square 1 music offset and fetch data | |
inc MusicOffset_Square1 | |
lda (MusicData),y | |
bne Squ1NoteHandler ;if nonzero, then skip this part | |
lda #$83 | |
sta SND_SQUARE1_REG ;store some data into control regs for square 1 | |
lda #$94 ;and fetch another byte of data, used to give | |
sta SND_SQUARE1_REG+1 ;death music its unique sound | |
sta AltRegContentFlag | |
bne FetchSqu1MusicData ;unconditional branch | |
Squ1NoteHandler: | |
jsr AlternateLengthHandler | |
sta Squ1_NoteLenCounter ;save contents of A in square 1 note counter | |
ldy Square1SoundBuffer ;is there a sound playing on square 1? | |
bne HandleTriangleMusic | |
txa | |
and #%00111110 ;change saved data to appropriate note format | |
jsr SetFreq_Squ1 ;play the note | |
beq SkipCtrlL | |
jsr LoadControlRegs | |
SkipCtrlL: sta Squ1_EnvelopeDataCtrl ;save envelope offset | |
jsr Dump_Squ1_Regs | |
MiscSqu1MusicTasks: | |
lda Square1SoundBuffer ;is there a sound playing on square 1? | |
bne HandleTriangleMusic | |
lda EventMusicBuffer ;check for death music or d4 set on secondary buffer | |
and #%10010001 | |
bne DeathMAltReg | |
ldy Squ1_EnvelopeDataCtrl ;check saved envelope offset | |
beq NoDecEnv2 | |
dec Squ1_EnvelopeDataCtrl ;decrement unless already zero | |
NoDecEnv2: jsr LoadEnvelopeData ;do a load of envelope data | |
sta SND_SQUARE1_REG ;based on offset set by first load | |
DeathMAltReg: lda AltRegContentFlag ;check for alternate control reg data | |
bne DoAltLoad | |
lda #$7f ;load this value if zero, the alternate value | |
DoAltLoad: sta SND_SQUARE1_REG+1 ;if nonzero, and let's move on | |
HandleTriangleMusic: | |
lda MusicOffset_Triangle | |
dec Tri_NoteLenCounter ;decrement triangle note length | |
bne HandleNoiseMusic ;is it time for more data? | |
ldy MusicOffset_Triangle ;increment square 1 music offset and fetch data | |
inc MusicOffset_Triangle | |
lda (MusicData),y | |
beq LoadTriCtrlReg ;if zero, skip all this and move on to noise | |
bpl TriNoteHandler ;if non-negative, data is note | |
jsr ProcessLengthData ;otherwise, it is length data | |
sta Tri_NoteLenBuffer ;save contents of A | |
lda #$1f | |
sta SND_TRIANGLE_REG ;load some default data for triangle control reg | |
ldy MusicOffset_Triangle ;fetch another byte | |
inc MusicOffset_Triangle | |
lda (MusicData),y | |
beq LoadTriCtrlReg ;check once more for nonzero data | |
TriNoteHandler: | |
jsr SetFreq_Tri | |
ldx Tri_NoteLenBuffer ;save length in triangle note counter | |
stx Tri_NoteLenCounter | |
lda EventMusicBuffer | |
and #%01101110 ;check for death music or d4 set on secondary buffer | |
bne NotDOrD4 ;if playing any other secondary, skip primary buffer check | |
lda AreaMusicBuffer ;check primary buffer for water or castle level music | |
and #%00001010 | |
beq HandleNoiseMusic ;if playing any other primary, or death or d4, go on to noise routine | |
NotDOrD4: txa ;if playing water or castle music or any secondary | |
cmp #$12 ;besides death music or d4 set, check length of note | |
bcs LongN | |
lda EventMusicBuffer ;check for win castle music again if not playing a long note | |
and #EndOfCastleMusic | |
beq MediN | |
lda #$0f ;load value $0f if playing the win castle music and playing a short | |
bne LoadTriCtrlReg ;note, load value $1f if playing water or castle level music or any | |
MediN: lda #$1f ;secondary besides death and d4 except win castle or win castle and playing | |
bne LoadTriCtrlReg ;a short note, and load value $ff if playing a long note on water, castle | |
LongN: lda #$ff ;or any secondary (including win castle) except death and d4 | |
LoadTriCtrlReg: | |
sta SND_TRIANGLE_REG ;save final contents of A into control reg for triangle | |
HandleNoiseMusic: | |
lda AreaMusicBuffer ;check if playing underground or castle music | |
and #%11110011 | |
beq ExitMusicHandler ;if so, skip the noise routine | |
dec Noise_BeatLenCounter ;decrement noise beat length | |
bne ExitMusicHandler ;is it time for more data? | |
FetchNoiseBeatData: | |
ldy MusicOffset_Noise ;increment noise beat offset and fetch data | |
inc MusicOffset_Noise | |
lda (MusicData),y ;get noise beat data, if nonzero, branch to handle | |
bne NoiseBeatHandler | |
lda NoiseDataLoopbackOfs ;if data is zero, reload original noise beat offset | |
sta MusicOffset_Noise ;and loopback next time around | |
bne FetchNoiseBeatData ;unconditional branch | |
NoiseBeatHandler: | |
jsr AlternateLengthHandler | |
sta Noise_BeatLenCounter ;store length in noise beat counter | |
txa | |
and #%00111110 ;reload data and erase length bits | |
beq SilentBeat ;if no beat data, silence | |
cmp #$30 ;check the beat data and play the appropriate | |
beq LongBeat ;noise accordingly | |
cmp #$20 | |
beq StrongBeat | |
and #%00010000 | |
beq SilentBeat | |
lda #$1c ;short beat data | |
ldx #$03 | |
ldy #$18 | |
bne PlayBeat | |
StrongBeat: | |
lda #$1c ;strong beat data | |
ldx #$0c | |
ldy #$18 | |
bne PlayBeat | |
LongBeat: | |
lda #$1c ;long beat data | |
ldx #$03 | |
ldy #$58 | |
bne PlayBeat | |
SilentBeat: | |
lda #$10 ;silence | |
PlayBeat: | |
sta SND_NOISE_REG ;load beat data into noise regs | |
stx SND_NOISE_REG+2 | |
sty SND_NOISE_REG+3 | |
ExitMusicHandler: | |
rts | |
AlternateLengthHandler: | |
tax ;save a copy of original byte into X | |
ror ;save LSB from original byte into carry | |
txa ;reload original byte and rotate three times | |
rol ;turning xx00000x into 00000xxx, with the | |
rol ;bit in carry as the MSB here | |
rol | |
ProcessLengthData: | |
and #%00000111 ;clear all but the three LSBs | |
clc | |
adc $f0 ;add offset loaded from first header byte | |
adc NoteLengthTblAdder ;add extra if time running out music | |
tay | |
lda MusicLengthLookupTbl,y ;load length | |
rts | |
LoadControlRegs: | |
lda EventMusicBuffer ;check secondary buffer for win castle music | |
and #EndOfCastleMusic | |
beq NotECstlM | |
lda #$04 ;this value is only used for win castle music | |
bne AllMus ;unconditional branch | |
NotECstlM: lda AreaMusicBuffer | |
and #%01111101 ;check primary buffer for water music | |
beq WaterMus | |
lda #$08 ;this is the default value for all other music | |
bne AllMus | |
WaterMus: lda #$28 ;this value is used for water music and all other event music | |
AllMus: ldx #$82 ;load contents of other sound regs for square 2 | |
ldy #$7f | |
rts | |
LoadEnvelopeData: | |
lda EventMusicBuffer ;check secondary buffer for win castle music | |
and #EndOfCastleMusic | |
beq LoadUsualEnvData | |
lda EndOfCastleMusicEnvData,y ;load data from offset for win castle music | |
rts | |
LoadUsualEnvData: | |
lda AreaMusicBuffer ;check primary buffer for water music | |
and #%01111101 | |
beq LoadWaterEventMusEnvData | |
lda AreaMusicEnvData,y ;load default data from offset for all other music | |
rts | |
LoadWaterEventMusEnvData: | |
lda WaterEventMusEnvData,y ;load data from offset for water music and all other event music | |
rts | |
;-------------------------------- | |
;music header offsets | |
MusicHeaderData: | |
.db DeathMusHdr-MHD ;event music | |
.db GameOverMusHdr-MHD | |
.db VictoryMusHdr-MHD | |
.db WinCastleMusHdr-MHD | |
.db GameOverMusHdr-MHD | |
.db EndOfLevelMusHdr-MHD | |
.db TimeRunningOutHdr-MHD | |
.db SilenceHdr-MHD | |
.db GroundLevelPart1Hdr-MHD ;area music | |
.db WaterMusHdr-MHD | |
.db UndergroundMusHdr-MHD | |
.db CastleMusHdr-MHD | |
.db Star_CloudHdr-MHD | |
.db GroundLevelLeadInHdr-MHD | |
.db Star_CloudHdr-MHD | |
.db SilenceHdr-MHD | |
.db GroundLevelLeadInHdr-MHD ;ground level music layout | |
.db GroundLevelPart1Hdr-MHD, GroundLevelPart1Hdr-MHD | |
.db GroundLevelPart2AHdr-MHD, GroundLevelPart2BHdr-MHD, GroundLevelPart2AHdr-MHD, GroundLevelPart2CHdr-MHD | |
.db GroundLevelPart2AHdr-MHD, GroundLevelPart2BHdr-MHD, GroundLevelPart2AHdr-MHD, GroundLevelPart2CHdr-MHD | |
.db GroundLevelPart3AHdr-MHD, GroundLevelPart3BHdr-MHD, GroundLevelPart3AHdr-MHD, GroundLevelLeadInHdr-MHD | |
.db GroundLevelPart1Hdr-MHD, GroundLevelPart1Hdr-MHD | |
.db GroundLevelPart4AHdr-MHD, GroundLevelPart4BHdr-MHD, GroundLevelPart4AHdr-MHD, GroundLevelPart4CHdr-MHD | |
.db GroundLevelPart4AHdr-MHD, GroundLevelPart4BHdr-MHD, GroundLevelPart4AHdr-MHD, GroundLevelPart4CHdr-MHD | |
.db GroundLevelPart3AHdr-MHD, GroundLevelPart3BHdr-MHD, GroundLevelPart3AHdr-MHD, GroundLevelLeadInHdr-MHD | |
.db GroundLevelPart4AHdr-MHD, GroundLevelPart4BHdr-MHD, GroundLevelPart4AHdr-MHD, GroundLevelPart4CHdr-MHD | |
;music headers | |
;header format is as follows: | |
;1 byte - length byte offset | |
;2 bytes - music data address | |
;1 byte - triangle data offset | |
;1 byte - square 1 data offset | |
;1 byte - noise data offset (not used by secondary music) | |
TimeRunningOutHdr: .db $08, <TimeRunOutMusData, >TimeRunOutMusData, $27, $18 | |
Star_CloudHdr: .db $20, <Star_CloudMData, >Star_CloudMData, $2e, $1a, $40 | |
EndOfLevelMusHdr: .db $20, <WinLevelMusData, >WinLevelMusData, $3d, $21 | |
ResidualHeaderData: .db $20, $c4, $fc, $3f, $1d | |
UndergroundMusHdr: .db $18, <UndergroundMusData, >UndergroundMusData, $00, $00 | |
SilenceHdr: .db $08, <SilenceData, >SilenceData, $00 | |
CastleMusHdr: .db $00, <CastleMusData, >CastleMusData, $93, $62 | |
VictoryMusHdr: .db $10, <VictoryMusData, >VictoryMusData, $24, $14 | |
GameOverMusHdr: .db $18, <GameOverMusData, >GameOverMusData, $1e, $14 | |
WaterMusHdr: .db $08, <WaterMusData, >WaterMusData, $a0, $70, $68 | |
WinCastleMusHdr: .db $08, <EndOfCastleMusData, >EndOfCastleMusData, $4c, $24 | |
GroundLevelPart1Hdr: .db $18, <GroundM_P1Data, >GroundM_P1Data, $2d, $1c, $b8 | |
GroundLevelPart2AHdr: .db $18, <GroundM_P2AData, >GroundM_P2AData, $20, $12, $70 | |
GroundLevelPart2BHdr: .db $18, <GroundM_P2BData, >GroundM_P2BData, $1b, $10, $44 | |
GroundLevelPart2CHdr: .db $18, <GroundM_P2CData, >GroundM_P2CData, $11, $0a, $1c | |
GroundLevelPart3AHdr: .db $18, <GroundM_P3AData, >GroundM_P3AData, $2d, $10, $58 | |
GroundLevelPart3BHdr: .db $18, <GroundM_P3BData, >GroundM_P3BData, $14, $0d, $3f | |
GroundLevelLeadInHdr: .db $18, <GroundMLdInData, >GroundMLdInData, $15, $0d, $21 | |
GroundLevelPart4AHdr: .db $18, <GroundM_P4AData, >GroundM_P4AData, $18, $10, $7a | |
GroundLevelPart4BHdr: .db $18, <GroundM_P4BData, >GroundM_P4BData, $19, $0f, $54 | |
GroundLevelPart4CHdr: .db $18, <GroundM_P4CData, >GroundM_P4CData, $1e, $12, $2b | |
DeathMusHdr: .db $18, <DeathMusData, >DeathMusData, $1e, $0f, $2d | |
;-------------------------------- | |
;MUSIC DATA | |
;square 2/triangle format | |
;d7 - length byte flag (0-note, 1-length) | |
;if d7 is set to 0 and d6-d0 is nonzero: | |
;d6-d0 - note offset in frequency look-up table (must be even) | |
;if d7 is set to 1: | |
;d6-d3 - unused | |
;d2-d0 - length offset in length look-up table | |
;value of $00 in square 2 data is used as null terminator, affects all sound channels | |
;value of $00 in triangle data causes routine to skip note | |
;square 1 format | |
;d7-d6, d0 - length offset in length look-up table (bit order is d0,d7,d6) | |
;d5-d1 - note offset in frequency look-up table | |
;value of $00 in square 1 data is flag alternate control reg data to be loaded | |
;noise format | |
;d7-d6, d0 - length offset in length look-up table (bit order is d0,d7,d6) | |
;d5-d4 - beat type (0 - rest, 1 - short, 2 - strong, 3 - long) | |
;d3-d1 - unused | |
;value of $00 in noise data is used as null terminator, affects only noise | |
;all music data is organized into sections (unless otherwise stated): | |
;square 2, square 1, triangle, noise | |
Star_CloudMData: | |
.db $84, $2c, $2c, $2c, $82, $04, $2c, $04, $85, $2c, $84, $2c, $2c | |
.db $2a, $2a, $2a, $82, $04, $2a, $04, $85, $2a, $84, $2a, $2a, $00 | |
.db $1f, $1f, $1f, $98, $1f, $1f, $98, $9e, $98, $1f | |
.db $1d, $1d, $1d, $94, $1d, $1d, $94, $9c, $94, $1d | |
.db $86, $18, $85, $26, $30, $84, $04, $26, $30 | |
.db $86, $14, $85, $22, $2c, $84, $04, $22, $2c | |
.db $21, $d0, $c4, $d0, $31, $d0, $c4, $d0, $00 | |
GroundM_P1Data: | |
.db $85, $2c, $22, $1c, $84, $26, $2a, $82, $28, $26, $04 | |
.db $87, $22, $34, $3a, $82, $40, $04, $36, $84, $3a, $34 | |
.db $82, $2c, $30, $85, $2a | |
SilenceData: | |
.db $00 | |
.db $5d, $55, $4d, $15, $19, $96, $15, $d5, $e3, $eb | |
.db $2d, $a6, $2b, $27, $9c, $9e, $59 | |
.db $85, $22, $1c, $14, $84, $1e, $22, $82, $20, $1e, $04, $87 | |
.db $1c, $2c, $34, $82, $36, $04, $30, $34, $04, $2c, $04, $26 | |
.db $2a, $85, $22 | |
GroundM_P2AData: | |
.db $84, $04, $82, $3a, $38, $36, $32, $04, $34 | |
.db $04, $24, $26, $2c, $04, $26, $2c, $30, $00 | |
.db $05, $b4, $b2, $b0, $2b, $ac, $84 | |
.db $9c, $9e, $a2, $84, $94, $9c, $9e | |
.db $85, $14, $22, $84, $2c, $85, $1e | |
.db $82, $2c, $84, $2c, $1e | |
GroundM_P2BData: | |
.db $84, $04, $82, $3a, $38, $36, $32, $04, $34 | |
.db $04, $64, $04, $64, $86, $64, $00 | |
.db $05, $b4, $b2, $b0, $2b, $ac, $84 | |
.db $37, $b6, $b6, $45 | |
.db $85, $14, $1c, $82, $22, $84, $2c | |
.db $4e, $82, $4e, $84, $4e, $22 | |
GroundM_P2CData: | |
.db $84, $04, $85, $32, $85, $30, $86, $2c, $04, $00 | |
.db $05, $a4, $05, $9e, $05, $9d, $85 | |
.db $84, $14, $85, $24, $28, $2c, $82 | |
.db $22, $84, $22, $14 | |
.db $21, $d0, $c4, $d0, $31, $d0, $c4, $d0, $00 | |
GroundM_P3AData: | |
.db $82, $2c, $84, $2c, $2c, $82, $2c, $30 | |
.db $04, $34, $2c, $04, $26, $86, $22, $00 | |
.db $a4, $25, $25, $a4, $29, $a2, $1d, $9c, $95 | |
GroundM_P3BData: | |
.db $82, $2c, $2c, $04, $2c, $04, $2c, $30, $85, $34, $04, $04, $00 | |
.db $a4, $25, $25, $a4, $a8, $63, $04 | |
;triangle data used by both sections of third part | |
.db $85, $0e, $1a, $84, $24, $85, $22, $14, $84, $0c | |
GroundMLdInData: | |
.db $82, $34, $84, $34, $34, $82, $2c, $84, $34, $86, $3a, $04, $00 | |
.db $a0, $21, $21, $a0, $21, $2b, $05, $a3 | |
.db $82, $18, $84, $18, $18, $82, $18, $18, $04, $86, $3a, $22 | |
;noise data used by lead-in and third part sections | |
.db $31, $90, $31, $90, $31, $71, $31, $90, $90, $90, $00 | |
GroundM_P4AData: | |
.db $82, $34, $84, $2c, $85, $22, $84, $24 | |
.db $82, $26, $36, $04, $36, $86, $26, $00 | |
.db $ac, $27, $5d, $1d, $9e, $2d, $ac, $9f | |
.db $85, $14, $82, $20, $84, $22, $2c | |
.db $1e, $1e, $82, $2c, $2c, $1e, $04 | |
GroundM_P4BData: | |
.db $87, $2a, $40, $40, $40, $3a, $36 | |
.db $82, $34, $2c, $04, $26, $86, $22, $00 | |
.db $e3, $f7, $f7, $f7, $f5, $f1, $ac, $27, $9e, $9d | |
.db $85, $18, $82, $1e, $84, $22, $2a | |
.db $22, $22, $82, $2c, $2c, $22, $04 | |
DeathMusData: | |
.db $86, $04 ;death music share data with fourth part c of ground level music | |
GroundM_P4CData: | |
.db $82, $2a, $36, $04, $36, $87, $36, $34, $30, $86, $2c, $04, $00 | |
.db $00, $68, $6a, $6c, $45 ;death music only | |
.db $a2, $31, $b0, $f1, $ed, $eb, $a2, $1d, $9c, $95 | |
.db $86, $04 ;death music only | |
.db $85, $22, $82, $22, $87, $22, $26, $2a, $84, $2c, $22, $86, $14 | |
;noise data used by fourth part sections | |
.db $51, $90, $31, $11, $00 | |
CastleMusData: | |
.db $80, $22, $28, $22, $26, $22, $24, $22, $26 | |
.db $22, $28, $22, $2a, $22, $28, $22, $26 | |
.db $22, $28, $22, $26, $22, $24, $22, $26 | |
.db $22, $28, $22, $2a, $22, $28, $22, $26 | |
.db $20, $26, $20, $24, $20, $26, $20, $28 | |
.db $20, $26, $20, $28, $20, $26, $20, $24 | |
.db $20, $26, $20, $24, $20, $26, $20, $28 | |
.db $20, $26, $20, $28, $20, $26, $20, $24 | |
.db $28, $30, $28, $32, $28, $30, $28, $2e | |
.db $28, $30, $28, $2e, $28, $2c, $28, $2e | |
.db $28, $30, $28, $32, $28, $30, $28, $2e | |
.db $28, $30, $28, $2e, $28, $2c, $28, $2e, $00 | |
.db $04, $70, $6e, $6c, $6e, $70, $72, $70, $6e | |
.db $70, $6e, $6c, $6e, $70, $72, $70, $6e | |
.db $6e, $6c, $6e, $70, $6e, $70, $6e, $6c | |
.db $6e, $6c, $6e, $70, $6e, $70, $6e, $6c | |
.db $76, $78, $76, $74, $76, $74, $72, $74 | |
.db $76, $78, $76, $74, $76, $74, $72, $74 | |
.db $84, $1a, $83, $18, $20, $84, $1e, $83, $1c, $28 | |
.db $26, $1c, $1a, $1c | |
GameOverMusData: | |
.db $82, $2c, $04, $04, $22, $04, $04, $84, $1c, $87 | |
.db $26, $2a, $26, $84, $24, $28, $24, $80, $22, $00 | |
.db $9c, $05, $94, $05, $0d, $9f, $1e, $9c, $98, $9d | |
.db $82, $22, $04, $04, $1c, $04, $04, $84, $14 | |
.db $86, $1e, $80, $16, $80, $14 | |
TimeRunOutMusData: | |
.db $81, $1c, $30, $04, $30, $30, $04, $1e, $32, $04, $32, $32 | |
.db $04, $20, $34, $04, $34, $34, $04, $36, $04, $84, $36, $00 | |
.db $46, $a4, $64, $a4, $48, $a6, $66, $a6, $4a, $a8, $68, $a8 | |
.db $6a, $44, $2b | |
.db $81, $2a, $42, $04, $42, $42, $04, $2c, $64, $04, $64, $64 | |
.db $04, $2e, $46, $04, $46, $46, $04, $22, $04, $84, $22 | |
WinLevelMusData: | |
.db $87, $04, $06, $0c, $14, $1c, $22, $86, $2c, $22 | |
.db $87, $04, $60, $0e, $14, $1a, $24, $86, $2c, $24 | |
.db $87, $04, $08, $10, $18, $1e, $28, $86, $30, $30 | |
.db $80, $64, $00 | |
.db $cd, $d5, $dd, $e3, $ed, $f5, $bb, $b5, $cf, $d5 | |
.db $db, $e5, $ed, $f3, $bd, $b3, $d1, $d9, $df, $e9 | |
.db $f1, $f7, $bf, $ff, $ff, $ff, $34 | |
.db $00 ;unused byte | |
.db $86, $04, $87, $14, $1c, $22, $86, $34, $84, $2c | |
.db $04, $04, $04, $87, $14, $1a, $24, $86, $32, $84 | |
.db $2c, $04, $86, $04, $87, $18, $1e, $28, $86, $36 | |
.db $87, $30, $30, $30, $80, $2c | |
;square 2 and triangle use the same data, square 1 is unused | |
UndergroundMusData: | |
.db $82, $14, $2c, $62, $26, $10, $28, $80, $04 | |
.db $82, $14, $2c, $62, $26, $10, $28, $80, $04 | |
.db $82, $08, $1e, $5e, $18, $60, $1a, $80, $04 | |
.db $82, $08, $1e, $5e, $18, $60, $1a, $86, $04 | |
.db $83, $1a, $18, $16, $84, $14, $1a, $18, $0e, $0c | |
.db $16, $83, $14, $20, $1e, $1c, $28, $26, $87 | |
.db $24, $1a, $12, $10, $62, $0e, $80, $04, $04 | |
.db $00 | |
;noise data directly follows square 2 here unlike in other songs | |
WaterMusData: | |
.db $82, $18, $1c, $20, $22, $26, $28 | |
.db $81, $2a, $2a, $2a, $04, $2a, $04, $83, $2a, $82, $22 | |
.db $86, $34, $32, $34, $81, $04, $22, $26, $2a, $2c, $30 | |
.db $86, $34, $83, $32, $82, $36, $84, $34, $85, $04, $81, $22 | |
.db $86, $30, $2e, $30, $81, $04, $22, $26, $2a, $2c, $2e | |
.db $86, $30, $83, $22, $82, $36, $84, $34, $85, $04, $81, $22 | |
.db $86, $3a, $3a, $3a, $82, $3a, $81, $40, $82, $04, $81, $3a | |
.db $86, $36, $36, $36, $82, $36, $81, $3a, $82, $04, $81, $36 | |
.db $86, $34, $82, $26, $2a, $36 | |
.db $81, $34, $34, $85, $34, $81, $2a, $86, $2c, $00 | |
.db $84, $90, $b0, $84, $50, $50, $b0, $00 | |
.db $98, $96, $94, $92, $94, $96, $58, $58, $58, $44 | |
.db $5c, $44, $9f, $a3, $a1, $a3, $85, $a3, $e0, $a6 | |
.db $23, $c4, $9f, $9d, $9f, $85, $9f, $d2, $a6, $23 | |
.db $c4, $b5, $b1, $af, $85, $b1, $af, $ad, $85, $95 | |
.db $9e, $a2, $aa, $6a, $6a, $6b, $5e, $9d | |
.db $84, $04, $04, $82, $22, $86, $22 | |
.db $82, $14, $22, $2c, $12, $22, $2a, $14, $22, $2c | |
.db $1c, $22, $2c, $14, $22, $2c, $12, $22, $2a, $14 | |
.db $22, $2c, $1c, $22, $2c, $18, $22, $2a, $16, $20 | |
.db $28, $18, $22, $2a, $12, $22, $2a, $18, $22, $2a | |
.db $12, $22, $2a, $14, $22, $2c, $0c, $22, $2c, $14, $22, $34, $12 | |
.db $22, $30, $10, $22, $2e, $16, $22, $34, $18, $26 | |
.db $36, $16, $26, $36, $14, $26, $36, $12, $22, $36 | |
.db $5c, $22, $34, $0c, $22, $22, $81, $1e, $1e, $85, $1e | |
.db $81, $12, $86, $14 | |
EndOfCastleMusData: | |
.db $81, $2c, $22, $1c, $2c, $22, $1c, $85, $2c, $04 | |
.db $81, $2e, $24, $1e, $2e, $24, $1e, $85, $2e, $04 | |
.db $81, $32, $28, $22, $32, $28, $22, $85, $32 | |
.db $87, $36, $36, $36, $84, $3a, $00 | |
.db $5c, $54, $4c, $5c, $54, $4c | |
.db $5c, $1c, $1c, $5c, $5c, $5c, $5c | |
.db $5e, $56, $4e, $5e, $56, $4e | |
.db $5e, $1e, $1e, $5e, $5e, $5e, $5e | |
.db $62, $5a, $50, $62, $5a, $50 | |
.db $62, $22, $22, $62, $e7, $e7, $e7, $2b | |
.db $86, $14, $81, $14, $80, $14, $14, $81, $14, $14, $14, $14 | |
.db $86, $16, $81, $16, $80, $16, $16, $81, $16, $16, $16, $16 | |
.db $81, $28, $22, $1a, $28, $22, $1a, $28, $80, $28, $28 | |
.db $81, $28, $87, $2c, $2c, $2c, $84, $30 | |
VictoryMusData: | |
.db $83, $04, $84, $0c, $83, $62, $10, $84, $12 | |
.db $83, $1c, $22, $1e, $22, $26, $18, $1e, $04, $1c, $00 | |
.db $e3, $e1, $e3, $1d, $de, $e0, $23 | |
.db $ec, $75, $74, $f0, $f4, $f6, $ea, $31, $2d | |
.db $83, $12, $14, $04, $18, $1a, $1c, $14 | |
.db $26, $22, $1e, $1c, $18, $1e, $22, $0c, $14 | |
;unused space | |
.db $ff, $ff, $ff | |
FreqRegLookupTbl: | |
.db $00, $88, $00, $2f, $00, $00 | |
.db $02, $a6, $02, $80, $02, $5c, $02, $3a | |
.db $02, $1a, $01, $df, $01, $c4, $01, $ab | |
.db $01, $93, $01, $7c, $01, $67, $01, $53 | |
.db $01, $40, $01, $2e, $01, $1d, $01, $0d | |
.db $00, $fe, $00, $ef, $00, $e2, $00, $d5 | |
.db $00, $c9, $00, $be, $00, $b3, $00, $a9 | |
.db $00, $a0, $00, $97, $00, $8e, $00, $86 | |
.db $00, $77, $00, $7e, $00, $71, $00, $54 | |
.db $00, $64, $00, $5f, $00, $59, $00, $50 | |
.db $00, $47, $00, $43, $00, $3b, $00, $35 | |
.db $00, $2a, $00, $23, $04, $75, $03, $57 | |
.db $02, $f9, $02, $cf, $01, $fc, $00, $6a | |
MusicLengthLookupTbl: | |
.db $05, $0a, $14, $28, $50, $1e, $3c, $02 | |
.db $04, $08, $10, $20, $40, $18, $30, $0c | |
.db $03, $06, $0c, $18, $30, $12, $24, $08 | |
.db $36, $03, $09, $06, $12, $1b, $24, $0c | |
.db $24, $02, $06, $04, $0c, $12, $18, $08 | |
.db $12, $01, $03, $02, $06, $09, $0c, $04 | |
EndOfCastleMusicEnvData: | |
.db $98, $99, $9a, $9b | |
AreaMusicEnvData: | |
.db $90, $94, $94, $95, $95, $96, $97, $98 | |
WaterEventMusEnvData: | |
.db $90, $91, $92, $92, $93, $93, $93, $94 | |
.db $94, $94, $94, $94, $94, $95, $95, $95 | |
.db $95, $95, $95, $96, $96, $96, $96, $96 | |
.db $96, $96, $96, $96, $96, $96, $96, $96 | |
.db $96, $96, $96, $96, $95, $95, $94, $93 | |
BowserFlameEnvData: | |
.db $15, $16, $16, $17, $17, $18, $19, $19 | |
.db $1a, $1a, $1c, $1d, $1d, $1e, $1e, $1f | |
.db $1f, $1f, $1f, $1e, $1d, $1c, $1e, $1f | |
.db $1f, $1e, $1d, $1c, $1a, $18, $16, $14 | |
BrickShatterEnvData: | |
.db $15, $16, $16, $17, $17, $18, $19, $19 | |
.db $1a, $1a, $1c, $1d, $1d, $1e, $1e, $1f | |
;------------------------------------------------------------------------------------- | |
;INTERRUPT VECTORS | |
.dw NonMaskableInterrupt | |
.dw Start | |
.dw $fff0 ;unused |
Is this supposed to be the ROM without sprite data???
Yes, the graphics in SMB and most NES games were stored in a separate ROM chip (Called CHR-ROM).
This is a dump of the Program logic and data, which is stored in another chip, the PRG-ROM.
原来这样
Homie's coding in the language of the Gods
damn how i compile this shit
damn how i compile this shit
See line 17.
damn how i compile this shit
See line 17.
no te entendi ni una mierda jaja salu2
Really nice to see this
i played super mario bros yesterday and when i was a child
so rertro!!11
how do i make a sprite move around using left arrow and right arrow i have a script to read the controller but i dont know where to go from there
Wow, how many peoples to do this class? Woooooow
Amazing!
WooooooW!
Absolutely goated
yes we live in crazy world how he made it