-
-
Save SilatJedi/f2af27ee6aab68eee6f44d8e38d62ce7 to your computer and use it in GitHub Desktop.
A Comprehensive Super Mario Bros. Disassembly
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment