Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ;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
Add Comment
Please, Sign In to add comment