;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