;SMBDIS.ASM - A COMPREHENSIVE SUPER MARIO BROS. DISASSEMBLY
;by doppelganger (doppelheathen@gmail.com)

;This file is provided for your own use as-is.  It will require the character rom data
;and an iNES file header to get it to work.

;There are so many people I have to thank for this, that taking all the credit for
;myself would be an unforgivable act of arrogance. Without their help this would
;probably not be possible.  So I thank all the peeps in the nesdev scene whose insight into
;the 6502 and the NES helped me learn how it works (you guys know who you are, there's no 
;way I could have done this without your help), as well as the authors of x816 and SMB 
;Utility, and the reverse-engineers who did the original Super Mario Bros. Hacking Project, 
;which I compared notes with but did not copy from.  Last but certainly not least, I thank
;Nintendo for creating this game and the NES, without which this disassembly would
;only be theory.

;Assembles with x816.

;-------------------------------------------------------------------------------------
;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

;-------------------------------------------------------------------------------------
                            mx    %11
; External wrapper is responsible for setting the stack
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?
                            rtl                              ; GTE - Return to caller

;-------------------------------------------------------------------------------------
;$00 - vram buffer address table low, also used for pseudorandom bit
;$01 - vram buffer address table high

VRAM_AddrTable_Low
                            db    <VRAM_Buffer1,<WaterPaletteData,
                            db    <UndergroundPaletteData,<CastlePaletteData,
                            db    <VRAM_Buffer2,<VRAM_Buffer2,
                            db    <DaySnowPaletteData,<NightSnowPaletteData,
                            db    <MarioThanksMessage,<LuigiThanksMessage,
                            db    <PrincessSaved1,<PrincessSaved2,
                            db    <WorldSelectMessage2

VRAM_AddrTable_High
                            db    >VRAM_Buffer1,>WaterPaletteData,
                            db    >UndergroundPaletteData,>CastlePaletteData,
                            db    >VRAM_Buffer2,>VRAM_Buffer2,
                            db    >DaySnowPaletteData,>NightSnowPaletteData,
                            db    >MarioThanksMessage,>LuigiThanksMessage,
                            db    >PrincessSaved1,>PrincessSaved2,
                            db    >WorldSelectMessage2

VRAM_Buffer_Offset
                            db    <VRAM_Buffer1_Offset,<VRAM_Buffer2_Offset

; This is the actual entry point (60 times per second to keep speeed). Uncomment hardware
; stuf that doesn't need to be communicated to the GTE wrapper
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!
                            rtl

;-------------------------------------------------------------------------------------

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,

MetatileGraphics_High
                            db    >Palette0_MTiles,>Palette1_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,
                            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
                            db    %00000000,%00011000
                            db    %00000001,%00011000
                            db    %00000111,%00011000
                            db    %00001111,%00011000
                            db    %11111111,%00011000
                            db    %00000001,%00011111
                            db    %00000111,%00011111
                            db    %00001111,%00011111
                            db    %10000001,%00011111
                            db    %00000001,%00000000
                            db    %10001111,%00011111
                            db    %11110001,%00011111
                            db    %11111001,%00011000
                            db    %11110001,%00011000
                            db    %11111111,%00011111

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,

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,
                            db    <E_GroundArea1,            <E_GroundArea2,
                            db    <E_GroundArea7,            <E_GroundArea8,
                            db    <E_GroundArea13,           <E_GroundArea14,
                            db    <E_GroundArea19,           <E_GroundArea20,
                            db    <E_UndergroundArea2,       <E_UndergroundArea3,

EnemyDataAddrHigh
                            db    >E_CastleArea1,            >E_CastleArea2,
                            db    >E_GroundArea1,            >E_GroundArea2,
                            db    >E_GroundArea7,            >E_GroundArea8,
                            db    >E_GroundArea13,           >E_GroundArea14,
                            db    >E_GroundArea19,           >E_GroundArea20,
                            db    >E_UndergroundArea2,       >E_UndergroundArea3,

AreaDataHOffsets
                            db    $00,$03,$19,$1c

AreaDataAddrLow
                            db    <L_WaterArea1,             <L_WaterArea2,
                            db    <L_GroundArea4,            <L_GroundArea5,
                            db    <L_GroundArea10,           <L_GroundArea11,
                            db    <L_GroundArea16,           <L_GroundArea17,
                            db    <L_GroundArea22,           <L_UndergroundArea1,
                            db    <L_CastleArea2,            <L_CastleArea3,

AreaDataAddrHigh
                            db    >L_WaterArea1,             >L_WaterArea2,
                            db    >L_GroundArea4,            >L_GroundArea5,
                            db    >L_GroundArea10,           >L_GroundArea11,
                            db    >L_GroundArea16,           >L_GroundArea17,
                            db    >L_GroundArea22,           >L_UndergroundArea1,
                            db    >L_CastleArea2,            >L_CastleArea3,

;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,

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,

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,

ClearBitsMask
                            db    %01111111,                 %10111111,

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,
                            db    GroundLevelPart2AHdr-MHD,  GroundLevelPart2BHdr-MHD,
                            db    GroundLevelPart3AHdr-MHD,  GroundLevelPart3BHdr-MHD,
                            db    GroundLevelPart1Hdr-MHD,   GroundLevelPart1Hdr-MHD
                            db    GroundLevelPart4AHdr-MHD,  GroundLevelPart4BHdr-MHD,
                            db    GroundLevelPart4AHdr-MHD,  GroundLevelPart4BHdr-MHD,
                            db    GroundLevelPart3AHdr-MHD,  GroundLevelPart3BHdr-MHD,
                            db    GroundLevelPart4AHdr-MHD,  GroundLevelPart4BHdr-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,
Star_CloudHdr               db    $20,                       <Star_CloudMData,
EndOfLevelMusHdr            db    $20,                       <WinLevelMusData,
ResidualHeaderData          db    $20,$c4,$fc,$3f,$1d
UndergroundMusHdr           db    $18,                       <UndergroundMusData,
SilenceHdr                  db    $08,                       <SilenceData,
CastleMusHdr                db    $00,                       <CastleMusData,
VictoryMusHdr               db    $10,                       <VictoryMusData,
GameOverMusHdr              db    $18,                       <GameOverMusData,
WaterMusHdr                 db    $08,                       <WaterMusData,
WinCastleMusHdr             db    $08,                       <EndOfCastleMusData,
GroundLevelPart1Hdr         db    $18,                       <GroundM_P1Data,
GroundLevelPart2AHdr        db    $18,                       <GroundM_P2AData,
GroundLevelPart2BHdr        db    $18,                       <GroundM_P2BData,
GroundLevelPart2CHdr        db    $18,                       <GroundM_P2CData,
GroundLevelPart3AHdr        db    $18,                       <GroundM_P3AData,
GroundLevelPart3BHdr        db    $18,                       <GroundM_P3BData,
GroundLevelLeadInHdr        db    $18,                       <GroundMLdInData,
GroundLevelPart4AHdr        db    $18,                       <GroundM_P4AData,
GroundLevelPart4BHdr        db    $18,                       <GroundM_P4BData,
GroundLevelPart4CHdr        db    $18,                       <GroundM_P4CData,
DeathMusHdr                 db    $18,                       <DeathMusData,

;--------------------------------

;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
























