PRGROM

by supermaks64

Game: SMB

Description:

PRGUpload:
REP #$20
	LDA $7F0000
	CMP #$ADDE
	BNE +
	LDA $7F0002
	CMP #$EFBE
	BEQ ++
+
SEP #$20
RTL
++
	SEP #$20
	LDA #$80
	STA $2100
	STZ $4200
LDA.b #$04
STA $00
LDA.b #$20
STA $01
LDA.b #$7F
STA $02
PHK
PEA.w .yo-1
PEA.w $84CE
JML $00811D|!bank

.yo:
if !sa1
LDA #$40
STA.l $001C00
endif
	LDA #$11
	STA $212C
	STA $212D
	LDA #$04
	STA $2133
LDA.b #$23
	STA.w $2107
	STZ $2101
	LDA #$01
	STA $210B
STZ $420B
STZ $2106
	LDA #$80
	STA $2115
	REP #$30
if !sa1
LDA #$1C00
STA $220C
LDA #$0000
TCD
endif
	STZ $2116
	LDX #$0000
---
	SEP #$20
	LDY #$0007
-
	LDA.l $7F0004,x
	STA $2118
	LDA.l $7F000C,x
	STA $2119
	INX
	DEY
	BPL -
	REP #$20
	LDY #$0007
--
	STZ $2118
	INX
	DEY
	BPL --
	CPX #$2000
	BCC ---
	
	LDA #$F000
	LDX #$01FE
-
	STA $0800,x
	DEX
	DEX
	BPL -
	LDX #$001E
-
	STZ $0A00,x
	DEX
	DEX
	BPL -
	SEP #$30
if !sa1
LDA #$80
STA $4200
endif
	JML PRGStart

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

;Update: removed residual note under CHKSTART label.  Thanks to ShaneM for pointing out
;the error.

;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           = $0800

!Sprite_Y_Position     = $0801
!Sprite_Tilenumber     = $0802
!Sprite_Attributes     = $0803
!Sprite_X_Position     = $0800

!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   = $0200
!VRAM_Buffer1          = $0201
!VRAM_Buffer2_Offset   = $0240
!VRAM_Buffer2          = $0241
!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

!PauseSoundQueue       = $fa
!AreaMusicQueue        = $fb
!EventMusicQueue       = $fc

!1DF9	= $ff
!1DFC	= $fe
!1DFA	= $fd

!DrumrollBuffer		   = $f3
!AreaMusicBuffer       = $f4
!EventMusicBuffer      = $07b1
!PauseSoundBuffer      = $07b2


;-------------------------------------------------------------------------------------
;CONSTANTS

;sound effects constants

;$1DF9
!Sfx_Flagpole          = $05
!Sfx_PipeDown_Injury   = $04
!Sfx_EnemySmack        = $03
!Sfx_EnemyStomp        = $02
!Sfx_Bump              = $01
!Sfx_PowerUpGrab       = $0A
!Sfx_Swim			   = $0E

;$1DFC
!Sfx_Fireball          = $06
!Sfx_BowserFall        = $20
!Sfx_ExtraLife         = $05
!Sfx_Drumroll		   = $11
!Sfx_Drumrolled		   = $12
!Sfx_Blast             = $09
!Sfx_GrowVine          = $03
!Sfx_GrowPowerUp       = $02
!Sfx_CoinGrab          = $01
!Sfx_BowserFlame       = $17
!Sfx_BrickShatter      = $07
!Sfx_Bridge			   = $16

;$1DFA
!Sfx_SmallJump         = $01
!Sfx_BigJump           = $01

;music constants
!Silence               = $0F

!StarPowerMusic        = $0D
!PipeIntroMusic        = $15
!CloudMusic            = $12
!CastleMusic           = $08
!UndergroundMusic      = $06
!WaterMusic            = $03
!GroundMusic           = $02

!TimeRunningOutMusic   = $80
!EndOfLevelMusic       = $0C
!AltGameOverMusic      = $0A
!EndOfCastleMusic      = $0B
!VictoryMusic          = $1C
!GameOverMusic         = $0A
!DeathMusic            = $09

;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
!SoundMemory           = $07b0

!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

PRGStart:
	REP #$10
	LDX #$01FF
	TXS
	PHK
	PLB
	SEP #$10
             ldy.b #!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.b #!WarmBootOffset          ;if passed both, load warm boot pointer
COLDBOOT:    jsr INITIALIZEMEMORY         ;clear memory using pointer in Y
             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 #$80
	STA $2100
             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
	JMP SKIPMAINOPER

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

VRAM_ADDRTABLE_LOW:
db !VRAM_Buffer1, WATERPALETTEDATA, GROUNDPALETTEDATA
db UNDERGROUNDPALETTEDATA, CASTLEPALETTEDATA, !VRAM_Buffer1_Offset
db !VRAM_Buffer2, !VRAM_Buffer2, BOWSERPALETTEDATA
db DAYSNOWPALETTEDATA, NIGHTSNOWPALETTEDATA, MUSHROOMPALETTEDATA
db MARIOTHANKSMESSAGE, LUIGITHANKSMESSAGE, MUSHROOMRETAINERSAVED
db PRINCESSSAVED1, PRINCESSSAVED2, WORLDSELECTMESSAGE1
db WORLDSELECTMESSAGE2
db TITLESCREEN

VRAM_ADDRTABLE_HIGH:
db !VRAM_Buffer1>>8, WATERPALETTEDATA>>8, GROUNDPALETTEDATA>>8
db UNDERGROUNDPALETTEDATA>>8, CASTLEPALETTEDATA>>8, !VRAM_Buffer1_Offset>>8
db !VRAM_Buffer2>>8, !VRAM_Buffer2>>8, BOWSERPALETTEDATA>>8
db DAYSNOWPALETTEDATA>>8, NIGHTSNOWPALETTEDATA>>8, MUSHROOMPALETTEDATA>>8
db MARIOTHANKSMESSAGE>>8, LUIGITHANKSMESSAGE>>8, MUSHROOMRETAINERSAVED>>8
db PRINCESSSAVED1>>8, PRINCESSSAVED2>>8, WORLDSELECTMESSAGE1>>8
db WORLDSELECTMESSAGE2>>8
db TITLESCREEN>>8

VRAM_BUFFER_OFFSET:
db !VRAM_Buffer1_Offset, !VRAM_Buffer2_Offset

NONMASKABLEINTERRUPT:
               lda !Mirror_PPU_CTRL_REG1  ;disable NMIs in mirror reg
               and #%01111111            ;save all other bits
               sta !Mirror_PPU_CTRL_REG1
               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
               ora #%00011110
SCREENOFF:     sta !Mirror_PPU_CTRL_REG2  ;save bits for later but not in register at the moment
	LDA #$80
	STA $2100
	STZ $210D
	STZ $210D
	STZ $210E
	STZ $210E

	STZ $4300
	REP #$20
	STZ $2102
	LDA #$0004
	STA $4301
	LDA #$0008
	STA $4303
	LDA #$0220
	STA $4305
	LDY #$01
	STY $420B
	SEP #$20
               ;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
	CPX #$13
	BNE +
	JSR TITLESCREENPROP
+
               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 !Sprite0HitDetectFlag
	BNE +
	LDX !HorizontalScroll
	LDA !Mirror_PPU_CTRL_REG1
	AND #$01
	STX $210D
	STA $210D
+
               lda !Mirror_PPU_CTRL_REG2  ;copy mirror of $2001 to register
	LDY #$80
	AND #%00011000
	BEQ +
	LDY #$0F
+
	STY $2100
	JSR SPCComm
	;jsr SOUNDENGINE           ;play sound
               jsr READJOYPADS           ;read joypads
               jsr PAUSEROUTINE          ;handle pause
	LDA !SavedJoypad1Bits
	CMP #$30
	BNE +
	LDA #$12
	STA $2140
	LDA #!Silence
	STA $2142
	JMP PRGStart
	
+
               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
-
	BIT $4212
	BMI -
               lda !GamePauseStatus       ;if in pause mode, do not bother with sprites at all
               lsr
               bcs SPRITE0HIT
               jsr MOVESPRITESOFFSCREEN
               jsr SPRITESHUFFLER
SPRITE0HIT:
	LDX !HorizontalScroll
	LDA !Mirror_PPU_CTRL_REG1
	AND #$01
-
	LDY $2137
	LDY $213D
	CPY #$1F
	BCC -
-
        BIT $4212
	BVC -
	NOP
	STX $210D
	STA $210D
SKIPSPRITE0:
               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:
-
	BIT $4212
	BPL -
	JMP NONMASKABLEINTERRUPT

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

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
               bne EXITPAUSE
               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                 ;this is a BIT opcode that skips over the next 2 bytes

MOVESPRITESOFFSCREEN:
              ldy #$04                ;this routine moves all but sprite 0
              lda #$f0                ;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 !1DFC        ;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 #$24 ;$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:	;TODO
            lda !OperMode                 ;are we in title screen mode?
            bne INCMODETASK_B            ;if not, exit
	LDA #$13
            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
	;STA $0300,x
	STA !SprObject_X_MoveForce,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 $20,$6B,$C1,$0C
  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 $21,$d1,$C1,$0C ; 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 $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,$C2,$24,$08         ; placeholder for left pipe
db $26,$2d,$C2,$24,$08         ; placeholder for middle pipe
db $26,$35,$C2,$24,$08         ; placeholder for right pipe
db $25,$84,$D5,$08,$08,$08,$08,$08,$08
db $08,$08,$08,$08,$08,$08,$08,$08,$08
db $08,$08,$08,$08,$08,$08
  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
	INY
	CPY #$0F
             bcc WARPNUMLOOP
	LDA #$2F               ;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 #$F4
	;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
	ASL                          ;note that metatile format is:
	ROL                          ;%xx000000 - attribute table bits, 
	ROL                          ;%00xxxxxx - metatile number
	STA $03                      ;store attribute table bits here
            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+5,x
	LDA $03
	ASL
	ASL
	STA !VRAM_Buffer2+4,x
	STA !VRAM_Buffer2+6,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
	INC $00
	INC $00
            ldx $01                      ;get current gfx buffer row, and check for
            inx                          ;the bottom of the screen
            cpx #$0d
            bcc DRAWMTLOOP               ;if not there yet, loop back
            ldy $00                      ;get current vram buffer offset, increment by 3
            iny                          ;(for name table address and length bytes)
            iny
            iny
            lda #$00
            sta !VRAM_Buffer2,y           ;put null terminator at end of data for name table
            sty !VRAM_Buffer2_Offset      ;store new buffer offset
            inc !CurrentNTAddr_Low        ;increment name table address low
            lda !CurrentNTAddr_Low        ;check current low byte
            and #%00011111               ;if no wraparound, just skip this part
            bne EXITDRAWM
            lda #$80                     ;if wraparound occurs, make sure low byte stays
            sta !CurrentNTAddr_Low        ;just under the status bar
            lda !CurrentNTAddr_High       ;and then invert d2 of the name table address high
            eor #%00000100               ;to move onto the next appropriate name table
            sta !CurrentNTAddr_High
EXITDRAWM:  jmp SETVRAMCTRL              ;jump to set buffer to $0341 and leave

;-------------------------------------------------------------------------------------
;$00 - temp attribute table address high (big endian order this time!)
;$01 - temp attribute table address low

RENDERATTRIBUTETABLES:
             lda !CurrentNTAddr_Low    ;get low byte of next name table address
             and #%00011111           ;to be written to, mask out all but 5 LSB,
             sec                      ;subtract four 
             sbc #$04
             and #%00011111           ;mask out bits again and store
             sta $01
             lda !CurrentNTAddr_High   ;get high byte and branch if borrow not set
             bcs SETATHIGH
             eor #%00000100           ;otherwise invert d2
SETATHIGH:   and #%00000100           ;mask out all other bits
             ora #$23                 ;add $2300 to the high byte and store
             sta $00
             lda $01                  ;get low byte - 4, divide by 4, add offset for
             lsr                      ;attribute table and store
             lsr
             adc #$c0                 ;we should now have the appropriate block of
             sta $01                  ;attribute table in our temp address
             ldx #$00
             ldy !VRAM_Buffer2_Offset  ;get buffer offset
ATTRIBLOOP:  lda $00
             sta !VRAM_Buffer2,y       ;store high byte of attribute table address
             lda $01
             clc                      ;get low byte, add 8 because we want to START
             adc #$08                 ;below the status bar, and store
             sta !VRAM_Buffer2+1,y
             sta $01                  ;also store in temp again
             lda !AttributeBuffer,x    ;fetch current attribute table byte and store
             sta !VRAM_Buffer2+3,y     ;in the buffer
             lda #$01
             sta !VRAM_Buffer2+2,y     ;store length of 1 in buffer
             lsr
             sta !AttributeBuffer,x    ;clear current byte in attribute buffer
             iny                      ;increment buffer offset by 4 bytes
             iny
             iny
             iny
             inx                      ;increment attribute offset and check to see
             cpx #$07                 ;if we're at the end yet
             bcc ATTRIBLOOP
             sta !VRAM_Buffer2,y       ;put null terminator at the end
             sty !VRAM_Buffer2_Offset  ;store offset in case we want to do any more
SETVRAMCTRL: lda #$06
             sta !VRAM_Buffer_AddrCtrl ;set buffer to $0341 and leave
             rts

;-------------------------------------------------------------------------------------
;$00 - used as temporary counter in COLORROTATION

COLORROTATEPALETTE:
       db $27, $27, $27, $17, $07, $17

BLANKPALETTE:
       db $3f, $0c, $04, $ff, $ff, $ff, $ff, $00

;used based on area type
PALETTE3DATA:
       db $0f, $07, $12, $0f 
       db $0f, $07, $17, $0f
       db $0f, $07, $17, $1c
       db $0f, $07, $17, $00

COLORROTATION:
              lda !FrameCounter         ;get frame counter
              and #$07                 ;mask out all but three LSB
              bne EXITCOLORROT         ;branch if not set to zero to do this every eighth frame
              ldx !VRAM_Buffer1_Offset  ;check vram buffer offset
              cpx #$31
              bcs EXITCOLORROT         ;if offset over 48 bytes, branch to leave
              tay                      ;otherwise use frame counter's 3 LSB as offset here
GETBLANKPAL:  lda BLANKPALETTE,y       ;get blank palette for palette 3
              sta !VRAM_Buffer1,x       ;store it in the vram buffer
              inx                      ;increment offsets
              iny
              cpy #$08
              bcc GETBLANKPAL          ;do this until all bytes are copied
              ldx !VRAM_Buffer1_Offset  ;get current vram buffer offset
              lda #$03
              sta $00                  ;set counter here
              lda !AreaType             ;get area type
              asl                      ;multiply by 4 to get proper offset
              asl
              tay                      ;save as offset here
GETAREAPAL:   lda PALETTE3DATA,y       ;fetch palette to be written based on area type
              sta !VRAM_Buffer1+3,x     ;store it to overwrite blank palette in vram buffer
              iny
              inx
              dec $00                  ;decrement counter
              bpl GETAREAPAL           ;do this until the palette is all copied
              ldx !VRAM_Buffer1_Offset  ;get current vram buffer offset
              ldy !ColorRotateOffset    ;get color cycling offset
              lda COLORROTATEPALETTE,y
              sta !VRAM_Buffer1+4,x     ;get and store current color in second slot of palette
              lda !VRAM_Buffer1_Offset
              clc                      ;add seven bytes to vram buffer offset
              adc #$07
              sta !VRAM_Buffer1_Offset
              inc !ColorRotateOffset    ;increment color cycling offset
              lda !ColorRotateOffset
              cmp #$06                 ;check to see if it's still in range
              bcc EXITCOLORROT         ;if so, branch to leave
              lda #$00
              sta !ColorRotateOffset    ;otherwise, init to keep it in range
EXITCOLORROT: rts                      ;leave

;-------------------------------------------------------------------------------------
;$00 - temp store for offset control bit
;$01 - temp vram buffer offset
;$02 - temp store for vertical high nybble in block buffer routine
;$03 - temp adder for high byte of name table address
;$04, $05 - name table address low/high
;$06, $07 - block buffer address low/high

BLOCKGFXDATA:
       db $45, $45, $47, $47
       db $47, $47, $47, $47
       db $57, $58, $59, $5a
       db $24, $24, $24, $24
       db $26, $26, $26, $26

REMOVECOIN_AXE:
              ldy #$41                 ;set low byte so offset points to $0341
              lda #$03                 ;load offset for default blank metatile
              ldx !AreaType             ;check area type
              bne WRITEBLANKMT         ;if not water type, use offset
              lda #$04                 ;otherwise load offset for blank metatile used in water
WRITEBLANKMT: jsr PUTBLOCKMETATILE     ;do a sub to write blank metatile to vram buffer
              lda #$06
              sta !VRAM_Buffer_AddrCtrl ;set vram address controller to $0341 and leave
              rts

REPLACEBLOCKMETATILE:
       jsr WRITEBLOCKMETATILE    ;write metatile to vram buffer to replace block object
       inc !Block_ResidualCounter ;increment unused counter (residual code)
       dec !Block_RepFlag,x       ;decrement flag (residual code)
       rts                       ;leave

DESTROYBLOCKMETATILE:
       lda #$00       ;force blank metatile if branched/jumped to this point

WRITEBLOCKMETATILE:
             ldy #$03                ;load offset for blank metatile
             cmp #$00                ;check contents of A for blank metatile
             beq USEBOFFSET          ;branch if found (unconditional if branched from 8a6b)
             ldy #$00                ;load offset for brick metatile w/ line
             cmp #$58
             beq USEBOFFSET          ;use offset if metatile is brick with coins (w/ line)
             cmp #$51
             beq USEBOFFSET          ;use offset if metatile is breakable brick w/ line
             iny                     ;increment offset for brick metatile w/o line
             cmp #$5d
             beq USEBOFFSET          ;use offset if metatile is brick with coins (w/o line)
             cmp #$52
             beq USEBOFFSET          ;use offset if metatile is breakable brick w/o line
             iny                     ;if any other metatile, increment offset for empty block
USEBOFFSET:  tya                     ;put Y in A
             ldy !VRAM_Buffer1_Offset ;get vram buffer offset
             iny                     ;move onto next byte
             jsr PUTBLOCKMETATILE    ;get appropriate block data and write to vram buffer
MOVEVOFFSET: dey                     ;decrement vram buffer offset
             tya                     ;add 10 bytes to it
             clc
             adc #10
             jmp SETVRAMOFFSET       ;branch to store as new vram buffer offset

PUTBLOCKMETATILE:
            stx $00               ;store control bit from !SprDataOffset_Ctrl
            sty $01               ;store vram buffer offset for next byte
            asl
            asl                   ;multiply A by four and use as X
            tax
            ldy #$20              ;load high byte for name table 0
            lda $06               ;get low byte of block buffer pointer
            cmp #$d0              ;check to see if we're on odd-page block buffer
            bcc SAVEHADDER        ;if not, use current high byte
            ldy #$24              ;otherwise load high byte for name table 1
SAVEHADDER: sty $03               ;save high byte here
            and #$0f              ;mask out high nybble of block buffer pointer
            asl                   ;multiply by 2 to get appropriate name table low byte
            sta $04               ;and then store it here
            lda #$00
            sta $05               ;initialize temp high byte
            lda $02               ;get vertical high nybble offset used in block buffer routine
            clc
            adc #$20              ;add 32 pixels for the status bar
            asl
            rol $05               ;shift and rotate d7 onto d0 and d6 into carry
            asl
            rol $05               ;shift and rotate d6 onto d0 and d5 into carry
            adc $04               ;add low byte of name table and carry to vertical high nybble
            sta $04               ;and store here
            lda $05               ;get whatever was in d7 and d6 of vertical high nybble
            adc #$00              ;add carry
            clc
            adc $03               ;then add high byte of name table
            sta $05               ;store here
            ldy $01               ;get vram buffer offset to be used
REMBRIDGE:  lda BLOCKGFXDATA,x    ;write top left and top right
            sta !VRAM_Buffer1+2,y  ;tile numbers into first spot
            lda BLOCKGFXDATA+1,x
            sta !VRAM_Buffer1+3,y
            lda BLOCKGFXDATA+2,x  ;write bottom left and bottom
            sta !VRAM_Buffer1+7,y  ;right tiles numbers into
            lda BLOCKGFXDATA+3,x  ;second spot
            sta !VRAM_Buffer1+8,y
            lda $04
            sta !VRAM_Buffer1,y    ;write low byte of name table
            clc                   ;into first slot as read
            adc #$20              ;add 32 bytes to value
            sta !VRAM_Buffer1+5,y  ;write low byte of name table
            lda $05               ;plus 32 bytes into second slot
            sta !VRAM_Buffer1-1,y  ;write high byte of name
            sta !VRAM_Buffer1+4,y  ;table address to both slots
            lda #$02
            sta !VRAM_Buffer1+1,y  ;put length of 2 in
            sta !VRAM_Buffer1+6,y  ;both slots
            lda #$00
            sta !VRAM_Buffer1+9,y  ;put null terminator at end
            ldx $00               ;get offset control bit here
            rts                   ;and leave

;-------------------------------------------------------------------------------------
;METATILE GRAPHICS TABLE

METATILEGRAPHICS_LOW:
  db PALETTE0_MTILES, PALETTE1_MTILES, PALETTE2_MTILES, PALETTE3_MTILES

METATILEGRAPHICS_HIGH:
  db PALETTE0_MTILES>>8, PALETTE1_MTILES>>8, PALETTE2_MTILES>>8, PALETTE3_MTILES>>8

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, $01, $03
  db $30, $00, $10
  db $00

NIGHTSNOWPALETTEDATA:
  db $3f, $00, $04
  db $0f, $30, $00, $10
  db $00

MUSHROOMPALETTEDATA:
  db $3f, $01, $03
  db $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 ($0006)  ;jump to the address we loaded

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

INITIALIZENAMETABLES:
		lda !Mirror_PPU_CTRL_REG1
		ora #%00010000           
		and #%11110000           
	STA !Mirror_PPU_CTRL_REG1
	LDA #$80
	STA $2115
	REP #$30
	LDA #$2000
	STA $2116
	LDA #$0824
	LDX #$0800
-
	STA $2118
	DEX
	BPL -
	SEP #$30
	TXA
	STZ !VRAM_Buffer1_Offset   ;init vram buffer 1 offset
	STZ !VRAM_Buffer1          ;init vram buffer 1
	STZ !HorizontalScroll      ;reset scroll variables
	STZ !VerticalScroll
	RTS

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

RGBLow:
db $8C,$A0,$62,$47,$2C,$2E,$2E,$6B,$A6,$C1,$E0,$E0,$E0,$00,$00,$00
db $B5,$81,$48,$0F,$D4,$D8,$F7,$33,$6D,$A6,$C0,$E0,$C0,$00,$00,$00
db $FF,$CB,$91,$59,$1E,$1F,$3F,$7D,$B7,$F0,$0A,$27,$07,$29,$00,$00
db $FF,$77,$7A,$5D,$3F,$3F,$3F,$5F,$7C,$99,$B7,$B5,$95,$D6,$00,$00
RGBHigh:
db $31,$34,$3C,$3C,$30,$18,$00,$00,$00,$00,$00,$08,$20,$00,$00,$00
db $56,$59,$69,$65,$54,$38,$18,$01,$01,$01,$01,$1D,$3D,$00,$00,$00
db $7F,$7E,$7E,$7E,$7E,$62,$3E,$26,$16,$16,$2B,$47,$67,$25,$00,$00
db $7F,$7F,$7F,$7F,$7F,$73,$67,$5B,$57,$57,$5F,$6B,$77,$5A,$00,$00

HandleRGBBG:
	INY
	LDA ($00),y
	DEY
	TAX
	LDA RGBHigh,x
	XBA
	LDA RGBLow,x
	REP #$20
	ASL #3
	SEP #$21
	ROR #3
	XBA
	ORA #$40
	STA $2132
	LDA RGBHigh,x
	LSR A
	SEC : ROR
	STA $2132
	XBA
	STA $2132
	RTS

HandleRGB:
	INY
	LDA ($00),y
	TAX
	ASL
	ASL
	BIT #%01000000
	BEQ +
	ORA #$80
+
	AND #%10110000
	PHA
	TXA
	AND #%00000011
	ORA $01,s
	STA $2121
	STA $01,s
	INY
	BIT #%00110011
	BNE +
	JSR HandleRGBBG
+
	LDA ($00),y
-
	PHA
	INY
	LDA ($00),y
	TAX
	LDA RGBLow,x
	STA $2122
	LDA RGBHigh,x
	STA $2122
	PLA
	DEC
	BEQ +
	BIT #%00000011
	BNE -
	TAX
	LDA $01,s
	CLC
	ADC #$10
	BIT #%01000000
	BEQ ++
	PHX
	JSR HandleRGBBG
	PLX
	LDA #$80
++
	STA $2121
	STA $01,s
	TXA
	BRA -
+
	PLA
	BRA ++++

WRITEBUFFERTOSCREEN:
	CMP #$3F
	BEQ HandleRGB
	STA $2117
               iny
               lda ($00),y               ;load next byte (second)
	STA $2116
               iny
               lda ($00),y               ;load next byte (third)
               asl                       ;shift to left and save in stack
	LDX #$00
	BCC +
	INX
	ASL
	BCC ++
	LDX #$81
	LSR
	LSR
	BIT #$01
	BEQ +++
	DEX
	SEC
+++
	STX $2115
	TAX
-
	BCS +++
	INY
	LDA ($00),y
	STA $2118
	DEX
+++
	INY
	LDA ($00),y
	STA $2119
	DEX
	BNE -
	BRA ++++
+
	ASL
++
	STX $2115
               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 $2118
               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
UPDATESCREEN:
               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
               rts

TITLESCREENPROP:
	PHX
	LDA #$80
	STA $2115
	REP #$20
	LDY #$04
	LDA #$2085
--
	STA $2116
	LDX #$15
-
	STY $2119
	DEX
	BPL -
	CLC
	ADC #$0020
	CMP #$2205
	BNE --
	LDA #$2249
--
	STA $2116
	LDY #$04
	STY $2119
	LDY #$08
	LDX #$0D
-
	STY $2119
	DEX
	BPL -
	CLC
	ADC #$0040
	CMP #$22C9
	BNE --
	LDA #$22EC
	STA $2116
	LDX #$0A
-
	STY $2119
	DEX
	BPL -
	SEP #$20
	PLX
	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
	db $58,$18,$FF,$06
;-------------------------------------------------------------------------------------

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 in game mode
               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
	STA $0300,y
             iny
             bne CLEARVRLOOP
             sta !GameTimerExpiredFlag  ;clear game timer exp flag
             sta !DisableIntermediate   ;clear skip lives display flag
             sta !BackloadingFlag       ;clear value here
             lda #$ff
             sta !BalPlatformAlignment  ;initialize balance platform assignment flag
             lda !ScreenLeft_PageLoc    ;get left side page location
             lsr !Mirror_PPU_CTRL_REG1  ;shift LSB of ppu register #1 mirror out
             and #$01                  ;mask out all but LSB of page location
             ror                       ;rotate LSB of page location into carry then onto mirror
             rol !Mirror_PPU_CTRL_REG1  ;this is to set the proper PPU name table
             jsr GETAREAMUSIC          ;load proper music into queue
             lda #$38                  ;load sprite shuffle amounts to be used later
             sta !SprShuffleAmt+2
             lda #$48
             sta !SprShuffleAmt+1
             lda #$58
             sta !SprShuffleAmt
             ldx #$0e                  ;load default OAM offsets into $06e4-$06f2
SHUFAMTLOOP: lda DEFAULTSPROFFSETS,x
             sta !SprDataOffset,x
             dex                       ;do this until they're all set
             bpl SHUFAMTLOOP
             ldy #$03                  ;set up sprite #0
ISPR0LOOP:   lda SPRITE0DATA,y
             sta !Sprite_Data,y
             dey
             bpl ISPR0LOOP
             jsr DONOTHING2            ;these jsrs doesn't do anything useful
             jsr DONOTHING1
             inc !Sprite0HitDetectFlag  ;set sprite #0 check flag
             inc !OperMode_Task         ;increment to next task
             rts

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

;$06 - RAM address low
;$07 - RAM address high

INITIALIZEMEMORY:
              ldx #$07          ;set initial high byte to $0700-$07ff
              lda #$00          ;set initial low byte to START of page (at $00 of page)
              sta $06
INITPAGELOOP: stx $07
INITBYTELOOP: cpx #$01          ;check to see if we're on the stack ($0100-$01ff)
              bne INITBYTE      ;if not, go ahead anyway
              cpy #$60          ;otherwise, check to see if we're at $0160-$01ff
              bcs SKIPBYTE      ;if so, skip write
INITBYTE:     sta ($06),y       ;otherwise, initialize byte with current low byte in Y
SKIPBYTE:     dey
              cpy #$ff          ;do this until all bytes in page have been erased
              bne INITBYTELOOP
              dex               ;go onto the next page
              bpl INITPAGELOOP  ;do this until all pages of memory have been erased
              rts

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

MUSICSELECTDATA:
      db !WaterMusic, !GroundMusic, !UndergroundMusic, !CastleMusic
      db !CloudMusic, !PipeIntroMusic

GETAREAMUSIC:
             lda !OperMode           ;if in title screen mode, leave
             beq EXITGETM
             lda !AltEntranceControl ;check for specific alternate mode of entry
             cmp #$02               ;if found, branch without checking starting position
             beq CHKAREATYPE        ;from area object data header
             ldy #$05               ;select music for pipe intro scene by default
             lda !PlayerEntranceCtrl ;check value from level header for certain values
             cmp #$06
             beq STOREMUSIC         ;load music for pipe intro scene if header
             cmp #$07               ;START position either value $06 or $07
             beq STOREMUSIC
CHKAREATYPE: ldy !AreaType           ;load area type as offset for music bit
             lda !CloudTypeOverride
             beq STOREMUSIC         ;check for cloud type override
             ldy #$04               ;select music for cloud type level if found
STOREMUSIC:  lda MUSICSELECTDATA,y  ;otherwise select appropriate music for level type
             sta !AreaMusicQueue     ;store in queue and leave
EXITGETM:    rts

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

PLAYERSTARTING_X_POS:
      db $28, $18
      db $38, $28

ALTYPOSOFFSET:
      db $08, $00

PLAYERSTARTING_Y_POS:
      db $00, $20, $b0, $50, $00, $00, $b0, $b0
      db $f0

PLAYERBGPRIORITYDATA:
      db $00, $20, $00, $00, $00, $00, $00, $00

GAMETIMERDATA:
      db $20 ;dummy byte, used as part of bg priority data
      db $04, $03, $02

ENTRANCE_GAMETIMERSETUP:
          lda !ScreenLeft_PageLoc      ;set current page for area objects
          sta !Player_PageLoc          ;as page location for player
          lda #$28                    ;store value here
          sta !VerticalForceDown       ;for fractional movement downwards if necessary
          lda #$01                    ;set high byte of player position and
          sta !PlayerFacingDir         ;set facing direction so that player faces right
          sta !Player_Y_HighPos
          lda #$00                    ;set player state to on the ground by default
          sta !Player_State
          dec !Player_CollisionBits    ;initialize player's collision bits
          ldy #$00                    ;initialize halfway page
          sty !HalfwayPage      
          lda !AreaType                ;check area type
          bne CHKSTPOS                ;if water type, set swimming flag, otherwise do not set
          iny
CHKSTPOS: sty !SwimmingFlag
          ldx !PlayerEntranceCtrl      ;get starting position loaded from header
          ldy !AltEntranceControl      ;check alternate mode of entry flag for 0 or 1
          beq SETSTPOS
          cpy #$01
          beq SETSTPOS
          ldx ALTYPOSOFFSET-2,y       ;if not 0 or 1, override $0710 with new offset in X
SETSTPOS: lda PLAYERSTARTING_X_POS,y  ;load appropriate horizontal position
          sta !Player_X_Position       ;and vertical positions for the player, using
          lda PLAYERSTARTING_Y_POS,x  ;!AltEntranceControl as offset for horizontal and either $0710
          sta !Player_Y_Position       ;or value that overwrote $0710 as offset for vertical
          lda PLAYERBGPRIORITYDATA,x
          sta !Player_SprAttrib        ;set player sprite attributes using offset in X
          jsr GETPLAYERCOLORS         ;get appropriate player palette
          ldy !GameTimerSetting        ;get timer control value from header
          beq CHKOVERR                ;if set to zero, branch (do not use dummy byte for this)
          lda !FetchNewGameTimerFlag   ;do we need to set the game timer? if not, use 
          beq CHKOVERR                ;old game timer setting
          lda GAMETIMERDATA,y         ;if game timer is set and game timer flag is also set,
          sta !GameTimerDisplay        ;use value of game timer control for first digit of game timer
          lda #$01
          sta !GameTimerDisplay+2      ;set last digit of game timer to 1
          lsr
          sta !GameTimerDisplay+1      ;set second digit of game timer
          sta !FetchNewGameTimerFlag   ;clear flag for game timer reset
          sta !StarInvincibleTimer     ;clear star mario timer
CHKOVERR: ldy !JoypadOverride          ;if controller bits not set, branch to skip this part
          beq CHKSWIME
          lda #$03                    ;set player state to climbing
          sta !Player_State
          ldx #$00                    ;set offset for first slot, for block object
          jsr INITBLOCK_XY_POS
          lda #$f0                    ;set vertical coordinate for block object
          sta !Block_Y_Position
          ldx #$05                    ;set offset in X for last enemy object buffer slot
          ldy #$00                    ;set offset in Y for object coordinates used earlier
          jsr SETUP_VINE              ;do a sub to grow vine
CHKSWIME: ldy !AreaType                ;if level not water-type,
          bne SETPESUB                ;skip this subroutine
          jsr SETUPBUBBLE             ;otherwise, execute sub to set up air bubbles
SETPESUB: lda #$07                    ;set to run player entrance subroutine
          sta !GameEngineSubroutine    ;on the next frame of game engine
          rts

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

;page numbers are in order from -1 to -4
HALFWAYPAGENYBBLES:
      db $56, $40
      db $65, $70
      db $66, $40
      db $66, $40
      db $66, $40
      db $66, $60
      db $65, $70
      db $00, $00

PLAYERLOSELIFE:
             inc !DisableScreenFlag    ;disable screen and sprite 0 check
             lda #$00
             sta !Sprite0HitDetectFlag
             lda #!Silence             ;silence music
             sta !EventMusicQueue
             dec !NumberofLives        ;take one life from player
             bpl STILLINGAME          ;if player still has lives, branch
             lda #$00
             sta !OperMode_Task        ;initialize mode task,
             lda #!GameOverModeValue   ;switch to game over mode
             sta !OperMode             ;and leave
             rts
STILLINGAME: lda !WorldNumber          ;multiply world number by 2 and use
             asl                      ;as offset
             tax
             lda !LevelNumber          ;if in area -3 or -4, increment
             and #$02                 ;offset by one byte, otherwise
             beq GETHALFWAY           ;leave offset alone
             inx
GETHALFWAY:  ldy HALFWAYPAGENYBBLES,x ;get halfway page number with offset
             lda !LevelNumber          ;check area number's LSB
             lsr
             tya                      ;if in area -2 or -4, use lower nybble
             bcs MASKHPNYB
             lsr                      ;move higher nybble to lower if area
             lsr                      ;number is -1 or -3
             lsr
             lsr
MASKHPNYB:   and #%00001111           ;mask out all but lower nybble
             cmp !ScreenLeft_PageLoc
             beq SETHALFWAY           ;left side of screen must be at the halfway page,
             bcc SETHALFWAY           ;otherwise player must START at the
             lda #$00                 ;beginning of the level
SETHALFWAY:  sta !HalfwayPage          ;store as halfway page for player
             jsr TRANSPOSEPLAYERS     ;switch players around if 2-player game
             jmp CONTINUEGAME         ;continue the game

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

GAMEOVERMODE:
      lda !OperMode_Task
      jsr JUMPENGINE
      
      dw SETUPGAMEOVER
      dw SCREENROUTINES
      dw RUNGAMEOVER

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

SETUPGAMEOVER:
      lda #$00                  ;reset screen routine task control for title screen, game,
      sta !ScreenRoutineTask     ;and game over modes
      sta !Sprite0HitDetectFlag  ;disable sprite 0 check
      lda #!GameOverMusic
      sta !EventMusicQueue       ;put game over music in secondary queue
      inc !DisableScreenFlag     ;disable screen output
      inc !OperMode_Task         ;set secondary mode to 1
      rts

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

RUNGAMEOVER:
      lda #$00              ;reenable screen
      sta !DisableScreenFlag
      lda !SavedJoypad1Bits  ;check controller for START pressed
      and #!Start_Button
      bne TERMINATEGAME
      lda !ScreenTimer       ;if not pressed, wait for
      bne GAMEISON          ;screen timer to expire
TERMINATEGAME:
      lda #!Silence          ;silence music
      sta !EventMusicQueue
      jsr TRANSPOSEPLAYERS  ;check if other player can keep
      bcc CONTINUEGAME      ;going, and do so if possible
      lda !WorldNumber       ;otherwise put world number of current
      sta !ContinueWorld     ;player into secret continue function variable
      lda #$00
      asl                   ;residual ASL instruction
      sta !OperMode_Task     ;reset all modes to title screen and
      sta !ScreenTimer       ;leave
      sta !OperMode
      rts

CONTINUEGAME:
           jsr LOADAREAPOINTER       ;update level pointer with
           lda #$01                  ;actual world and area numbers, then
           sta !PlayerSize            ;reset player's size, status, and
           inc !FetchNewGameTimerFlag ;set game timer flag to reload
           lda #$00                  ;game timer from header
           sta !TimerControl          ;also set flag for timers to count again
           sta !PlayerStatus
           sta !GameEngineSubroutine  ;reset task for game core
           sta !OperMode_Task         ;set modes and leave
           lda #$01                  ;if in game over mode, switch back to
           sta !OperMode              ;game mode, because game is still on
GAMEISON:  rts

TRANSPOSEPLAYERS:
           sec                       ;set carry flag by default to end game
           lda !NumberOfPlayers       ;if only a 1 player game, leave
           beq EXTRANS
           lda !OffScr_NumberofLives  ;does offscreen player have any lives left?
           bmi EXTRANS               ;branch if not
           lda !CurrentPlayer         ;invert bit to update
           eor #%00000001            ;which player is on the screen
           sta !CurrentPlayer
           ldx #$06
TRANSLOOP: lda !OnscreenPlayerInfo,x    ;transpose the information
           pha                         ;of the onscreen player
           lda !OffscreenPlayerInfo,x   ;with that of the offscreen player
           sta !OnscreenPlayerInfo,x
           pla
           sta !OffscreenPlayerInfo,x
           dex
           bpl TRANSLOOP
           clc            ;clear carry flag to get game going
EXTRANS:   rts

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

DONOTHING1:
      lda #$ff       ;this is residual code, this value is
      sta $06c9      ;not used anywhere in the program
DONOTHING2:
      rts

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

AREAPARSERTASKHANDLER:
              ldy !AreaParserTaskNum     ;check number of tasks here
              bne DOAPTASKS             ;if already set, go ahead
              ldy #$08
              sty !AreaParserTaskNum     ;otherwise, set eight by default
DOAPTASKS:    dey
              tya
              jsr AREAPARSERTASKS
              dec !AreaParserTaskNum     ;if all tasks not complete do not
              bne SKIPATRENDER          ;render attribute table yet
	;jsr RENDERATTRIBUTETABLES
SKIPATRENDER: rts

AREAPARSERTASKS:
      jsr JUMPENGINE

      dw INCREMENTCOLUMNPOS
      dw RENDERAREAGRAPHICS
      dw RENDERAREAGRAPHICS
      dw AREAPARSERCORE
      dw INCREMENTCOLUMNPOS
      dw RENDERAREAGRAPHICS
      dw RENDERAREAGRAPHICS
      dw AREAPARSERCORE

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

INCREMENTCOLUMNPOS:
           inc !CurrentColumnPos     ;increment column where we're at
           lda !CurrentColumnPos
           and #%00001111           ;mask out higher nybble
           bne NOCOLWRAP
           sta !CurrentColumnPos     ;if no bits left set, wrap back to zero (0-f)
           inc !CurrentPageLoc       ;and increment page number where we're at
NOCOLWRAP: inc !BlockBufferColumnPos ;increment column offset where we're at
           lda !BlockBufferColumnPos
           and #%00011111           ;mask out all but 5 LSB (0-1f)
           sta !BlockBufferColumnPos ;and save
           rts

;-------------------------------------------------------------------------------------
;$00 - used as counter, store for low nybble for background, ceiling byte for terrain
;$01 - used to store floor byte for terrain
;$07 - used to store terrain metatile
;$06-$07 - used to store block buffer address

BSCENEDATAOFFSETS:
      db $00, $30, $60 

BACKSCENERYDATA:
   db $93, $00, $00, $11, $12, $12, $13, $00 ;clouds
   db $00, $51, $52, $53, $00, $00, $00, $00
   db $00, $00, $01, $02, $02, $03, $00, $00
   db $00, $00, $00, $00, $91, $92, $93, $00
   db $00, $00, $00, $51, $52, $53, $41, $42
   db $43, $00, $00, $00, $00, $00, $91, $92

   db $97, $87, $88, $89, $99, $00, $00, $00 ;mountains and bushes
   db $11, $12, $13, $a4, $a5, $a5, $a5, $a6
   db $97, $98, $99, $01, $02, $03, $00, $a4
   db $a5, $a6, $00, $11, $12, $12, $12, $13
   db $00, $00, $00, $00, $01, $02, $02, $03
   db $00, $a4, $a5, $a5, $a6, $00, $00, $00

   db $11, $12, $12, $13, $00, $00, $00, $00 ;trees and fences
   db $00, $00, $00, $9c, $00, $8b, $aa, $aa
   db $aa, $aa, $11, $12, $13, $8b, $00, $9c
   db $9c, $00, $00, $01, $02, $03, $11, $12
   db $12, $13, $00, $00, $00, $00, $aa, $aa
   db $9c, $aa, $00, $8b, $00, $01, $02, $03

BACKSCENERYMETATILES:
   db $80, $83, $00 ;cloud left
   db $81, $84, $00 ;cloud middle
   db $82, $85, $00 ;cloud right
   db $02, $00, $00 ;bush left
   db $03, $00, $00 ;bush middle
   db $04, $00, $00 ;bush right
   db $00, $05, $06 ;mountain left
   db $07, $06, $0a ;mountain middle
   db $00, $08, $09 ;mountain right
   db $4d, $00, $00 ;fence
   db $0d, $0f, $4e ;tall tree
   db $0e, $4e, $4e ;short tree

FSCENEDATAOFFSETS:
      db $00, $0d, $1a

FORESCENERYDATA:
   db $86, $87, $87, $87, $87, $87, $87   ;in water
   db $87, $87, $87, $87, $69, $69

   db $00, $00, $00, $00, $00, $45, $47   ;wall
   db $47, $47, $47, $47, $00, $00

   db $00, $00, $00, $00, $00, $00, $00   ;over water
   db $00, $00, $00, $00, $86, $87

TERRAINMETATILES:
      db $69, $54, $52, $62

TERRAINRENDERBITS:
      db %00000000, %00000000 ;no ceiling or floor
      db %00000000, %00011000 ;no ceiling, floor 2
      db %00000001, %00011000 ;ceiling 1, floor 2
      db %00000111, %00011000 ;ceiling 3, floor 2
      db %00001111, %00011000 ;ceiling 4, floor 2
      db %11111111, %00011000 ;ceiling 8, floor 2
      db %00000001, %00011111 ;ceiling 1, floor 5
      db %00000111, %00011111 ;ceiling 3, floor 5
      db %00001111, %00011111 ;ceiling 4, floor 5
      db %10000001, %00011111 ;ceiling 1, floor 6
      db %00000001, %00000000 ;ceiling 1, no floor
      db %10001111, %00011111 ;ceiling 4, floor 6
      db %11110001, %00011111 ;ceiling 1, floor 9
      db %11111001, %00011000 ;ceiling 1, middle 5, floor 2
      db %11110001, %00011000 ;ceiling 1, middle 4, floor 2
      db %11111111, %00011111 ;completely solid top to bottom

AREAPARSERCORE:
      lda !BackloadingFlag       ;check to see if we are starting right of START
      beq RENDERSCENERYTERRAIN  ;if not, go ahead and render background, foreground and terrain
      jsr PROCESSAREADATA       ;otherwise skip ahead and load level data

RENDERSCENERYTERRAIN:
          ldx #$0c
          lda #$00
CLRMTBUF: sta !MetatileBuffer,x       ;clear out metatile buffer
          dex
          bpl CLRMTBUF
          ldy !BackgroundScenery      ;do we need to render the background scenery?
          beq RENDFORE               ;if not, skip to check the foreground
          lda !CurrentPageLoc         ;otherwise check for every third page
THIRDP:   cmp #$03
          bmi RENDBACK               ;if less than three we're there
          sec
          sbc #$03                   ;if 3 or more, subtract 3 and 
          bpl THIRDP                 ;do an unconditional branch
RENDBACK: asl                        ;move results to higher nybble
          asl
          asl
          asl
          adc BSCENEDATAOFFSETS-1,y  ;add to it offset loaded from here
          adc !CurrentColumnPos       ;add to the result our current column position
          tax
          lda BACKSCENERYDATA,x      ;load data from sum of offsets
          beq RENDFORE               ;if zero, no scenery for that part
          pha
          and #$0f                   ;save to stack and clear high nybble
          sec
          sbc #$01                   ;subtract one (because low nybble is $01-$0c)
          sta $00                    ;save low nybble
          asl                        ;multiply by three (shift to left and add result to old one)
          adc $00                    ;note that since d7 was nulled, the carry flag is always clear
          tax                        ;save as offset for background scenery metatile data
          pla                        ;get high nybble from stack, move low
          lsr
          lsr
          lsr
          lsr
          tay                        ;use as second offset (used to determine height)
          lda #$03                   ;use previously saved memory location for counter
          sta $00
SCELOOP1: lda BACKSCENERYMETATILES,x ;load metatile data from offset of (lsb - 1) * 3
          sta !MetatileBuffer,y       ;store into buffer from offset of (msb / 16)
          inx
          iny
          cpy #$0b                   ;if at this location, leave loop
          beq RENDFORE
          dec $00                    ;decrement until counter expires, barring exception
          bne SCELOOP1
RENDFORE: ldx !ForegroundScenery      ;check for foreground data needed or not
          beq RENDTERR               ;if not, skip this part
          ldy FSCENEDATAOFFSETS-1,x  ;load offset from location offset by header value, then
          ldx #$00                   ;reinit X
SCELOOP2: lda FORESCENERYDATA,y      ;load data until counter expires
          beq NOFORE                 ;do not store if zero found
          sta !MetatileBuffer,x
NOFORE:   iny
          inx
          cpx #$0d                   ;store up to end of metatile buffer
          bne SCELOOP2
RENDTERR: ldy !AreaType               ;check world type for water level
          bne TERMTILE               ;if not water level, skip this part
          lda !WorldNumber            ;check world number, if not world number eight
          cmp #!World8                ;then skip this part
          bne TERMTILE
          lda #$62                   ;if set as water level and world number eight,
          jmp STOREMT                ;use castle wall metatile as terrain type
TERMTILE: lda TERRAINMETATILES,y     ;otherwise get appropriate metatile for area type
          ldy !CloudTypeOverride      ;check for cloud type override
          beq STOREMT                ;if not set, keep value otherwise
          lda #$88                   ;use cloud block terrain
STOREMT:  sta $07                    ;store value here
          ldx #$00                   ;initialize X, use as metatile buffer offset
          lda !TerrainControl         ;use yet another value from the header
          asl                        ;multiply by 2 and use as yet another offset
          tay
TERRLOOP: lda TERRAINRENDERBITS,y    ;get one of the terrain rendering bit data
          sta $00
          iny                        ;increment Y and use as offset next time around
          sty $01
          lda !CloudTypeOverride      ;skip if value here is zero
          beq NOCLOUD2
          cpx #$00                   ;otherwise, check if we're doing the ceiling byte
          beq NOCLOUD2
          lda $00                    ;if not, mask out all but d3
          and #%00001000
          sta $00
NOCLOUD2: ldy #$00                   ;START at beginning of BITMASKS
TERRBCHK: lda BITMASKS,y             ;load bitmask, then perform AND on contents of first byte
          bit $00
          beq NEXTTBIT               ;if not set, skip this part (do not write terrain to buffer)
          lda $07
          sta !MetatileBuffer,x       ;load terrain type metatile number and store into buffer here
NEXTTBIT: inx                        ;continue until end of buffer
          cpx #$0d
          beq RENDBBUF               ;if we're at the end, break out of this loop
          lda !AreaType               ;check world type for underground area
          cmp #$02
          bne ENDUCHK                ;if not underground, skip this part
          cpx #$0b
          bne ENDUCHK                ;if we're at the bottom of the screen, override
          lda #$54                   ;old terrain type with ground level terrain type
          sta $07
ENDUCHK:  iny                        ;increment BITMASKS offset in Y
          cpy #$08
          bne TERRBCHK               ;if not all bits checked, loop back    
          ldy $01
          bne TERRLOOP               ;unconditional branch, use Y to load next byte
RENDBBUF: jsr PROCESSAREADATA        ;do the area data loading routine now
          lda !BlockBufferColumnPos
          jsr GETBLOCKBUFFERADDR     ;get block buffer address from where we're at
          ldx #$00
          ldy #$00                   ;init index regs and START at beginning of smaller buffer
CHKMTLOW: sty $00
          lda !MetatileBuffer,x       ;load stored metatile number
          and #%11000000             ;mask out all but 2 MSB
          asl
          rol                        ;make %xx000000 into %000000xx
          rol
          tay                        ;use as offset in Y
          lda !MetatileBuffer,x       ;reload original unmasked value here
          cmp BLOCKBUFFLOWBOUNDS,y   ;check for certain values depending on bits set
          bcs STRBLOCK               ;if equal or greater, branch
          lda #$00                   ;if less, init value before storing
STRBLOCK: ldy $00                    ;get offset for block buffer
          sta ($06),y                ;store value into block buffer
          tya
          clc                        ;add 16 (move down one row) to offset
          adc #$10
          tay
          inx                        ;increment column value
          cpx #$0d
          bcc CHKMTLOW               ;continue until we pass last row, then leave
          rts

;numbers lower than these with the same attribute bits
;will not be stored in the block buffer
BLOCKBUFFLOWBOUNDS:
      db $10, $51, $88, $c0

;-------------------------------------------------------------------------------------
;$00 - used to store area object identifier
;$07 - used as adder to find proper area object code

PROCESSAREADATA:
            ldx #$02                 ;START at the end of area object buffer
PROCADLOOP: stx !ObjectOffset
            lda #$00                 ;reset flag
            sta !BehindAreaParserFlag
            ldy !AreaDataOffset       ;get offset of area data pointer
            lda (!AreaData),y         ;get first byte of area object
            cmp #$fd                 ;if end-of-area, skip all this crap
            beq RDYDECODE
            lda !AreaObjectLength,x   ;check area object buffer flag
            bpl RDYDECODE            ;if buffer not negative, branch, otherwise
            iny
            lda (!AreaData),y         ;get second byte of area object
            asl                      ;check for page select bit (d7), branch if not set
            bcc CHK1ROW13
            lda !AreaObjectPageSel    ;check page select
            bne CHK1ROW13
            inc !AreaObjectPageSel    ;if not already set, set it now
            inc !AreaObjectPageLoc    ;and increment page location
CHK1ROW13:  dey
            lda (!AreaData),y         ;reread first byte of level object
            and #$0f                 ;mask out high nybble
            cmp #$0d                 ;row 13?
            bne CHK1ROW14
            iny                      ;if so, reread second byte of level object
            lda (!AreaData),y
            dey                      ;decrement to get ready to read first byte
            and #%01000000           ;check for d6 set (if not, object is page control)
            bne CHECKREAR
            lda !AreaObjectPageSel    ;if page select is set, do not reread
            bne CHECKREAR
            iny                      ;if d6 not set, reread second byte
            lda (!AreaData),y
            and #%00011111           ;mask out all but 5 LSB and store in page control
            sta !AreaObjectPageLoc
            inc !AreaObjectPageSel    ;increment page select
            jmp NEXTAOBJ
CHK1ROW14:  cmp #$0e                 ;row 14?
            bne CHECKREAR
            lda !BackloadingFlag      ;check flag for saved page number and branch if set
            bne RDYDECODE            ;to render the object (otherwise bg might not look right)
CHECKREAR:  lda !AreaObjectPageLoc    ;check to see if current page of level object is
            cmp !CurrentPageLoc       ;behind current page of renderer
            bcc SETBEHIND            ;if so branch
RDYDECODE:  jsr DECODEAREADATA       ;do sub and do not turn on flag
            jmp CHKLENGTH
SETBEHIND:  inc !BehindAreaParserFlag ;turn on flag if object is behind renderer
NEXTAOBJ:   jsr INCAREAOBJOFFSET     ;increment buffer offset and move on
CHKLENGTH:  ldx !ObjectOffset         ;get buffer offset
            lda !AreaObjectLength,x   ;check object length for anything stored here
            bmi PROCLOOPB            ;if not, branch to handle loopback
            dec !AreaObjectLength,x   ;otherwise decrement length or get rid of it
PROCLOOPB:  dex                      ;decrement buffer offset
            bpl PROCADLOOP           ;and loopback unless exceeded buffer
            lda !BehindAreaParserFlag ;check for flag set if objects were behind renderer
            bne PROCESSAREADATA      ;branch if true to load more level data, otherwise
            lda !BackloadingFlag      ;check for flag set if starting right of page $00
            bne PROCESSAREADATA      ;branch if true to load more level data, otherwise leave
ENDAPARSE:  rts

INCAREAOBJOFFSET:
      inc !AreaDataOffset    ;increment offset of level pointer
      inc !AreaDataOffset
      lda #$00              ;reset page select
      sta !AreaObjectPageSel
      rts

DECODEAREADATA:
          lda !AreaObjectLength,x     ;check current buffer flag
          bmi CHK1STB
          ldy !AreaObjOffsetBuffer,x  ;if not, get offset from buffer
CHK1STB:  ldx #$10                   ;load offset of 16 for special row 15
          lda (!AreaData),y           ;get first byte of level object again
          cmp #$fd
          beq ENDAPARSE              ;if end of level, leave this routine
          and #$0f                   ;otherwise, mask out low nybble
          cmp #$0f                   ;row 15?
          beq CHKROW14               ;if so, keep the offset of 16
          ldx #$08                   ;otherwise load offset of 8 for special row 12
          cmp #$0c                   ;row 12?
          beq CHKROW14               ;if so, keep the offset value of 8
          ldx #$00                   ;otherwise nullify value by default
CHKROW14: stx $07                    ;store whatever value we just loaded here
          ldx !ObjectOffset           ;get object offset again
          cmp #$0e                   ;row 14?
          bne CHKROW13
          lda #$00                   ;if so, load offset with $00
          sta $07
          lda #$2e                   ;and load A with another value
          bne NORMOBJ                ;unconditional branch
CHKROW13: cmp #$0d                   ;row 13?
          bne CHKSROWS
          lda #$22                   ;if so, load offset with 34
          sta $07
          iny                        ;get next byte
          lda (!AreaData),y
          and #%01000000             ;mask out all but d6 (page control obj bit)
          beq LEAVEPAR               ;if d6 clear, branch to leave (we handled this earlier)
          lda (!AreaData),y           ;otherwise, get byte again
          and #%01111111             ;mask out d7
          cmp #$4b                   ;check for loop command in low nybble
          bne MASK2MSB               ;(plus d6 set for object other than page control)
          inc !LoopCommand            ;if loop command, set loop command flag
MASK2MSB: and #%00111111             ;mask out d7 and d6
          jmp NORMOBJ                ;and jump
CHKSROWS: cmp #$0c                   ;row 12-15?
          bcs SPECOBJ
          iny                        ;if not, get second byte of level object
          lda (!AreaData),y
          and #%01110000             ;mask out all but d6-d4
          bne LRGOBJ                 ;if any bits set, branch to handle large object
          lda #$16
          sta $07                    ;otherwise set offset of 24 for small object
          lda (!AreaData),y           ;reload second byte of level object
          and #%00001111             ;mask out higher nybble and jump
          jmp NORMOBJ
LRGOBJ:   sta $00                    ;store value here (branch for large objects)
          cmp #$70                   ;check for vertical pipe object
          bne NOTWPIPE
          lda (!AreaData),y           ;if not, reload second byte
          and #%00001000             ;mask out all but d3 (usage control bit)
          beq NOTWPIPE               ;if d3 clear, branch to get original value
          lda #$00                   ;otherwise, nullify value for warp pipe
          sta $00
NOTWPIPE: lda $00                    ;get value and jump ahead
          jmp MOVEAOID
SPECOBJ:  iny                        ;branch here for rows 12-15
          lda (!AreaData),y
          and #%01110000             ;get next byte and mask out all but d6-d4
MOVEAOID: lsr                        ;move d6-d4 to lower nybble
          lsr
          lsr
          lsr
NORMOBJ:  sta $00                    ;store value here (branch for small objects and rows 13 and 14)
          lda !AreaObjectLength,x     ;is there something stored here already?
          bpl RUNAOBJ                ;if so, branch to do its particular sub
          lda !AreaObjectPageLoc      ;otherwise check to see if the object we've loaded is on the
          cmp !CurrentPageLoc         ;same page as the renderer, and if so, branch
          beq INITREAR
          ldy !AreaDataOffset         ;if not, get old offset of level pointer
          lda (!AreaData),y           ;and reload first byte
          and #%00001111
          cmp #$0e                   ;row 14?
          bne LEAVEPAR
          lda !BackloadingFlag        ;if so, check backloading flag
          bne STRAOBJ                ;if set, branch to render object, else leave
LEAVEPAR: rts
INITREAR: lda !BackloadingFlag        ;check backloading flag to see if it's been initialized
          beq BACKCOLC               ;branch to column-wise check
          lda #$00                   ;if not, initialize both backloading and 
          sta !BackloadingFlag        ;behind-renderer flags and leave
          sta !BehindAreaParserFlag
          sta !ObjectOffset
LOOPCMDE: rts
BACKCOLC: ldy !AreaDataOffset         ;get first byte again
          lda (!AreaData),y
          and #%11110000             ;mask out low nybble and move high to low
          lsr
          lsr
          lsr
          lsr
          cmp !CurrentColumnPos       ;is this where we're at?
          bne LEAVEPAR               ;if not, branch to leave
STRAOBJ:  lda !AreaDataOffset         ;if so, load area obj offset and store in buffer
          sta !AreaObjOffsetBuffer,x
          jsr INCAREAOBJOFFSET       ;do sub to increment to next object data
RUNAOBJ:  lda $00                    ;get stored value and add offset to it
          clc                        ;then use the jump engine with current contents of A
          adc $07
          jsr JUMPENGINE

;large objects (rows $00-$0b or 00-11, d6-d4 set)
      dw VERTICALPIPE         ;used by warp pipes
      dw AREASTYLEOBJECT
      dw ROWOFBRICKS
      dw ROWOFSOLIDBLOCKS
      dw ROWOFCOINS
      dw COLUMNOFBRICKS
      dw COLUMNOFSOLIDBLOCKS
      dw VERTICALPIPE         ;used by decoration pipes

;objects for special row $0c or 12
      dw HOLE_EMPTY
      dw PULLEYROPEOBJECT
      dw BRIDGE_HIGH
      dw BRIDGE_MIDDLE
      dw BRIDGE_LOW
      dw HOLE_WATER
      dw QUESTIONBLOCKROW_HIGH
      dw QUESTIONBLOCKROW_LOW

;objects for special row $0f or 15
      dw ENDLESSROPE
      dw BALANCEPLATROPE
      dw CASTLEOBJECT
      dw STAIRCASEOBJECT
      dw EXITPIPE
      dw FLAGBALLS_RESIDUAL

;small objects (rows $00-$0b or 00-11, d6-d4 all clear)
      dw QUESTIONBLOCK     ;power-up
      dw QUESTIONBLOCK     ;coin
      dw QUESTIONBLOCK     ;hidden, coin
      dw HIDDEN1UPBLOCK    ;hidden, 1-up
      dw BRICKWITHITEM     ;brick, power-up
      dw BRICKWITHITEM     ;brick, vine
      dw BRICKWITHITEM     ;brick, star
      dw BRICKWITHCOINS    ;brick, coins
      dw BRICKWITHITEM     ;brick, 1-up
      dw WATERPIPE
      dw EMPTYBLOCK
      dw JUMPSPRING

;objects for special row $0d or 13 (d6 set)
      dw INTROPIPE
      dw FLAGPOLEOBJECT
      dw AXEOBJ
      dw CHAINOBJ
      dw CASTLEBRIDGEOBJ
      dw SCROLLLOCKOBJECT_WARP
      dw SCROLLLOCKOBJECT
      dw SCROLLLOCKOBJECT
      dw AREAFRENZY            ;flying cheep-cheeps 
      dw AREAFRENZY            ;bullet bills or swimming cheep-cheeps
      dw AREAFRENZY            ;stop frenzy
      dw LOOPCMDE

;object for special row $0e or 14
      dw ALTERAREAATTRIBUTES

;-------------------------------------------------------------------------------------
;(these apply to all area object subroutines in this section unless otherwise stated)
;$00 - used to store offset used to find object code
;$07 - starts with adder from area parser, used to store row offset

ALTERAREAATTRIBUTES:
         ldy !AreaObjOffsetBuffer,x ;load offset for level object data saved in buffer
         iny                       ;load second byte
         lda (!AreaData),y
         pha                       ;save in stack for now
         and #%01000000
         bne ALTER2                ;branch if d6 is set
         pla
         pha                       ;pull and push offset to copy to A
         and #%00001111            ;mask out high nybble and store as
         sta !TerrainControl        ;new terrain height type bits
         pla
         and #%00110000            ;pull and mask out all but d5 and d4
         lsr                       ;move bits to lower nybble and store
         lsr                       ;as new background scenery bits
         lsr
         lsr
         sta !BackgroundScenery     ;then leave
         rts
ALTER2:  pla
         and #%00000111            ;mask out all but 3 LSB
         cmp #$04                  ;if four or greater, set color control bits
         bcc SETFORE               ;and nullify foreground scenery bits
         sta !BackgroundColorCtrl
         lda #$00
SETFORE: sta !ForegroundScenery     ;otherwise set new foreground scenery bits
         rts

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

SCROLLLOCKOBJECT_WARP:
         ldx #$04            ;load value of 4 for game text routine as default
         lda !WorldNumber     ;warp zone (4-3-2), then check world number
         beq WARPNUM
         inx                 ;if world number > 1, increment for next warp zone (5)
         ldy !AreaType        ;check area type
         dey
         bne WARPNUM         ;if ground area type, increment for last warp zone
         inx                 ;(8-7-6) and move on
WARPNUM: txa
         sta !WarpZoneControl ;store number here to be used by warp zone routine
         jsr WRITEGAMETEXT   ;print text and warp zone numbers
         lda #!PiranhaPlant
         jsr KILLENEMIES     ;load identifier for piranha plants and do sub

SCROLLLOCKOBJECT:
      lda !ScrollLock      ;invert scroll lock to turn it on
      eor #%00000001
      sta !ScrollLock
      rts

;--------------------------------
;$00 - used to store enemy identifier in KILLENEMIES

KILLENEMIES:
           sta $00           ;store identifier here
           lda #$00
           ldx #$04          ;check for identifier in enemy object buffer
KILLELOOP: ldy !Enemy_ID,x
           cpy $00           ;if not found, branch
           bne NOKILLE
           sta !Enemy_Flag,x  ;if found, deactivate enemy object flag
NOKILLE:   dex               ;do this until all slots are checked
           bpl KILLELOOP
           rts

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

FRENZYIDDATA:
      db !FlyCheepCheepFrenzy, !BBill_CCheep_Frenzy, !Stop_Frenzy

AREAFRENZY:  ldx $00               ;use area object identifier bit as offset
             lda FRENZYIDDATA-8,x  ;note that it starts at 8, thus weird address here
             ldy #$05
FRECOMPLOOP: dey                   ;check regular slots of enemy object buffer
             bmi EXITAFRENZY       ;if all slots checked and enemy object not found, branch to store
             cmp !Enemy_ID,y    ;check for enemy object in buffer versus frenzy object
             bne FRECOMPLOOP
             lda #$00              ;if enemy object already present, nullify queue and leave
EXITAFRENZY: sta !EnemyFrenzyQueue  ;store enemy into frenzy queue
             rts

;--------------------------------
;$06 - used by MUSHROOMLEDGE to store length

AREASTYLEOBJECT:
      lda !AreaStyle        ;load level object style and jump to the right sub
      jsr JUMPENGINE 
      dw TREELEDGE        ;also used for cloud type levels
      dw MUSHROOMLEDGE
      dw BULLETBILLCANNON

TREELEDGE:
          jsr GETLRGOBJATTRIB     ;get row and length of green ledge
          lda !AreaObjectLength,x  ;check length counter for expiration
          beq ENDTREEL   
          bpl MIDTREEL
          tya
          sta !AreaObjectLength,x  ;store lower nybble into buffer flag as length of ledge
          lda !CurrentPageLoc
          ora !CurrentColumnPos    ;are we at the START of the level?
          beq MIDTREEL
          lda #$16                ;render START of tree ledge
          jmp NOUNDER
MIDTREEL: ldx $07
          lda #$17                ;render middle of tree ledge
          sta !MetatileBuffer,x    ;note that this is also used if ledge position is
          lda #$4c                ;at the START of level for continuous effect
          jmp ALLUNDER            ;now render the part underneath
ENDTREEL: lda #$18                ;render end of tree ledge
          jmp NOUNDER

MUSHROOMLEDGE:
          jsr CHKLRGOBJLENGTH        ;get shroom dimensions
          sty $06                    ;store length here for now
          bcc ENDMUSHL
          lda !AreaObjectLength,x     ;divide length by 2 and store elsewhere
          lsr
          sta !MushroomLedgeHalfLen,x
          lda #$19                   ;render START of mushroom
          jmp NOUNDER
ENDMUSHL: lda #$1b                   ;if at the end, render end of mushroom
          ldy !AreaObjectLength,x
          beq NOUNDER
          lda !MushroomLedgeHalfLen,x ;get divided length and store where length
          sta $06                    ;was stored originally
          ldx $07
          lda #$1a
          sta !MetatileBuffer,x       ;render middle of mushroom
          cpy $06                    ;are we smack dab in the center?
          bne MUSHLEXIT              ;if not, branch to leave
          inx
          lda #$4f
          sta !MetatileBuffer,x       ;render stem top of mushroom underneath the middle
          lda #$50
ALLUNDER: inx
          ldy #$0f                   ;set $0f to render all way down
          jmp RENDERUNDERPART        ;now render the stem of mushroom
NOUNDER:  ldx $07                    ;load row of ledge
          ldy #$00                   ;set 0 for no bottom on this part
          jmp RENDERUNDERPART

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

;tiles used by pulleys and rope object
PULLEYROPEMETATILES:
      db $42, $41, $43

PULLEYROPEOBJECT:
           jsr CHKLRGOBJLENGTH       ;get length of pulley/rope object
           ldy #$00                  ;initialize metatile offset
           bcs RENDERPUL             ;if starting, render left pulley
           iny
           lda !AreaObjectLength,x    ;if not at the end, render rope
           bne RENDERPUL
           iny                       ;otherwise render right pulley
RENDERPUL: lda PULLEYROPEMETATILES,y
           sta !MetatileBuffer        ;render at the top of the screen
MUSHLEXIT: rts                       ;and leave

;--------------------------------
;$06 - used to store upper limit of rows for CASTLEOBJECT

CASTLEMETATILES:
      db $00, $45, $45, $45, $00
      db $00, $48, $47, $46, $00
      db $45, $49, $49, $49, $45
      db $47, $47, $4a, $47, $47
      db $47, $47, $4b, $47, $47
      db $49, $49, $49, $49, $49
      db $47, $4a, $47, $4a, $47
      db $47, $4b, $47, $4b, $47
      db $47, $47, $47, $47, $47
      db $4a, $47, $4a, $47, $4a
      db $4b, $47, $4b, $47, $4b

CASTLEOBJECT:
            jsr GETLRGOBJATTRIB      ;save lower nybble as starting row
            sty $07                  ;if starting row is above $0a, game will crash!!!
            ldy #$04
            jsr CHKLRGOBJFIXEDLENGTH ;load length of castle if not already loaded
            txa                  
            pha                      ;save obj buffer offset to stack
            ldy !AreaObjectLength,x   ;use current length as offset for castle data
            ldx $07                  ;begin at starting row
            lda #$0b
            sta $06                  ;load upper limit of number of rows to print
CRENDLOOP:  lda CASTLEMETATILES,y    ;load current byte using offset
            sta !MetatileBuffer,x
            inx                      ;store in buffer and increment buffer offset
            lda $06
            beq CHKCFLOOR            ;have we reached upper limit yet?
            iny                      ;if not, increment column-wise
            iny                      ;to byte in next row
            iny
            iny
            iny
            dec $06                  ;move closer to upper limit
CHKCFLOOR:  cpx #$0b                 ;have we reached the row just before floor?
            bne CRENDLOOP            ;if not, go back and do another row
            pla
            tax                      ;get obj buffer offset from before
            lda !CurrentPageLoc
            beq EXITCASTLE           ;if we're at page 0, we do not need to do anything else
            lda !AreaObjectLength,x   ;check length
            cmp #$01                 ;if length almost about to expire, put brick at floor
            beq PLAYERSTOP
            ldy $07                  ;check starting row for tall castle ($00)
            bne NOTTALL
            cmp #$03                 ;if found, then check to see if we're at the second column
            beq PLAYERSTOP
NOTTALL:    cmp #$02                 ;if not tall castle, check to see if we're at the third column
            bne EXITCASTLE           ;if we aren't and the castle is tall, don't create flag yet
            jsr GETAREAOBJXPOSITION  ;otherwise, obtain and save horizontal pixel coordinate
            pha
            jsr FINDEMPTYENEMYSLOT   ;find an empty place on the enemy object buffer
            pla
            sta !Enemy_X_Position,x   ;then write horizontal coordinate for star flag
            lda !CurrentPageLoc
            sta !Enemy_PageLoc,x      ;set page location for star flag
            lda #$01
            sta !Enemy_Y_HighPos,x    ;set vertical high byte
            sta !Enemy_Flag,x         ;set flag for buffer
            lda #$90
            sta !Enemy_Y_Position,x   ;set vertical coordinate
            lda #!StarFlagObject      ;set star flag value in buffer itself
            sta !Enemy_ID,x
            rts
PLAYERSTOP: ldy #$52                 ;put brick at floor to stop player at end of level
            sty !MetatileBuffer+10    ;this is only done if we're on the second column
EXITCASTLE: rts

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

WATERPIPE:
      jsr GETLRGOBJATTRIB     ;get row and lower nybble
      ldy !AreaObjectLength,x  ;get length (residual code, water pipe is 1 col thick)
      ldx $07                 ;get row
      lda #$6b
      sta !MetatileBuffer,x    ;draw something here and below it
      lda #$6c
      sta !MetatileBuffer+1,x
      rts

;--------------------------------
;$05 - used to store length of vertical shaft in RENDERSIDEWAYSPIPE
;$06 - used to store leftover horizontal length in RENDERSIDEWAYSPIPE
; and vertical length in VERTICALPIPE and GETPIPEHEIGHT

INTROPIPE:
               ldy #$03                 ;check if length set, if not set, set it
               jsr CHKLRGOBJFIXEDLENGTH
               ldy #$0a                 ;set fixed value and render the sideways part
               jsr RENDERSIDEWAYSPIPE
               bcs NOBLANKP             ;if carry flag set, not time to draw vertical pipe part
               ldx #$06                 ;blank everything above the vertical pipe part
VPIPESECTLOOP: lda #$00                 ;all the way to the top of the screen
               sta !MetatileBuffer,x     ;because otherwise it will look like exit pipe
               dex
               bpl VPIPESECTLOOP
               lda VERTICALPIPEDATA,y   ;draw the end of the vertical pipe part
               sta !MetatileBuffer+7
NOBLANKP:      rts

SIDEPIPESHAFTDATA:
      db $15, $14  ;used to control whether or not vertical pipe shaft
      db $00, $00  ;is drawn, and if so, controls the metatile number
SIDEPIPETOPPART:
      db $15, $1e  ;top part of sideways part of pipe
      db $1d, $1c
SIDEPIPEBOTTOMPART: 
      db $15, $21  ;bottom part of sideways part of pipe
      db $20, $1f

EXITPIPE:
      ldy #$03                 ;check if length set, if not set, set it
      jsr CHKLRGOBJFIXEDLENGTH
      jsr GETLRGOBJATTRIB      ;get vertical length, then plow on through RENDERSIDEWAYSPIPE

RENDERSIDEWAYSPIPE:
              dey                       ;decrement twice to make room for shaft at bottom
              dey                       ;and store here for now as vertical length
              sty $05
              ldy !AreaObjectLength,x    ;get length left over and store here
              sty $06
              ldx $05                   ;get vertical length plus one, use as buffer offset
              inx
              lda SIDEPIPESHAFTDATA,y   ;check for value $00 based on horizontal offset
              cmp #$00
              beq DRAWSIDEPART          ;if found, do not draw the vertical pipe shaft
              ldx #$00
              ldy $05                   ;init buffer offset and get vertical length
              jsr RENDERUNDERPART       ;and render vertical shaft using tile number in A
              clc                       ;clear carry flag to be used by INTROPIPE
DRAWSIDEPART: ldy $06                   ;render side pipe part at the bottom
              lda SIDEPIPETOPPART,y
              sta !MetatileBuffer,x      ;note that the pipe parts are stored
              lda SIDEPIPEBOTTOMPART,y  ;backwards horizontally
              sta !MetatileBuffer+1,x
              rts

VERTICALPIPEDATA:
      db $11, $10 ;used by pipes that lead somewhere
      db $15, $14
      db $13, $12 ;used by decoration pipes
      db $15, $14

VERTICALPIPE:
          jsr GETPIPEHEIGHT
          lda $00                  ;check to see if value was nullified earlier
          beq WARPPIPE             ;(if d3, the usage control bit of second byte, was set)
          iny
          iny
          iny
          iny                      ;add four if usage control bit was not set
WARPPIPE: tya                      ;save value in stack
          pha
          lda !AreaNumber
          ora !WorldNumber          ;if at world 1-1, do not add piranha plant ever
          beq DRAWPIPE
          ldy !AreaObjectLength,x   ;if on second column of pipe, branch
          beq DRAWPIPE             ;(because we only need to do this once)
          jsr FINDEMPTYENEMYSLOT   ;check for an empty moving data buffer space
          bcs DRAWPIPE             ;if not found, too many enemies, thus skip
          jsr GETAREAOBJXPOSITION  ;get horizontal pixel coordinate
          clc
          adc #$08                 ;add eight to put the piranha plant in the center
          sta !Enemy_X_Position,x   ;store as enemy's horizontal coordinate
          lda !CurrentPageLoc       ;add carry to current page number
          adc #$00
          sta !Enemy_PageLoc,x      ;store as enemy's page coordinate
          lda #$01
          sta !Enemy_Y_HighPos,x
          sta !Enemy_Flag,x         ;activate enemy flag
          jsr GETAREAOBJYPOSITION  ;get piranha plant's vertical coordinate and store here
          sta !Enemy_Y_Position,x
          lda #!PiranhaPlant        ;write piranha plant's value into buffer
          sta !Enemy_ID,x
          jsr INITPIRANHAPLANT
DRAWPIPE: pla                      ;get value saved earlier and use as Y
          tay
          ldx $07                  ;get buffer offset
          lda VERTICALPIPEDATA,y   ;draw the appropriate pipe with the Y we loaded earlier
          sta !MetatileBuffer,x     ;render the top of the pipe
          inx
          lda VERTICALPIPEDATA+2,y ;render the REST of the pipe
          ldy $06                  ;subtract one from length and render the part underneath
          dey
          jmp RENDERUNDERPART
      
GETPIPEHEIGHT:
      ldy #$01       ;check for length loaded, if not, load
      jsr CHKLRGOBJFIXEDLENGTH ;pipe length of 2 (horizontal)
      jsr GETLRGOBJATTRIB
      tya            ;get saved lower nybble as height
      and #$07       ;save only the three lower bits as
      sta $06        ;vertical length, then load Y with
      ldy !AreaObjectLength,x    ;length left over
      rts

FINDEMPTYENEMYSLOT:
              ldx #$00          ;START at first enemy slot
EMPTYCHKLOOP: clc               ;clear carry flag by default
              lda !Enemy_Flag,x  ;check enemy buffer for nonzero
              beq EXITEMPTYCHK  ;if zero, leave
              inx
              cpx #$05          ;if nonzero, check next value
              bne EMPTYCHKLOOP
EXITEMPTYCHK: rts               ;if all values nonzero, carry flag is set

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

HOLE_WATER:
      jsr CHKLRGOBJLENGTH   ;get low nybble and save as length
      lda #$86              ;render waves
      sta !MetatileBuffer+10
      ldx #$0b
      ldy #$01              ;now render the water underneath
      lda #$87
      jmp RENDERUNDERPART

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

QUESTIONBLOCKROW_HIGH:
      lda #$03    ;START on the fourth row
	db $2c     ;BIT instruction opcode

QUESTIONBLOCKROW_LOW:
      lda #$07             ;START on the eighth row
      pha                  ;save whatever row to the stack for now
      jsr CHKLRGOBJLENGTH  ;get low nybble and save as length
      pla
      tax                  ;render question boxes with coins
      lda #$c0
      sta !MetatileBuffer,x
      rts

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

BRIDGE_HIGH:
      lda #$06  ;START on the seventh row from top of screen
      db $2c   ;BIT instruction opcode

BRIDGE_MIDDLE:
      lda #$07  ;START on the eighth row
      db $2c   ;BIT instruction opcode

BRIDGE_LOW:
      lda #$09             ;START on the tenth row
      pha                  ;save whatever row to the stack for now
      jsr CHKLRGOBJLENGTH  ;get low nybble and save as length
      pla
      tax                  ;render bridge railing
      lda #$0b
      sta !MetatileBuffer,x
      inx
      ldy #$00             ;now render the bridge itself
      lda #$63
      jmp RENDERUNDERPART

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

FLAGBALLS_RESIDUAL:
      jsr GETLRGOBJATTRIB  ;get low nybble from object byte
      ldx #$02             ;render flag balls on third row from top
      lda #$6d             ;of screen downwards based on low nybble
      jmp RENDERUNDERPART

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

FLAGPOLEOBJECT:
      lda #$24                 ;render flagpole ball on top
      sta !MetatileBuffer
      ldx #$01                 ;now render the flagpole shaft
      ldy #$08
      lda #$25
      jsr RENDERUNDERPART
      lda #$61                 ;render solid block at the bottom
      sta !MetatileBuffer+10
      jsr GETAREAOBJXPOSITION
      sec                      ;get pixel coordinate of where the flagpole is,
      sbc #$08                 ;subtract eight pixels and use as horizontal
      sta !Enemy_X_Position+5   ;coordinate for the flag
      lda !CurrentPageLoc
      sbc #$00                 ;subtract borrow from page location and use as
      sta !Enemy_PageLoc+5      ;page location for the flag
      lda #$30
      sta !Enemy_Y_Position+5   ;set vertical coordinate for flag
      lda #$b0
      sta !FlagpoleFNum_Y_Pos   ;set initial vertical coordinate for flagpole's floatey number
      lda #!FlagpoleFlagObject
      sta !Enemy_ID+5           ;set flag identifier, note that identifier and coordinates
      inc !Enemy_Flag+5         ;use last space in enemy object buffer
      rts

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

ENDLESSROPE:
      ldx #$00       ;render rope from the top to the bottom of screen
      ldy #$0f
      jmp DRAWROPE

BALANCEPLATROPE:
          txa                 ;save object buffer offset for now
          pha
          ldx #$01            ;blank out all from second row to the bottom
          ldy #$0f            ;with blank used for balance platform rope
          lda #$44
          jsr RENDERUNDERPART
          pla                 ;get back object buffer offset
          tax
          jsr GETLRGOBJATTRIB ;get vertical length from lower nybble
          ldx #$01
DRAWROPE: lda #$40            ;render the actual rope
          jmp RENDERUNDERPART

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

COINMETATILEDATA:
      db $c3, $c2, $c2, $c2

ROWOFCOINS:
      ldy !AreaType            ;get area type
      lda COINMETATILEDATA,y  ;load appropriate coin metatile
      jmp GETROW

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

C_OBJECTROW:
      db $06, $07, $08

C_OBJECTMETATILE:
      db $c5, $0c, $89

CASTLEBRIDGEOBJ:
      ldy #$0c                  ;load length of 13 columns
      jsr CHKLRGOBJFIXEDLENGTH
      jmp CHAINOBJ

AXEOBJ:
      lda #$08                  ;load bowser's palette into sprite portion of palette
      sta !VRAM_Buffer_AddrCtrl

CHAINOBJ:
      ldy $00                   ;get value loaded earlier from decoder
      ldx C_OBJECTROW-2,y       ;get appropriate row and metatile for object
      lda C_OBJECTMETATILE-2,y
      jmp COLOBJ

EMPTYBLOCK:
        jsr GETLRGOBJATTRIB  ;get row location
        ldx $07
        lda #$c4
COLOBJ: ldy #$00             ;column length of 1
        jmp RENDERUNDERPART

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

SOLIDBLOCKMETATILES:
      db $69, $61, $61, $62

BRICKMETATILES:
      db $22, $51, $52, $52
      db $88 ;used only by row of bricks object

ROWOFBRICKS:
            ldy !AreaType           ;load area type obtained from area offset pointer
            lda !CloudTypeOverride  ;check for cloud type override
            beq DRAWBRICKS
            ldy #$04               ;if cloud type, override area type
DRAWBRICKS: lda BRICKMETATILES,y   ;get appropriate metatile
            jmp GETROW             ;and go render it

ROWOFSOLIDBLOCKS:
         ldy !AreaType               ;load area type obtained from area offset pointer
         lda SOLIDBLOCKMETATILES,y  ;get metatile
GETROW:  pha                        ;store metatile here
         jsr CHKLRGOBJLENGTH        ;get row number, load length
DRAWROW: ldx $07
         ldy #$00                   ;set vertical height of 1
         pla
         jmp RENDERUNDERPART        ;render object

COLUMNOFBRICKS:
      ldy !AreaType          ;load area type obtained from area offset
      lda BRICKMETATILES,y  ;get metatile (no cloud override as for row)
      jmp GETROW2

COLUMNOFSOLIDBLOCKS:
         ldy !AreaType               ;load area type obtained from area offset
         lda SOLIDBLOCKMETATILES,y  ;get metatile
GETROW2: pha                        ;save metatile to stack for now
         jsr GETLRGOBJATTRIB        ;get length and row
         pla                        ;restore metatile
         ldx $07                    ;get starting row
         jmp RENDERUNDERPART        ;now render the column

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

BULLETBILLCANNON:
             jsr GETLRGOBJATTRIB      ;get row and length of bullet bill cannon
             ldx $07                  ;START at first row
             lda #$64                 ;render bullet bill cannon
             sta !MetatileBuffer,x
             inx
             dey                      ;done yet?
             bmi SETUPCANNON
             lda #$65                 ;if not, render middle part
             sta !MetatileBuffer,x
             inx
             dey                      ;done yet?
             bmi SETUPCANNON
             lda #$66                 ;if not, render bottom until length expires
             jsr RENDERUNDERPART
SETUPCANNON: ldx !Cannon_Offset        ;get offset for data used by cannons and whirlpools
             jsr GETAREAOBJYPOSITION  ;get proper vertical coordinate for cannon
             sta !Cannon_Y_Position,x  ;and store it here
             lda !CurrentPageLoc
             sta !Cannon_PageLoc,x     ;store page number for cannon here
             jsr GETAREAOBJXPOSITION  ;get proper horizontal coordinate for cannon
             sta !Cannon_X_Position,x  ;and store it here
             inx
             cpx #$06                 ;increment and check offset
             bcc STRCOFFSET           ;if not yet reached sixth cannon, branch to save offset
             ldx #$00                 ;otherwise initialize it
STRCOFFSET:  stx !Cannon_Offset        ;save new offset and leave
             rts

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

STAIRCASEHEIGHTDATA:
      db $07, $07, $06, $05, $04, $03, $02, $01, $00

STAIRCASEROWDATA:
      db $03, $03, $04, $05, $06, $07, $08, $09, $0a

STAIRCASEOBJECT:
           jsr CHKLRGOBJLENGTH       ;check and load length
           bcc NEXTSTAIR             ;if length already loaded, skip init part
           lda #$09                  ;START past the end for the bottom
           sta !StaircaseControl      ;of the staircase
NEXTSTAIR: dec !StaircaseControl      ;move onto next step (or first if starting)
           ldy !StaircaseControl
           ldx STAIRCASEROWDATA,y    ;get starting row and height to render
           lda STAIRCASEHEIGHTDATA,y
           tay
           lda #$61                  ;now render solid block staircase
           jmp RENDERUNDERPART

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

JUMPSPRING:
      jsr GETLRGOBJATTRIB
      jsr FINDEMPTYENEMYSLOT      ;find empty space in enemy object buffer
      jsr GETAREAOBJXPOSITION     ;get horizontal coordinate for JUMPSPRING
      sta !Enemy_X_Position,x      ;and store
      lda !CurrentPageLoc          ;store page location of JUMPSPRING
      sta !Enemy_PageLoc,x
      jsr GETAREAOBJYPOSITION     ;get vertical coordinate for JUMPSPRING
      sta !Enemy_Y_Position,x      ;and store
      sta !Jumpspring_FixedYPos,x  ;store as permanent coordinate here
      lda #!JumpspringObject
      sta !Enemy_ID,x              ;write JUMPSPRING object to enemy object buffer
      ldy #$01
      sty !Enemy_Y_HighPos,x       ;store vertical high byte
      inc !Enemy_Flag,x            ;set flag for enemy object buffer
      ldx $07
      lda #$67                    ;draw metatiles in two rows where JUMPSPRING is
      sta !MetatileBuffer,x
      lda #$68
      sta !MetatileBuffer+1,x
      rts

;--------------------------------
;$07 - used to save ID of brick object

HIDDEN1UPBLOCK:
      lda !Hidden1UpFlag  ;if flag not set, do not render object
      beq EXITDECBLOCK
      lda #$00           ;if set, init for the next one
      sta !Hidden1UpFlag
      jmp BRICKWITHITEM  ;jump to code shared with unbreakable bricks

QUESTIONBLOCK:
      jsr GETAREAOBJECTID ;get value from level decoder routine
      jmp DRAWQBLK        ;go to render it

BRICKWITHCOINS:
      lda #$00                 ;initialize multi-coin timer flag
      sta !BrickCoinTimerFlag

BRICKWITHITEM:
          jsr GETAREAOBJECTID         ;save area object ID
          sty $07              
          lda #$00                    ;load default adder for bricks with lines
          ldy !AreaType                ;check level type for ground level
          dey
          beq BWITHL                  ;if ground type, do not START with 5
          lda #$05                    ;otherwise use adder for bricks without lines
BWITHL:   clc                         ;add object ID to adder
          adc $07
          tay                         ;use as offset for metatile
DRAWQBLK: lda BRICKQBLOCKMETATILES,y  ;get appropriate metatile for brick (question block
          pha                         ;if branched to here from question block routine)
          jsr GETLRGOBJATTRIB         ;get row from location byte
          jmp DRAWROW                 ;now render the object

GETAREAOBJECTID:
              lda $00    ;get value saved from area parser routine
              sec
              sbc #$00   ;possibly residual code
              tay        ;save to Y
EXITDECBLOCK: rts

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

HOLEMETATILES:
      db $87, $00, $00, $00

HOLE_EMPTY:
            jsr CHKLRGOBJLENGTH          ;get lower nybble and save as length
            bcc NOWHIRLP                 ;skip this part if length already loaded
            lda !AreaType                 ;check for water type level
            bne NOWHIRLP                 ;if not water type, skip this part
            ldx !Whirlpool_Offset         ;get offset for data used by cannons and whirlpools
            jsr GETAREAOBJXPOSITION      ;get proper vertical coordinate of where we're at
            sec
            sbc #$10                     ;subtract 16 pixels
            sta !Whirlpool_LeftExtent,x   ;store as left extent of whirlpool
            lda !CurrentPageLoc           ;get page location of where we're at
            sbc #$00                     ;subtract borrow
            sta !Whirlpool_PageLoc,x      ;save as page location of whirlpool
            iny
            iny                          ;increment length by 2
            tya
            asl                          ;multiply by 16 to get size of whirlpool
            asl                          ;note that whirlpool will always be
            asl                          ;two blocks bigger than actual size of hole
            asl                          ;and extend one block beyond each edge
            sta !Whirlpool_Length,x       ;save size of whirlpool here
            inx
            cpx #$05                     ;increment and check offset
            bcc STRWOFFSET               ;if not yet reached fifth whirlpool, branch to save offset
            ldx #$00                     ;otherwise initialize it
STRWOFFSET: stx !Whirlpool_Offset         ;save new offset here
NOWHIRLP:   ldx !AreaType                 ;get appropriate metatile, then
            lda HOLEMETATILES,x          ;render the hole proper
            ldx #$08
            ldy #$0f                     ;START at ninth row and go to bottom, run RENDERUNDERPART

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

RENDERUNDERPART:
             sty !AreaObjectHeight  ;store vertical length to render
             ldy !MetatileBuffer,x  ;check current spot to see if there's something
             beq DRAWTHISROW       ;we need to keep, if nothing, go ahead
             cpy #$17
             beq WAITONEROW        ;if middle part (tree ledge), wait until next row
             cpy #$1a
             beq WAITONEROW        ;if middle part (mushroom ledge), wait until next row
             cpy #$c0
             beq DRAWTHISROW       ;if question block w/ coin, overwrite
             cpy #$c0
             bcs WAITONEROW        ;if any other metatile with palette 3, wait until next row
             cpy #$54
             bne DRAWTHISROW       ;if cracked rock terrain, overwrite
             cmp #$50
             beq WAITONEROW        ;if stem top of mushroom, wait until next row
DRAWTHISROW: sta !MetatileBuffer,x  ;render contents of A from routine that called this
WAITONEROW:  inx
             cpx #$0d              ;stop rendering if we're at the bottom of the screen
             bcs EXITUPARTR
             ldy !AreaObjectHeight  ;decrement, and stop rendering if there is no more length
             dey
             bpl RENDERUNDERPART
EXITUPARTR:  rts

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

CHKLRGOBJLENGTH:
        jsr GETLRGOBJATTRIB     ;get row location and size (length if branched to from here)

CHKLRGOBJFIXEDLENGTH:
        lda !AreaObjectLength,x  ;check for set length counter
        clc                     ;clear carry flag for not just starting
        bpl LENSET              ;if counter not set, load it, otherwise leave alone
        tya                     ;save length into length counter
        sta !AreaObjectLength,x
        sec                     ;set carry flag if just starting
LENSET: rts

GETLRGOBJATTRIB:
      ldy !AreaObjOffsetBuffer,x ;get offset saved from area obj decoding routine
      lda (!AreaData),y          ;get first byte of level object
      and #%00001111
      sta $07                   ;save row location
      iny
      lda (!AreaData),y          ;get next byte, save lower nybble (length or height)
      and #%00001111            ;as Y, then leave
      tay
      rts

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

GETAREAOBJXPOSITION:
      lda !CurrentColumnPos    ;multiply current offset where we're at by 16
      asl                     ;to obtain horizontal pixel coordinate
      asl
      asl
      asl
      rts

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

GETAREAOBJYPOSITION:
      lda $07  ;multiply value by 16
      asl
      asl      ;this will give us the proper vertical pixel coordinate
      asl
      asl
      clc
      adc #32  ;add 32 pixels for the status bar
      rts

;-------------------------------------------------------------------------------------
;$06-$07 - used to store block buffer address used as indirect

BLOCKBUFFERADDR:
      db !Block_Buffer_1, !Block_Buffer_2
      db !Block_Buffer_1>>8, !Block_Buffer_2>>8

GETBLOCKBUFFERADDR:
      pha                      ;take value of A, save
      lsr                      ;move high nybble to low
      lsr
      lsr
      lsr
      tay                      ;use nybble as pointer to high byte
      lda BLOCKBUFFERADDR+2,y  ;of indirect here
      sta $07
      pla
      and #%00001111           ;pull from stack, mask out high nybble
      clc
      adc BLOCKBUFFERADDR,y    ;add to low byte
      sta $06                  ;store here and leave
      rts

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

;unused space
      db $ff, $ff

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

AREADATAOFSLOOPBACK:
      db $12, $36, $0e, $0e, $0e, $32, $32, $32, $0a, $26, $40

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

LOADAREAPOINTER:
             jsr FINDAREAPOINTER  ;find it and store it here
             sta !AreaPointer
GETAREATYPE: and #%01100000       ;mask out all but d6 and d5
             asl
             rol
             rol
             rol                  ;make %0xx00000 into %000000xx
             sta !AreaType         ;save 2 MSB as area type
             rts

FINDAREAPOINTER:
      ldy !WorldNumber        ;load offset from world variable
      lda WORLDADDROFFSETS,y
      clc                    ;add area number used to find data
      adc !AreaNumber
      tay
      lda AREAADDROFFSETS,y  ;from there we have our area pointer
      rts


GETAREADATAADDRS:
            lda !AreaPointer          ;use 2 MSB for Y
            jsr GETAREATYPE
            tay
            lda !AreaPointer          ;mask out all but 5 LSB
            and #%00011111
            sta !AreaAddrsLOffset     ;save as low offset
            lda ENEMYADDRHOFFSETS,y  ;load base value with 2 altered MSB,
            clc                      ;then add base value to 5 LSB, result
            adc !AreaAddrsLOffset     ;becomes offset for level data
            tay
            lda ENEMYDATAADDRLOW,y   ;use offset to load pointer
            sta !EnemyDataLow
            lda ENEMYDATAADDRHIGH,y
            sta !EnemyDataHigh
            ldy !AreaType             ;use area type as offset
            lda AREADATAHOFFSETS,y   ;do the same thing but with different base value
            clc
            adc !AreaAddrsLOffset        
            tay
            lda AREADATAADDRLOW,y    ;use this offset to load another pointer
            sta !AreaDataLow
            lda AREADATAADDRHIGH,y
            sta !AreaDataHigh
            ldy #$00                 ;load first byte of header
            lda (!AreaData),y     
            pha                      ;save it to the stack for now
            and #%00000111           ;save 3 LSB for foreground scenery or bg color control
            cmp #$04
            bcc STOREFORE
            sta !BackgroundColorCtrl  ;if 4 or greater, save value here as bg color control
            lda #$00
STOREFORE:  sta !ForegroundScenery    ;if less, save value here as foreground scenery
            pla                      ;pull byte from stack and push it back
            pha
            and #%00111000           ;save player entrance control bits
            lsr                      ;shift bits over to LSBs
            lsr
            lsr
            sta !PlayerEntranceCtrl       ;save value here as player entrance control
            pla                      ;pull byte again but do not push it back
            and #%11000000           ;save 2 MSB for game timer setting
            clc
            rol                      ;rotate bits over to LSBs
            rol
            rol
            sta !GameTimerSetting     ;save value here as game timer setting
            iny
            lda (!AreaData),y         ;load second byte of header
            pha                      ;save to stack
            and #%00001111           ;mask out all but lower nybble
            sta !TerrainControl
            pla                      ;pull and push byte to copy it to A
            pha
            and #%00110000           ;save 2 MSB for background scenery type
            lsr
            lsr                      ;shift bits to LSBs
            lsr
            lsr
            sta !BackgroundScenery    ;save as background scenery
            pla           
            and #%11000000
            clc
            rol                      ;rotate bits over to LSBs
            rol
            rol
            cmp #%00000011           ;if set to 3, store here
            bne STORESTYLE           ;and nullify other value
            sta !CloudTypeOverride    ;otherwise store value in other place
            lda #$00
STORESTYLE: sta !AreaStyle
            lda !AreaDataLow          ;increment area data address by 2 bytes
            clc
            adc #$02
            sta !AreaDataLow
            lda !AreaDataHigh
            adc #$00
            sta !AreaDataHigh
            rts

;-------------------------------------------------------------------------------------
;GAME LEVELS DATA

WORLDADDROFFSETS:
      db WORLD1AREAS-AREAADDROFFSETS, WORLD2AREAS-AREAADDROFFSETS
      db WORLD3AREAS-AREAADDROFFSETS, WORLD4AREAS-AREAADDROFFSETS
      db WORLD5AREAS-AREAADDROFFSETS, WORLD6AREAS-AREAADDROFFSETS
      db WORLD7AREAS-AREAADDROFFSETS, WORLD8AREAS-AREAADDROFFSETS

AREAADDROFFSETS:
WORLD1AREAS: db $25, $29, $c0, $26, $60
WORLD2AREAS: db $28, $29, $01, $27, $62
WORLD3AREAS: db $24, $35, $20, $63
WORLD4AREAS: db $22, $29, $41, $2c, $61
WORLD5AREAS: db $2a, $31, $26, $62
WORLD6AREAS: db $2e, $23, $2d, $60
WORLD7AREAS: db $33, $29, $01, $27, $64
WORLD8AREAS: db $30, $32, $21, $65

;bonus area data offsets, included here for comparison purposes
;underground bonus area  - c2
;cloud area 1 (day)      - 2b
;cloud area 2 (night)    - 34
;water area (5-2/6-2)    - 00
;water area (8-4)        - 02
;warp zone area (4-2)    - 2f

ENEMYADDRHOFFSETS:
      db $1f, $06, $1c, $00

ENEMYDATAADDRLOW:
      db E_CASTLEAREA1, E_CASTLEAREA2, E_CASTLEAREA3, E_CASTLEAREA4, E_CASTLEAREA5, E_CASTLEAREA6
      db E_GROUNDAREA1, E_GROUNDAREA2, E_GROUNDAREA3, E_GROUNDAREA4, E_GROUNDAREA5, E_GROUNDAREA6
      db E_GROUNDAREA7, E_GROUNDAREA8, E_GROUNDAREA9, E_GROUNDAREA10, E_GROUNDAREA11, E_GROUNDAREA12
      db E_GROUNDAREA13, E_GROUNDAREA14, E_GROUNDAREA15, E_GROUNDAREA16, E_GROUNDAREA17, E_GROUNDAREA18
      db E_GROUNDAREA19, E_GROUNDAREA20, E_GROUNDAREA21, E_GROUNDAREA22, E_UNDERGROUNDAREA1
      db E_UNDERGROUNDAREA2, E_UNDERGROUNDAREA3, E_WATERAREA1, E_WATERAREA2, E_WATERAREA3

ENEMYDATAADDRHIGH:
      db E_CASTLEAREA1>>8, E_CASTLEAREA2>>8, E_CASTLEAREA3>>8, E_CASTLEAREA4>>8, E_CASTLEAREA5>>8, E_CASTLEAREA6>>8
      db E_GROUNDAREA1>>8, E_GROUNDAREA2>>8, E_GROUNDAREA3>>8, E_GROUNDAREA4>>8, E_GROUNDAREA5>>8, E_GROUNDAREA6>>8
      db E_GROUNDAREA7>>8, E_GROUNDAREA8>>8, E_GROUNDAREA9>>8, E_GROUNDAREA10>>8, E_GROUNDAREA11>>8, E_GROUNDAREA12>>8
      db E_GROUNDAREA13>>8, E_GROUNDAREA14>>8, E_GROUNDAREA15>>8, E_GROUNDAREA16>>8, E_GROUNDAREA17>>8, E_GROUNDAREA18>>8
      db E_GROUNDAREA19>>8, E_GROUNDAREA20>>8, E_GROUNDAREA21>>8, E_GROUNDAREA22>>8, E_UNDERGROUNDAREA1>>8
      db E_UNDERGROUNDAREA2>>8, E_UNDERGROUNDAREA3>>8, E_WATERAREA1>>8, E_WATERAREA2>>8, E_WATERAREA3>>8

AREADATAHOFFSETS:
      db $00, $03, $19, $1c

AREADATAADDRLOW:
      db L_WATERAREA1, L_WATERAREA2, L_WATERAREA3, L_GROUNDAREA1, L_GROUNDAREA2, L_GROUNDAREA3
      db L_GROUNDAREA4, L_GROUNDAREA5, L_GROUNDAREA6, L_GROUNDAREA7, L_GROUNDAREA8, L_GROUNDAREA9
      db L_GROUNDAREA10, L_GROUNDAREA11, L_GROUNDAREA12, L_GROUNDAREA13, L_GROUNDAREA14, L_GROUNDAREA15
      db L_GROUNDAREA16, L_GROUNDAREA17, L_GROUNDAREA18, L_GROUNDAREA19, L_GROUNDAREA20, L_GROUNDAREA21
      db L_GROUNDAREA22, L_UNDERGROUNDAREA1, L_UNDERGROUNDAREA2, L_UNDERGROUNDAREA3, L_CASTLEAREA1
      db L_CASTLEAREA2, L_CASTLEAREA3, L_CASTLEAREA4, L_CASTLEAREA5, L_CASTLEAREA6

AREADATAADDRHIGH:
      db L_WATERAREA1>>8, L_WATERAREA2>>8, L_WATERAREA3>>8, L_GROUNDAREA1>>8, L_GROUNDAREA2>>8, L_GROUNDAREA3>>8
      db L_GROUNDAREA4>>8, L_GROUNDAREA5>>8, L_GROUNDAREA6>>8, L_GROUNDAREA7>>8, L_GROUNDAREA8>>8, L_GROUNDAREA9>>8
      db L_GROUNDAREA10>>8, L_GROUNDAREA11>>8, L_GROUNDAREA12>>8, L_GROUNDAREA13>>8, L_GROUNDAREA14>>8, L_GROUNDAREA15>>8
      db L_GROUNDAREA16>>8, L_GROUNDAREA17>>8, L_GROUNDAREA18>>8, L_GROUNDAREA19>>8, L_GROUNDAREA20>>8, L_GROUNDAREA21>>8
      db L_GROUNDAREA22>>8, L_UNDERGROUNDAREA1>>8, L_UNDERGROUNDAREA2>>8, L_UNDERGROUNDAREA3>>8, L_CASTLEAREA1>>8
      db L_CASTLEAREA2>>8, L_CASTLEAREA3>>8, L_CASTLEAREA4>>8, L_CASTLEAREA5>>8, L_CASTLEAREA6>>8

;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
;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
            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
	LDY #!DeathMusic
            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 #$06              ;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 #%11111000        ;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 #%11111000        ;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
	;TODO
             lda !FlagpoleSoundQueue   ;load flagpole sound
             sta !1DF9    ;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_Swim        ;load swim/goomba stomp sound into
           sta !1DF9      ;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 !1DFA      ;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 multiply friction by 2
           rol !FrictionAdderHigh      ;then leave
EXITPHY:   rts

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

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 !1DFC
      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 !1DFC    ;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 !1DFC     ;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
	LDA #!Sfx_CoinGrab
        sta !1DFC  ;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 !1DFC  ;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 !1DFC     ;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 !1DF9   ;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 !1DFC    ;load brick shatter sound
	LDA #$01
      sta !Block_RepFlag,x    ;set flag for block object to immediately replace metatile
      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                   ;(putting this call elsewhere will preserve A)
          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
	LDA #!Sfx_BowserFlame        ;load bowser's flame sound into queue
	STA !1DFC
        ldy !BowserFront_Offset      ;get bowser's buffer offset
        lda !Enemy_ID,y              ;check for bowser
        cmp #!Bowser
        beq SPAWNFROMMOUTH          ;branch if found
        jsr SETFLAMETIMER           ;get timer data based on flame counter
        clc
        adc #$20                    ;add 32 frames by default
        ldy !SecondaryHardMode
        beq SETFRT                  ;if secondary mode flag not set, use as timer setting
        sec
        sbc #$10                    ;otherwise subtract 16 frames for secondary hard mode
SETFRT: sta !FrenzyEnemyTimer        ;set timer accordingly
        lda !PseudoRandomBitReg,x
        and #%00000011              ;get 2 LSB from first part of LSFR
        sta !BowserFlamePRandomOfs,x ;set here
        tay                         ;use as offset
        lda FLAMEYPOSDATA,y         ;load vertical position based on pseudorandom offset

PUTATRIGHTEXTENT:
      sta !Enemy_Y_Position,x    ;set vertical position
      lda !ScreenRight_X_Pos
      clc
      adc #$20                  ;place enemy 32 pixels beyond right side of screen
      sta !Enemy_X_Position,x
      lda !ScreenRight_PageLoc
      adc #$00                  ;add carry
      sta !Enemy_PageLoc,x
      jmp FINISHFLAME           ;skip this part to finish setting values

SPAWNFROMMOUTH:
       lda !Enemy_X_Position,y    ;get bowser's horizontal position
       sec
       sbc #$0e                  ;subtract 14 pixels
       sta !Enemy_X_Position,x    ;save as flame's horizontal position
       lda !Enemy_PageLoc,y
       sta !Enemy_PageLoc,x       ;copy page location from bowser to flame
       lda !Enemy_Y_Position,y
       clc                       ;add 8 pixels to bowser's vertical position
       adc #$08
       sta !Enemy_Y_Position,x    ;save as flame's vertical position
       lda !PseudoRandomBitReg,x
       and #%00000011            ;get 2 LSB from first part of LSFR
       sta !Enemy_YMF_Dummy,x     ;save here
       tay                       ;use as offset
       lda FLAMEYPOSDATA,y       ;get value here using bits as offset
       ldy #$00                  ;load default offset
       cmp !Enemy_Y_Position,x    ;compare value to flame's current vertical position
       bcc SETMF                 ;if less, do not increment offset
       iny                       ;otherwise increment now
SETMF: lda FLAMEYMFADDERDATA,y   ;get value here and save
       sta !Enemy_Y_MoveForce,x   ;to vertical movement force
       lda #$00
       sta !EnemyFrenzyBuffer     ;clear enemy frenzy buffer

FINISHFLAME:
      lda #$08                 ;set $08 for bounding box control
      sta !Enemy_BoundBoxCtrl,x
      lda #$01                 ;set high byte of vertical and
      sta !Enemy_Y_HighPos,x    ;enemy buffer flag
      sta !Enemy_Flag,x
      lsr
      sta !Enemy_X_MoveForce,x  ;initialize horizontal movement force, and
      sta !Enemy_State,x        ;enemy state
      rts

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

FIREWORKSXPOSDATA:
      db $00, $30, $60, $60, $00, $20

FIREWORKSYPOSDATA:
      db $60, $40, $70, $40, $60, $30

INITFIREWORKS:
          lda !FrenzyEnemyTimer         ;if timer not expired yet, branch to leave
          bne EXITFWK
          lda #$20                     ;otherwise reset timer
          sta !FrenzyEnemyTimer
          dec !FireworksCounter         ;decrement for each explosion
          ldy #$06                     ;START at last slot
STARFCHK: dey
          lda !Enemy_ID,y               ;check for presence of star flag object
          cmp #!StarFlagObject          ;if there isn't a star flag object,
          bne STARFCHK                 ;routine goes into infinite loop = crash
          lda !Enemy_X_Position,y
          sec                          ;get horizontal coordinate of star flag object, then
          sbc #$30                     ;subtract 48 pixels from it and save to
          pha                          ;the stack
          lda !Enemy_PageLoc,y
          sbc #$00                     ;subtract the carry from the page location
          sta $00                      ;of the star flag object
          lda !FireworksCounter         ;get fireworks counter
          clc
          adc !Enemy_State,y            ;add state of star flag object (possibly not necessary)
          tay                          ;use as offset
          pla                          ;get saved horizontal coordinate of star flag - 48 pixels
          clc
          adc FIREWORKSXPOSDATA,y      ;add number based on offset of fireworks counter
          sta !Enemy_X_Position,x       ;store as the fireworks object horizontal coordinate
          lda $00
          adc #$00                     ;add carry and store as page location for
          sta !Enemy_PageLoc,x          ;the fireworks object
          lda FIREWORKSYPOSDATA,y      ;get vertical position using same offset
          sta !Enemy_Y_Position,x       ;and store as vertical coordinate for fireworks object
          lda #$01
          sta !Enemy_Y_HighPos,x        ;store in vertical high byte
          sta !Enemy_Flag,x             ;and activate enemy buffer flag
          lsr
          sta !ExplosionGfxCounter,x    ;initialize explosion counter
          lda #$08
          sta !ExplosionTimerCounter,x  ;set explosion timing counter
EXITFWK:  rts

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

BITMASKS:
      db %00000001, %00000010, %00000100, %00001000, %00010000, %00100000, %01000000, %10000000

ENEMY17YPOSDATA:
      db $40, $30, $90, $50, $20, $60, $a0, $70

SWIMCC_IDDATA:
      db $0a, $0b

BULLETBILLCHEEPCHEEP:
         lda !FrenzyEnemyTimer      ;if timer not expired yet, branch to leave
         bne EXF17
         lda !AreaType              ;are we in a water-type level?
         bne DOBULLETBILLS         ;if not, branch elsewhere
         cpx #$03                  ;are we past third enemy slot?
         bcs EXF17                 ;if so, branch to leave
         ldy #$00                  ;load default offset
         lda !PseudoRandomBitReg,x
         cmp #$aa                  ;check first part of LSFR against preset value
         bcc CHKW2                 ;if less than preset, do not increment offset
         iny                       ;otherwise increment
CHKW2:   lda !WorldNumber           ;check world number
         cmp #!World2
         beq GET17ID               ;if we're on world 2, do not increment offset
         iny                       ;otherwise increment
GET17ID: tya
         and #%00000001            ;mask out all but last bit of offset
         tay
         lda SWIMCC_IDDATA,y       ;load identifier for cheep-cheeps
SET17ID: sta !Enemy_ID,x            ;store whatever's in A as enemy identifier
         lda !BitMFilter
         cmp #$ff                  ;if not all bits set, skip init part and compare bits
         bne GETRBIT
         lda #$00                  ;initialize vertical position filter
         sta !BitMFilter
GETRBIT: lda !PseudoRandomBitReg,x  ;get first part of LSFR
         and #%00000111            ;mask out all but 3 LSB
CHKRBIT: tay                       ;use as offset
         lda BITMASKS,y            ;load bitmask
         bit !BitMFilter            ;perform AND on filter without changing it
         beq ADDFBIT
         iny                       ;increment offset
         tya
         and #%00000111            ;mask out all but 3 LSB thus keeping it 0-7
         jmp CHKRBIT               ;do another check
ADDFBIT: ora !BitMFilter            ;add bit to already set bits in filter
         sta !BitMFilter            ;and store
         lda ENEMY17YPOSDATA,y     ;load vertical position using offset
         jsr PUTATRIGHTEXTENT      ;set vertical position and other values
         sta !Enemy_YMF_Dummy,x     ;initialize dummy variable
         lda #$20                  ;set timer
         sta !FrenzyEnemyTimer
         jmp CHECKPOINTENEMYID     ;process our new enemy object

DOBULLETBILLS:
          ldy #$ff                   ;START at beginning of enemy slots
BB_SLOOP: iny                        ;move onto the next slot
          cpy #$05                   ;branch to play sound if we've done all slots
          bcs FIREBULLETBILL
          lda !Enemy_Flag,y           ;if enemy buffer flag not set,
          beq BB_SLOOP               ;loop back and check another slot
          lda !Enemy_ID,y
          cmp #!BulletBill_FrenzyVar  ;check enemy identifier for
          bne BB_SLOOP               ;bullet bill object (frenzy variant)
EXF17:    rts                        ;if found, leave

FIREBULLETBILL:
      ;lda !Square2SoundQueue
      LDA #!Sfx_Blast            ;play fireworks/gunfire sound
      sta !1DFC
      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:			;read as part of the previous table and unused for gfx purposes
      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_Bridge            ;load the fireworks/gunfire sound into the square 2 sfx
         sta !1DFC     ;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 !1DFC     ;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 #$24                    ;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 #$A4                    ;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 !1DFC
      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
	BNE +
	STZ !DrumrollBuffer
	LDA #!Sfx_Drumrolled
	STA !1DFC
	BRA INCREMENTSFTASK1   ;if no time left on game timer at all, branch to next task
+
	LDA !DrumrollBuffer
	BNE NOTTICK
	INC !DrumrollBuffer
	LDA #!Sfx_Drumroll
	STA !1DFC  ;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 #$04                   ;set palette and background priority bits
         sta !Sprite_Attributes,y    ;store as attributes
         lda !Enemy_Rel_XPos         ;get relative horizontal coordinate
         clc
         adc STARFLAGXPOSADDER,x    ;add X coordinate adder data
         sta !Sprite_X_Position,y    ;store as X coordinate
         iny
         iny                        ;increment OAM data offset four bytes
         iny                        ;for next sprite
         iny
         dex                        ;move onto next sprite
         bpl DSFLOOP                ;do this until all sprites are done
         ldx !ObjectOffset           ;get enemy object offset and leave
         rts

DRAWFLAGSETTIMER:
      jsr DRAWSTARFLAG          ;do sub to draw star flag
      lda #$06
      sta !EnemyIntervalTimer,x  ;set interval timer here

INCREMENTSFTASK2:
      inc !StarFlagTaskControl   ;move onto next task
      rts

DELAYTOAREAEND:
      jsr DRAWSTARFLAG          ;do sub to draw star flag
      lda !EnemyIntervalTimer,x  ;if interval timer set in previous task
      bne STARFLAGEXIT2         ;not yet expired, branch to leave
      lda !EventMusicBuffer      ;if event music buffer empty,
      beq INCREMENTSFTASK2      ;branch to increment task

STARFLAGEXIT2:
      rts                       ;otherwise leave

;--------------------------------
;$00 - used to store horizontal difference between player and piranha plant

MOVEPIRANHAPLANT:
      lda !Enemy_State,x           ;check enemy state
      bne PUTINPIPE               ;if set at all, branch to leave
      lda !EnemyFrameTimer,x       ;check enemy's timer here
      bne PUTINPIPE               ;branch to end if not yet expired
      lda !PiranhaPlant_MoveFlag,x ;check movement flag
      bne SETUPTOMOVEPPLANT       ;if moving, skip to part ahead
      lda !PiranhaPlant_Y_Speed,x  ;if currently rising, branch 
      bmi REVERSEPLANTSPEED       ;to move enemy upwards out of pipe
      jsr PLAYERENEMYDIFF         ;get horizontal difference between player and
      bpl CHKPLAYERNEARPIPE       ;piranha plant, and branch if enemy to right of player
      lda $00                     ;otherwise get saved horizontal difference
      eor #$ff
      clc                         ;and change to two's compliment
      adc #$01
      sta $00                     ;save as new horizontal difference

CHKPLAYERNEARPIPE:
      lda $00                     ;get saved horizontal difference
      cmp #$21
      bcc PUTINPIPE               ;if player within a certain distance, branch to leave

REVERSEPLANTSPEED:
      lda !PiranhaPlant_Y_Speed,x  ;get vertical speed
      eor #$ff
      clc                         ;change to two's compliment
      adc #$01
      sta !PiranhaPlant_Y_Speed,x  ;save as new vertical speed
      inc !PiranhaPlant_MoveFlag,x ;increment to set movement flag

SETUPTOMOVEPPLANT:
      lda !PiranhaPlantDownYPos,x  ;get original vertical coordinate (lowest point)
      ldy !PiranhaPlant_Y_Speed,x  ;get vertical speed
      bpl RISEFALLPIRANHAPLANT    ;branch if moving downwards
      lda !PiranhaPlantUpYPos,x    ;otherwise get other vertical coordinate (highest point)

RISEFALLPIRANHAPLANT:
      sta $00                     ;save vertical coordinate here
      lda !FrameCounter            ;get frame counter
      lsr
      bcc PUTINPIPE               ;branch to leave if d0 set (execute code every other frame)
      lda !TimerControl            ;get master timer control
      bne PUTINPIPE               ;branch to leave if set (likely not necessary)
      lda !Enemy_Y_Position,x      ;get current vertical coordinate
      clc
      adc !PiranhaPlant_Y_Speed,x  ;add vertical speed to move up or down
      sta !Enemy_Y_Position,x      ;save as new vertical coordinate
      cmp $00                     ;compare against low or high coordinate
      bne PUTINPIPE               ;branch to leave if not yet reached
      lda #$00
      sta !PiranhaPlant_MoveFlag,x ;otherwise clear movement flag
      lda #$40
      sta !EnemyFrameTimer,x       ;set timer to delay piranha plant movement

PUTINPIPE:
      lda #%00100000              ;set background priority bit in sprite
      sta !Enemy_SprAttrib,x       ;attributes to give illusion of being inside pipe
      rts                         ;then leave

;-------------------------------------------------------------------------------------
;$07 - spinning speed

FIREBARSPIN:
      sta $07                     ;save spinning speed here
      lda !FirebarSpinDirection,x  ;check spinning direction
      bne SPINCOUNTERCLOCKWISE    ;if moving counter-clockwise, branch to other part
      ldy #$18                    ;possibly residual ldy
      lda !FirebarSpinState_Low,x
      clc                         ;add spinning speed to what would normally be
      adc $07                     ;the horizontal speed
      sta !FirebarSpinState_Low,x
      lda !FirebarSpinState_High,x ;add carry to what would normally be the vertical speed
      adc #$00
      rts

SPINCOUNTERCLOCKWISE:
      ldy #$08                    ;possibly residual ldy
      lda !FirebarSpinState_Low,x
      sec                         ;subtract spinning speed to what would normally be
      sbc $07                     ;the horizontal speed
      sta !FirebarSpinState_Low,x
      lda !FirebarSpinState_High,x ;add carry to what would normally be the vertical speed
      sbc #$00
      rts

;-------------------------------------------------------------------------------------
;$00 - used to hold collision flag, Y movement force + 5 or low byte of name table for rope
;$01 - used to hold high byte of name table for rope
;$02 - used to hold page location of rope

BALANCEPLATFORM:
       lda !Enemy_Y_HighPos,x       ;check high byte of vertical position
       cmp #$03
       bne DOBPL
       jmp ERASEENEMYOBJECT        ;if far below screen, kill the object
DOBPL: lda !Enemy_State,x           ;get object's state (set to $ff or other platform offset)
       bpl CHECKBALPLATFORM        ;if doing other balance platform, branch to leave
       rts

CHECKBALPLATFORM:
       tay                         ;save offset from state as Y
       lda !PlatformCollisionFlag,x ;get collision flag of platform
       sta $00                     ;store here
       lda !Enemy_MovingDir,x       ;get moving direction
       beq CHKFORFALL
       jmp PLATFORMFALL            ;if set, jump here

CHKFORFALL:
       lda #$2d                    ;check if platform is above a certain point
       cmp !Enemy_Y_Position,x
       bcc CHKOTHERFORFALL         ;if not, branch elsewhere
       cpy $00                     ;if collision flag is set to same value as
       beq MAKEPLATFORMFALL        ;enemy state, branch to make platforms fall
       clc
       adc #$02                    ;otherwise add 2 pixels to vertical position
       sta !Enemy_Y_Position,x      ;of current platform and branch elsewhere
       jmp STOPPLATFORMS           ;to make platforms stop

MAKEPLATFORMFALL:
       jmp INITPLATFORMFALL        ;make platforms fall

CHKOTHERFORFALL:
       cmp !Enemy_Y_Position,y      ;check if other platform is above a certain point
       bcc CHKTOMOVEBALPLAT        ;if not, branch elsewhere
       cpx $00                     ;if collision flag is set to same value as
       beq MAKEPLATFORMFALL        ;enemy state, branch to make platforms fall
       clc
       adc #$02                    ;otherwise add 2 pixels to vertical position
       sta !Enemy_Y_Position,y      ;of other platform and branch elsewhere
       jmp STOPPLATFORMS           ;jump to stop movement and do not return

CHKTOMOVEBALPLAT:
        lda !Enemy_Y_Position,x      ;save vertical position to stack
        pha
        lda !PlatformCollisionFlag,x ;get collision flag
        bpl COLFLG                  ;branch if collision
        lda !Enemy_Y_MoveForce,x
        clc                         ;add $05 to contents of moveforce, whatever they be
        adc #$05
        sta $00                     ;store here
        lda !Enemy_Y_Speed,x
        adc #$00                    ;add carry to vertical speed
        bmi PLATDN                  ;branch if moving downwards
        bne PLATUP                  ;branch elsewhere if moving upwards
        lda $00
        cmp #$0b                    ;check if there's still a little force left
        bcc PLATST                  ;if not enough, branch to stop movement
        bcs PLATUP                  ;otherwise keep branch to move upwards
COLFLG: cmp !ObjectOffset            ;if collision flag matches
        beq PLATDN                  ;current enemy object offset, branch
PLATUP: jsr MOVEPLATFORMUP          ;do a sub to move upwards
        jmp DOOTHERPLATFORM         ;jump ahead to remaining code
PLATST: jsr STOPPLATFORMS           ;do a sub to stop movement
        jmp DOOTHERPLATFORM         ;jump ahead to remaining code
PLATDN: jsr MOVEPLATFORMDOWN        ;do a sub to move downwards

DOOTHERPLATFORM:
       ldy !Enemy_State,x           ;get offset of other platform
       pla                         ;get old vertical coordinate from stack
       sec
       sbc !Enemy_Y_Position,x      ;get difference of old vs. new coordinate
       clc
       adc !Enemy_Y_Position,y      ;add difference to vertical coordinate of other
       sta !Enemy_Y_Position,y      ;platform to move it in the opposite direction
       lda !PlatformCollisionFlag,x ;if no collision, skip this part here
       bmi DRAWERASEROPE
       tax                         ;put offset which collision occurred here
       jsr POSITIONPLAYERONVPLAT   ;and use it to position player accordingly

DRAWERASEROPE:
         ldy !ObjectOffset            ;get enemy object offset
         lda !Enemy_Y_Speed,y         ;check to see if current platform is
         ora !Enemy_Y_MoveForce,y     ;moving at all
         beq EXITRP                  ;if not, skip all of this and branch to leave
         ldx !VRAM_Buffer1_Offset     ;get vram buffer offset
         cpx #$20                    ;if offset beyond a certain point, go ahead
         bcs EXITRP                  ;and skip this, branch to leave
         lda !Enemy_Y_Speed,y
         pha                         ;save two copies of vertical speed to stack
         pha
         jsr SETUPPLATFORMROPE       ;do a sub to figure out where to put new bg tiles
         lda $01                     ;write name table address to vram buffer
         sta !VRAM_Buffer1,x          ;first the high byte, then the low
         lda $00
         sta !VRAM_Buffer1+1,x
         lda #$02                    ;set length for 2 bytes
         sta !VRAM_Buffer1+2,x
         lda !Enemy_Y_Speed,y         ;if platform moving upwards, branch 
         bmi ERASER1                 ;to do something else
         lda #$a2
         sta !VRAM_Buffer1+3,x        ;otherwise put tile numbers for left
         lda #$a3                    ;and right sides of rope in vram buffer
         sta !VRAM_Buffer1+4,x
         jmp OTHERROPE               ;jump to skip this part
ERASER1: lda #$24                    ;put blank tiles in vram buffer
         sta !VRAM_Buffer1+3,x        ;to erase rope
         sta !VRAM_Buffer1+4,x

OTHERROPE:
         lda !Enemy_State,y           ;get offset of other platform from state
         tay                         ;use as Y here
         pla                         ;pull second copy of vertical speed from stack
         eor #$ff                    ;invert bits to reverse speed
         jsr SETUPPLATFORMROPE       ;do sub again to figure out where to put bg tiles  
         lda $01                     ;write name table address to vram buffer
         sta !VRAM_Buffer1+5,x        ;this time we're doing putting tiles for
         lda $00                     ;the other platform
         sta !VRAM_Buffer1+6,x
         lda #$02
         sta !VRAM_Buffer1+7,x        ;set length again for 2 bytes
         pla                         ;pull first copy of vertical speed from stack
         bpl ERASER2                 ;if moving upwards (note inversion earlier), skip this
         lda #$a2
         sta !VRAM_Buffer1+8,x        ;otherwise put tile numbers for left
         lda #$a3                    ;and right sides of rope in vram
         sta !VRAM_Buffer1+9,x        ;transfer buffer
         jmp ENDRP                   ;jump to skip this part
ERASER2: lda #$24                    ;put blank tiles in vram buffer
         sta !VRAM_Buffer1+8,x        ;to erase rope
         sta !VRAM_Buffer1+9,x
ENDRP:   lda #$00                    ;put null terminator at the end
         sta !VRAM_Buffer1+10,x
         lda !VRAM_Buffer1_Offset     ;add ten bytes to the vram buffer offset
         clc                         ;and store
         adc #10
         sta !VRAM_Buffer1_Offset
EXITRP:  ldx !ObjectOffset            ;get enemy object buffer offset and leave
         rts

SETUPPLATFORMROPE:
        pha                     ;save second/third copy to stack
        lda !Enemy_X_Position,y  ;get horizontal coordinate
        clc
        adc #$08                ;add eight pixels
        ldx !SecondaryHardMode   ;if secondary hard mode flag set,
        bne GETLRP              ;use coordinate as-is
        clc
        adc #$10                ;otherwise add sixteen more pixels
GETLRP: pha                     ;save modified horizontal coordinate to stack
        lda !Enemy_PageLoc,y
        adc #$00                ;add carry to page location
        sta $02                 ;and save here
        pla                     ;pull modified horizontal coordinate
        and #%11110000          ;from the stack, mask out low nybble
        lsr                     ;and shift three bits to the right
        lsr
        lsr
        sta $00                 ;store result here as part of name table low byte
        ldx !Enemy_Y_Position,y  ;get vertical coordinate
        pla                     ;get second/third copy of vertical speed from stack
        bpl GETHRP              ;skip this part if moving downwards or not at all
        txa
        clc
        adc #$08                ;add eight to vertical coordinate and
        tax                     ;save as X
GETHRP: txa                     ;move vertical coordinate to A
        ldx !VRAM_Buffer1_Offset ;get vram buffer offset
        asl
        rol                     ;rotate d7 to d0 and d6 into carry
        pha                     ;save modified vertical coordinate to stack
        rol                     ;rotate carry to d0, thus d7 and d6 are at 2 LSB
        and #%00000011          ;mask out all bits but d7 and d6, then set
        ora #%00100000          ;d5 to get appropriate high byte of name table
        sta $01                 ;address, then store
        lda $02                 ;get saved page location from earlier
        and #$01                ;mask out all but LSB
        asl
        asl                     ;shift twice to the left and save with the
        ora $01                 ;REST of the bits of the high byte, to get
        sta $01                 ;the proper name table and the right place on it
        pla                     ;get modified vertical coordinate from stack
        and #%11100000          ;mask out low nybble and LSB of high nybble
        clc
        adc $00                 ;add to horizontal part saved here
        sta $00                 ;save as name table low byte
        lda !Enemy_Y_Position,y
        cmp #$e8                ;if vertical position not below the
        bcc EXPRP               ;bottom of the screen, we're done, branch to leave
        lda $00
        and #%10111111          ;mask out d6 of low byte of name table address
        sta $00
EXPRP:  rts                     ;leave!

INITPLATFORMFALL:
      tya                        ;move offset of other platform from Y to X
      tax
      jsr GETENEMYOFFSCREENBITS  ;get offscreen bits
      lda #$06
      jsr SETUPFLOATEYNUMBER     ;award 1000 points to player
      lda !Player_Rel_XPos
      sta !FloateyNum_X_Pos,x     ;put floatey number coordinates where player is
      lda !Player_Y_Position
      sta !FloateyNum_Y_Pos,x
      lda #$01                   ;set moving direction as flag for
      sta !Enemy_MovingDir,x      ;falling platforms

STOPPLATFORMS:
      jsr INITVSTF             ;initialize vertical speed and low byte
      sta !Enemy_Y_Speed,y      ;for both platforms and leave
      sta !Enemy_Y_MoveForce,y
      rts

PLATFORMFALL:
      tya                         ;save offset for other platform to stack
      pha
      jsr MOVEFALLINGPLATFORM     ;make current platform fall
      pla
      tax                         ;pull offset from stack and save to X
      jsr MOVEFALLINGPLATFORM     ;make other platform fall
      ldx !ObjectOffset
      lda !PlatformCollisionFlag,x ;if player not standing on either platform,
      bmi EXPF                    ;skip this part
      tax                         ;transfer collision flag offset as offset to X
      jsr POSITIONPLAYERONVPLAT   ;and position player appropriately
EXPF: ldx !ObjectOffset            ;get enemy object buffer offset and leave
      rts

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

YMOVINGPLATFORM:
        lda !Enemy_Y_Speed,x          ;if platform moving up or down, skip ahead to
        ora !Enemy_Y_MoveForce,x      ;check on other position
        bne CHKYCENTERPOS
        sta !Enemy_YMF_Dummy,x        ;initialize dummy variable
        lda !Enemy_Y_Position,x
        cmp !YPlatformTopYPos,x       ;if current vertical position => top position, branch
        bcs CHKYCENTERPOS            ;ahead of all this
        lda !FrameCounter
        and #%00000111               ;check for every eighth frame
        bne SKIPIY
        inc !Enemy_Y_Position,x       ;increase vertical position every eighth frame
SKIPIY: jmp CHKYPCOLLISION           ;skip ahead to last part

CHKYCENTERPOS:
        lda !Enemy_Y_Position,x       ;if current vertical position < central position, branch
        cmp !YPlatformCenterYPos,x    ;to slow ascent/move downwards
        bcc YMDOWN
        jsr MOVEPLATFORMUP           ;otherwise START slowing descent/moving upwards
        jmp CHKYPCOLLISION
YMDOWN: jsr MOVEPLATFORMDOWN         ;START slowing ascent/moving downwards

CHKYPCOLLISION:
       lda !PlatformCollisionFlag,x  ;if collision flag not set here, branch
       bmi EXYPL                    ;to leave
       jsr POSITIONPLAYERONVPLAT    ;otherwise position player appropriately
EXYPL: rts                          ;leave

;--------------------------------
;$00 - used as adder to position player hotizontally

XMOVINGPLATFORM:
      lda #$0e                     ;load preset maximum value for secondary counter
      jsr XMOVECNTR_PLATFORM       ;do a sub to increment counters for movement
      jsr MOVEWITHXMCNTRS          ;do a sub to move platform accordingly, and return value
      lda !PlatformCollisionFlag,x  ;if no collision with player,
      bmi EXXMP                    ;branch ahead to leave

POSITIONPLAYERONHPLAT:
         lda !Player_X_Position
         clc                       ;add saved value from second subroutine to
         adc $00                   ;current player's position to position
         sta !Player_X_Position     ;player accordingly in horizontal position
         lda !Player_PageLoc        ;get player's page location
         ldy $00                   ;check to see if saved value here is positive or negative
         bmi PPHSUBT               ;if negative, branch to subtract
         adc #$00                  ;otherwise add carry to page location
         jmp SETPVAR               ;jump to skip subtraction
PPHSUBT: sbc #$00                  ;subtract borrow from page location
SETPVAR: sta !Player_PageLoc        ;save result to player's page location
         sty !Platform_X_Scroll     ;put saved value from second sub here to be used later
         jsr POSITIONPLAYERONVPLAT ;position player vertically and appropriately
EXXMP:   rts                       ;and we are done here

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

DROPPLATFORM:
       lda !PlatformCollisionFlag,x  ;if no collision between platform and player
       bmi EXDPL                    ;occurred, just leave without moving anything
       jsr MOVEDROPPLATFORM         ;otherwise do a sub to move platform down very quickly
       jsr POSITIONPLAYERONVPLAT    ;do a sub to position player appropriately
EXDPL: rts                          ;leave

;--------------------------------
;$00 - residual value from sub

RIGHTPLATFORM:
       jsr MOVEENEMYHORIZONTALLY     ;move platform with current horizontal speed, if any
       sta $00                       ;store saved value here (residual code)
       lda !PlatformCollisionFlag,x   ;check collision flag, if no collision between player
       bmi EXRPL                     ;and platform, branch ahead, leave speed unaltered
       lda #$10
       sta !Enemy_X_Speed,x           ;otherwise set new speed (gets moving if motionless)
       jsr POSITIONPLAYERONHPLAT     ;use saved value from earlier sub to position player
EXRPL: rts                           ;then leave

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

MOVELARGELIFTPLAT:
      jsr MOVELIFTPLATFORMS  ;execute common to all large and small lift platforms
      jmp CHKYPCOLLISION     ;branch to position player correctly

MOVESMALLPLATFORM:
      jsr MOVELIFTPLATFORMS      ;execute common to all large and small lift platforms
      jmp CHKSMALLPLATCOLLISION  ;branch to position player correctly

MOVELIFTPLATFORMS:
      lda !TimerControl         ;if master timer control set, skip all of this
      bne EXLIFTP              ;and branch to leave
      lda !Enemy_YMF_Dummy,x
      clc                      ;add contents of movement amount to whatever's here
      adc !Enemy_Y_MoveForce,x
      sta !Enemy_YMF_Dummy,x
      lda !Enemy_Y_Position,x   ;add whatever vertical speed is set to current
      adc !Enemy_Y_Speed,x      ;vertical position plus carry to move up or down
      sta !Enemy_Y_Position,x   ;and then leave
      rts

CHKSMALLPLATCOLLISION:
         lda !PlatformCollisionFlag,x ;get bounding box counter saved in collision flag
         beq EXLIFTP                 ;if none found, leave player position alone
         jsr POSITIONPLAYERONS_PLAT  ;use to position player correctly
EXLIFTP: rts                         ;then leave

;-------------------------------------------------------------------------------------
;$00 - page location of extended left boundary
;$01 - extended left boundary position
;$02 - page location of extended right boundary
;$03 - extended right boundary position

OFFSCREENBOUNDSCHECK:
          lda !Enemy_ID,x          ;check for cheep-cheep object
          cmp #!FlyingCheepCheep   ;branch to leave if found
          beq EXSCRNBD
          lda !ScreenLeft_X_Pos    ;get horizontal coordinate for left side of screen
          ldy !Enemy_ID,x
          cpy #!HammerBro          ;check for hammer bro object
          beq LIMITB
          cpy #!PiranhaPlant       ;check for piranha plant object
          bne EXTENDLB            ;these two will be erased sooner than others if too far left
LIMITB:   adc #$38                ;add 56 pixels to coordinate if hammer bro or piranha plant
EXTENDLB: sbc #$48                ;subtract 72 pixels regardless of enemy object
          sta $01                 ;store result here
          lda !ScreenLeft_PageLoc
          sbc #$00                ;subtract borrow from page location of left side
          sta $00                 ;store result here
          lda !ScreenRight_X_Pos   ;add 72 pixels to the right side horizontal coordinate
          adc #$48
          sta $03                 ;store result here
          lda !ScreenRight_PageLoc     
          adc #$00                ;then add the carry to the page location
          sta $02                 ;and store result here
          lda !Enemy_X_Position,x  ;compare horizontal coordinate of the enemy object
          cmp $01                 ;to modified horizontal left edge coordinate to get carry
          lda !Enemy_PageLoc,x
          sbc $00                 ;then subtract it from the page coordinate of the enemy object
          bmi TOOFAR              ;if enemy object is too far left, branch to erase it
          lda !Enemy_X_Position,x  ;compare horizontal coordinate of the enemy object
          cmp $03                 ;to modified horizontal right edge coordinate to get carry
          lda !Enemy_PageLoc,x
          sbc $02                 ;then subtract it from the page coordinate of the enemy object
          bmi EXSCRNBD            ;if enemy object is on the screen, leave, do not erase enemy
          lda !Enemy_State,x       ;if at this point, enemy is offscreen to the right, so check
          cmp #!HammerBro          ;if in state used by spiny's egg, do not erase
          beq EXSCRNBD
          cpy #!PiranhaPlant       ;if piranha plant, do not erase
          beq EXSCRNBD
          cpy #!FlagpoleFlagObject ;if flagpole flag, do not erase
          beq EXSCRNBD
          cpy #!StarFlagObject     ;if star flag, do not erase
          beq EXSCRNBD
          cpy #!JumpspringObject   ;if JUMPSPRING, do not erase
          beq EXSCRNBD            ;erase all others too far to the right
TOOFAR:   jsr ERASEENEMYOBJECT    ;erase object if necessary
EXSCRNBD: rts                     ;leave

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

;some unused space
      db $ff, $ff, $ff

;-------------------------------------------------------------------------------------
;$01 - enemy buffer offset

FIREBALLENEMYCOLLISION:
      lda !Fireball_State,x  ;check to see if fireball state is set at all
      beq EXITFBALLENEMY    ;branch to leave if not
      asl
      bcs EXITFBALLENEMY    ;branch to leave also if d7 in state is set
      lda !FrameCounter
      lsr                   ;get LSB of frame counter
      bcs EXITFBALLENEMY    ;branch to leave if set (do routine every other frame)
      txa
      asl                   ;multiply fireball offset by four
      asl
      clc
      adc #$1c              ;then add $1c or 28 bytes to it
      tay                   ;to use fireball's bounding box coordinates 
      ldx #$04

FIREBALLENEMYCDLOOP:
           stx $01                     ;store enemy object offset here
           tya
           pha                         ;push fireball offset to the stack
           lda !Enemy_State,x
           and #%00100000              ;check to see if d5 is set in enemy state
           bne NOFTOECOL               ;if so, skip to next enemy slot
           lda !Enemy_Flag,x            ;check to see if buffer flag is set
           beq NOFTOECOL               ;if not, skip to next enemy slot
           lda !Enemy_ID,x              ;check enemy identifier
           cmp #$24
           bcc GOOMBADIE               ;if < $24, branch to check further
           cmp #$2b
           bcc NOFTOECOL               ;if in range $24-$2a, skip to next enemy slot
GOOMBADIE: cmp #!Goomba                 ;check for goomba identifier
           bne NOTGOOMBA               ;if not found, continue with code
           lda !Enemy_State,x           ;otherwise check for defeated state
           cmp #$02                    ;if stomped or otherwise defeated,
           bcs NOFTOECOL               ;skip to next enemy slot
NOTGOOMBA: lda !EnemyOffscrBitsMasked,x ;if any masked offscreen bits set,
           bne NOFTOECOL               ;skip to next enemy slot
           txa
           asl                         ;otherwise multiply enemy offset by four
           asl
           clc
           adc #$04                    ;add 4 bytes to it
           tax                         ;to use enemy's bounding box coordinates
           jsr SPROBJECTCOLLISIONCORE  ;do fireball-to-enemy collision detection
           ldx !ObjectOffset            ;return fireball's original offset
           bcc NOFTOECOL               ;if carry clear, no collision, thus do next enemy slot
           lda #%10000000
           sta !Fireball_State,x        ;set d7 in enemy state
           ldx $01                     ;get enemy offset
           jsr HANDLEENEMYFBALLCOL     ;jump to handle fireball to enemy collision
NOFTOECOL: pla                         ;pull fireball offset from stack
           tay                         ;put it in Y
           ldx $01                     ;get enemy object offset
           dex                         ;decrement it
           bpl FIREBALLENEMYCDLOOP     ;loop back until collision detection done on all enemies

EXITFBALLENEMY:
      ldx !ObjectOffset                 ;get original fireball offset and leave
      rts

BOWSERIDENTITIES:
      db !Goomba, !GreenKoopa, !BuzzyBeetle, !Spiny, !Lakitu, !Bloober, !HammerBro, !Bowser

HANDLEENEMYFBALLCOL:
      jsr RELATIVEENEMYPOSITION  ;get relative coordinate of enemy
      ldx $01                    ;get current enemy object offset
      lda !Enemy_Flag,x           ;check buffer flag for d7 set
      bpl CHKBUZZYBEETLE         ;branch if not set to continue
      and #%00001111             ;otherwise mask out high nybble and
      tax                        ;use low nybble as enemy offset
      lda !Enemy_ID,x
      cmp #!Bowser                ;check enemy identifier for bowser
      beq HURTBOWSER             ;branch if found
      ldx $01                    ;otherwise retrieve current enemy offset

CHKBUZZYBEETLE:
      lda !Enemy_ID,x
      cmp #!BuzzyBeetle           ;check for buzzy beetle
      beq EXHCF                  ;branch if found to leave (buzzy beetles fireproof)
      cmp #!Bowser                ;check for bowser one more time (necessary if d7 of flag was clear)
      bne CHKOTHERENEMIES        ;if not found, branch to check other enemies

HURTBOWSER:
          dec !BowserHitPoints        ;decrement bowser's hit points
          bne EXHCF                  ;if bowser still has hit points, branch to leave
          jsr INITVSTF               ;otherwise do sub to init vertical speed and movement force
          sta !Enemy_X_Speed,x        ;initialize horizontal speed
          sta !EnemyFrenzyBuffer      ;init enemy frenzy buffer
          lda #$fe
          sta !Enemy_Y_Speed,x        ;set vertical speed to make defeated bowser jump a little
          ldy !WorldNumber            ;use world number as offset
          lda BOWSERIDENTITIES,y     ;get enemy identifier to replace bowser with
          sta !Enemy_ID,x             ;set as new enemy identifier
          lda #$20                   ;set A to use starting value for state
          cpy #$03                   ;check to see if using offset of 3 or more
          bcs SETDBSTE               ;branch if so
          ora #$03                   ;otherwise add 3 to enemy state
SETDBSTE: sta !Enemy_State,x          ;set defeated enemy state
          lda #!Sfx_BowserFall
          sta !1DFC      ;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 !1DF9
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 !1DF9   ;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 !1DF9
       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
	LDA #!Sfx_PipeDown_Injury
          sta !1DF9     ;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
	LDX #!DeathMusic
      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 !1DF9
      lda !Enemy_ID,x
      ldy #$00                   ;initialize points data offset for stomped enemies
      cmp #!FlyingCheepCheep      ;branch for cheep-cheep
      beq ENEMYSTOMPEDPTS
      cmp #!BulletBill_FrenzyVar  ;branch for either bullet bill object
      beq ENEMYSTOMPEDPTS
      cmp #!BulletBill_CannonVar
      beq ENEMYSTOMPEDPTS
      cmp #!Podoboo               ;branch for podoboo (this branch is logically impossible
      beq ENEMYSTOMPEDPTS        ;for cpu to take due to earlier checking of podoboo)
      iny                        ;increment points data offset
      cmp #!HammerBro             ;branch for hammer bro
      beq ENEMYSTOMPEDPTS
      iny                        ;increment points data offset
      cmp #!Lakitu                ;branch for lakitu
      beq ENEMYSTOMPEDPTS
      iny                        ;increment points data offset
      cmp #!Bloober               ;branch if NOT bloober
      bne CHKFORDEMOTEKOOPA

ENEMYSTOMPEDPTS:
      lda STOMPEDENEMYPTSDATA,y  ;load points data using offset in Y
      jsr SETUPFLOATEYNUMBER     ;run sub to set floatey number controls
      lda !Enemy_MovingDir,x
      pha                        ;save enemy movement direction to stack
      jsr SETSTUN                ;run sub to kill enemy
      pla
      sta !Enemy_MovingDir,x      ;return enemy movement direction from stack
      lda #%00100000
      sta !Enemy_State,x          ;set d5 in enemy state
      jsr INITVSTF               ;nullify vertical speed, physics-related thing,
      sta !Enemy_X_Speed,x        ;and horizontal speed
      lda #$fd                   ;set player's vertical speed, to give bounce
      sta !Player_Y_Speed
      rts

CHKFORDEMOTEKOOPA:
      cmp #$09                   ;branch elsewhere if enemy object < $09
      bcc HANDLESTOMPEDSHELLE
      and #%00000001             ;DEMOTE koopa paratroopas to ordinary troopas
      sta !Enemy_ID,x
      ldy #$00                   ;return enemy to normal state
      sty !Enemy_State,x
      lda #$03                   ;award 400 points to the player
      jsr SETUPFLOATEYNUMBER
      jsr INITVSTF               ;nullify physics-related thing and vertical speed
      jsr ENEMYFACEPLAYER        ;turn enemy around if necessary
      lda DEMOTEDKOOPAXSPDDATA,y
      sta !Enemy_X_Speed,x        ;set appropriate moving speed based on direction
      jmp SBNCE                  ;then move onto something else

REVIVALRATEDATA:
      db $10, $0b

HANDLESTOMPEDSHELLE:
       lda #$04                   ;set defeated state for enemy
       sta !Enemy_State,x
       inc !StompChainCounter      ;increment the stomp counter
       lda !StompChainCounter      ;add whatever is in the stomp counter
       clc                        ;to whatever is in the stomp timer
       adc !StompTimer
       jsr SETUPFLOATEYNUMBER     ;award points accordingly
       inc !StompTimer             ;increment stomp timer of some sort
       ldy !PrimaryHardMode        ;check primary hard mode flag
       lda REVIVALRATEDATA,y      ;load timer setting according to flag
       sta !EnemyIntervalTimer,x   ;set as enemy timer to revive stomped enemy
SBNCE: lda #$fc                   ;set player's vertical speed for bounce
       sta !Player_Y_Speed         ;and then leave!!!
       rts

CHKENEMYFACERIGHT:
       lda !Enemy_MovingDir,x ;check to see if enemy is moving to the right
       cmp #$01
       bne LINJ              ;if not, branch
       jmp INJUREPLAYER      ;otherwise go back to hurt player
LINJ:  jsr ENEMYTURNAROUND   ;turn the enemy around, if necessary
       jmp INJUREPLAYER      ;go back to hurt player


ENEMYFACEPLAYER:
       ldy #$01               ;set to move right by default
       jsr PLAYERENEMYDIFF    ;get horizontal difference between player and enemy
       bpl SFCRT              ;if enemy is to the right of player, do not increment
       iny                    ;otherwise, increment to set to move to the left
SFCRT: sty !Enemy_MovingDir,x  ;set moving direction here
       dey                    ;then decrement to use as a proper offset
       rts

SETUPFLOATEYNUMBER:
       sta !FloateyNum_Control,x ;set number of points control for floatey numbers
       lda #$30
       sta !FloateyNum_Timer,x   ;set timer for floatey numbers
       lda !Enemy_Y_Position,x
       sta !FloateyNum_Y_Pos,x   ;set vertical coordinate
       lda !Enemy_Rel_XPos
       sta !FloateyNum_X_Pos,x   ;set horizontal coordinate and leave
EXSFN: rts

;-------------------------------------------------------------------------------------
;$01 - used to hold enemy offset for second enemy

SETBITSMASK:
      db %10000000, %01000000, %00100000, %00010000, %00001000, %00000100, %00000010

CLEARBITSMASK:
      db %01111111, %10111111, %11011111, %11101111, %11110111, %11111011, %11111101

ENEMIESCOLLISION:
        lda !FrameCounter            ;check counter for d0 set
        lsr
        bcc EXSFN                   ;if d0 not set, leave
        lda !AreaType
        beq EXSFN                   ;if water area type, leave
        lda !Enemy_ID,x
        cmp #$15                    ;if enemy object => $15, branch to leave
        bcs EXITECROUTINE
        cmp #!Lakitu                 ;if lakitu, branch to leave
        beq EXITECROUTINE
        cmp #!PiranhaPlant           ;if piranha plant, branch to leave
        beq EXITECROUTINE
        lda !EnemyOffscrBitsMasked,x ;if masked offscreen bits nonzero, branch to leave
        bne EXITECROUTINE
        jsr GETENEMYBOUNDBOXOFS     ;otherwise, do sub, get appropriate bounding box offset for
        dex                         ;first enemy we're going to compare, then decrement for second
        bmi EXITECROUTINE           ;branch to leave if there are no other enemies
ECLOOP: stx $01                     ;save enemy object buffer offset for second enemy here
        tya                         ;save first enemy's bounding box offset to stack
        pha
        lda !Enemy_Flag,x            ;check enemy object enable flag
        beq READYNEXTENEMY          ;branch if flag not set
        lda !Enemy_ID,x
        cmp #$15                    ;check for enemy object => $15
        bcs READYNEXTENEMY          ;branch if true
        cmp #!Lakitu
        beq READYNEXTENEMY          ;branch if enemy object is lakitu
        cmp #!PiranhaPlant
        beq READYNEXTENEMY          ;branch if enemy object is piranha plant
        lda !EnemyOffscrBitsMasked,x
        bne READYNEXTENEMY          ;branch if masked offscreen bits set
        txa                         ;get second enemy object's bounding box offset
        asl                         ;multiply by four, then add four
        asl
        clc
        adc #$04
        tax                         ;use as new contents of X
        jsr SPROBJECTCOLLISIONCORE  ;do collision detection using the two enemies here
        ldx !ObjectOffset            ;use first enemy offset for X
        ldy $01                     ;use second enemy offset for Y
        bcc NOENEMYCOLLISION        ;if carry clear, no collision, branch ahead of this
        lda !Enemy_State,x
        ora !Enemy_State,y           ;check both enemy states for d7 set
        and #%10000000
        bne YESEC                   ;branch if at least one of them is set
        lda !Enemy_CollisionBits,y   ;load first enemy's collision-related bits
        and SETBITSMASK,x           ;check to see if bit connected to second enemy is
        bne READYNEXTENEMY          ;already set, and move onto next enemy slot if set
        lda !Enemy_CollisionBits,y
        ora SETBITSMASK,x           ;if the bit is not set, set it now
        sta !Enemy_CollisionBits,y
YESEC:  jsr PROCENEMYCOLLISIONS     ;react according to the nature of collision
        jmp READYNEXTENEMY          ;move onto next enemy slot

NOENEMYCOLLISION:
      lda !Enemy_CollisionBits,y     ;load first enemy's collision-related bits
      and CLEARBITSMASK,x           ;clear bit connected to second enemy
      sta !Enemy_CollisionBits,y     ;then move onto next enemy slot

READYNEXTENEMY:
      pla              ;get first enemy's bounding box offset from the stack
      tay              ;use as Y again
      ldx $01          ;get and decrement second enemy's object buffer offset
      dex
      bpl ECLOOP       ;loop until all enemy slots have been checked

EXITECROUTINE:
      ldx !ObjectOffset ;get enemy object buffer offset
      rts              ;leave

PROCENEMYCOLLISIONS:
      lda !Enemy_State,y        ;check both enemy states for d5 set
      ora !Enemy_State,x
      and #%00100000           ;if d5 is set in either state, or both, branch
      bne EXITPROCESSECOLL     ;to leave and do nothing else at this point
      lda !Enemy_State,x
      cmp #$06                 ;if second enemy state < $06, branch elsewhere
      bcc PROCSECONDENEMYCOLL
      lda !Enemy_ID,x           ;check second enemy identifier for hammer bro
      cmp #!HammerBro           ;if hammer bro found in alt state, branch to leave
      beq EXITPROCESSECOLL
      lda !Enemy_State,y        ;check first enemy state for d7 set
      asl
      bcc SHELLCOLLISIONS      ;branch if d7 is clear
      lda #$06
      jsr SETUPFLOATEYNUMBER   ;award 1000 points for killing enemy
      jsr SHELLORBLOCKDEFEAT   ;then kill enemy, then load
      ldy $01                  ;original offset of second enemy

SHELLCOLLISIONS:
      tya                      ;move Y to X
      tax
      jsr SHELLORBLOCKDEFEAT   ;kill second enemy
      ldx !ObjectOffset
      lda !ShellChainCounter,x  ;get chain counter for shell
      clc
      adc #$04                 ;add four to get appropriate point offset
      ldx $01
      jsr SETUPFLOATEYNUMBER   ;award appropriate number of points for second enemy
      ldx !ObjectOffset         ;load original offset of first enemy
      inc !ShellChainCounter,x  ;increment chain counter for additional enemies

EXITPROCESSECOLL:
      rts                      ;leave!!!

PROCSECONDENEMYCOLL:
      lda !Enemy_State,y        ;if first enemy state < $06, branch elsewhere
      cmp #$06
      bcc MOVEEOFS
      lda !Enemy_ID,y           ;check first enemy identifier for hammer bro
      cmp #!HammerBro           ;if hammer bro found in alt state, branch to leave
      beq EXITPROCESSECOLL
      jsr SHELLORBLOCKDEFEAT   ;otherwise, kill first enemy
      ldy $01
      lda !ShellChainCounter,y  ;get chain counter for shell
      clc
      adc #$04                 ;add four to get appropriate point offset
      ldx !ObjectOffset
      jsr SETUPFLOATEYNUMBER   ;award appropriate number of points for first enemy
      ldx $01                  ;load original offset of second enemy
      inc !ShellChainCounter,x  ;increment chain counter for additional enemies
      rts                      ;leave!!!

MOVEEOFS:
      tya                      ;move Y ($01) to X
      tax
      jsr ENEMYTURNAROUND      ;do the sub here using value from $01
      ldx !ObjectOffset         ;then do it again using value from $08

ENEMYTURNAROUND:
       lda !Enemy_ID,x           ;check for specific enemies
       cmp #!PiranhaPlant
       beq EXTA                 ;if piranha plant, leave
       cmp #!Lakitu
       beq EXTA                 ;if lakitu, leave
       cmp #!HammerBro
       beq EXTA                 ;if hammer bro, leave
       cmp #!Spiny
       beq RXSPD                ;if spiny, turn it around
       cmp #!GreenParatroopaJump
       beq RXSPD                ;if green paratroopa, turn it around
       cmp #$07
       bcs EXTA                 ;if any OTHER enemy object => $07, leave
RXSPD: lda !Enemy_X_Speed,x      ;load horizontal speed
       eor #$ff                 ;get two's compliment for horizontal speed
       tay
       iny
       sty !Enemy_X_Speed,x      ;store as new horizontal speed
       lda !Enemy_MovingDir,x
       eor #%00000011           ;invert moving direction and store, then leave
       sta !Enemy_MovingDir,x    ;thus effectively turning the enemy around
EXTA:  rts                      ;leave!!!

;-------------------------------------------------------------------------------------
;$00 - vertical position of platform

LARGEPLATFORMCOLLISION:
       lda #$ff                     ;save value here
       sta !PlatformCollisionFlag,x
       lda !TimerControl             ;check master timer control
       bne EXLPC                    ;if set, branch to leave
       lda !Enemy_State,x            ;if d7 set in object state,
       bmi EXLPC                    ;branch to leave
       lda !Enemy_ID,x
       cmp #$24                     ;check enemy object identifier for
       bne CHKFORPLAYERC_LARGEP     ;balance platform, branch if not found
       lda !Enemy_State,x
       tax                          ;set state as enemy offset here
       jsr CHKFORPLAYERC_LARGEP     ;perform code with state offset, then original offset, in X

CHKFORPLAYERC_LARGEP:
       jsr CHECKPLAYERVERTICAL      ;figure out if player is below a certain point
       bcs EXLPC                    ;or offscreen, branch to leave if true
       txa
       jsr GETENEMYBOUNDBOXOFSARG   ;get bounding box offset in Y
       lda !Enemy_Y_Position,x       ;store vertical coordinate in
       sta $00                      ;temp variable for now
       txa                          ;send offset we're on to the stack
       pha
       jsr PLAYERCOLLISIONCORE      ;do player-to-platform collision detection
       pla                          ;retrieve offset from the stack
       tax
       bcc EXLPC                    ;if no collision, branch to leave
       jsr PROCLPLATCOLLISIONS      ;otherwise collision, perform sub
EXLPC: ldx !ObjectOffset             ;get enemy object buffer offset and leave
       rts

;--------------------------------
;$00 - counter for bounding boxes

SMALLPLATFORMCOLLISION:
      lda !TimerControl             ;if master timer control set,
      bne EXSPC                    ;branch to leave
      sta !PlatformCollisionFlag,x  ;otherwise initialize collision flag
      jsr CHECKPLAYERVERTICAL      ;do a sub to see if player is below a certain point
      bcs EXSPC                    ;or entirely offscreen, and branch to leave if true
      lda #$02
      sta $00                      ;load counter here for 2 bounding boxes

CHKSMALLPLATLOOP:
      ldx !ObjectOffset           ;get enemy object offset
      jsr GETENEMYBOUNDBOXOFS    ;get bounding box offset in Y
      and #%00000010             ;if d1 of offscreen lower nybble bits was set
      bne EXSPC                  ;then branch to leave
      lda !BoundingBox_UL_YPos,y  ;check top of platform's bounding box for being
      cmp #$20                   ;above a specific point
      bcc MOVEBOUNDBOX           ;if so, branch, don't do collision detection
      jsr PLAYERCOLLISIONCORE    ;otherwise, perform player-to-platform collision detection
      bcs PROCSPLATCOLLISIONS    ;skip ahead if collision

MOVEBOUNDBOX:
       lda !BoundingBox_UL_YPos,y  ;move bounding box vertical coordinates
       clc                        ;128 pixels downwards
       adc #$80
       sta !BoundingBox_UL_YPos,y
       lda !BoundingBox_DR_YPos,y
       clc
       adc #$80
       sta !BoundingBox_DR_YPos,y
       dec $00                    ;decrement counter we set earlier
       bne CHKSMALLPLATLOOP       ;loop back until both bounding boxes are checked
EXSPC: ldx !ObjectOffset           ;get enemy object buffer offset, then leave
       rts

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

PROCSPLATCOLLISIONS:
      ldx !ObjectOffset             ;return enemy object buffer offset to X, then continue

PROCLPLATCOLLISIONS:
      lda !BoundingBox_DR_YPos,y    ;get difference by subtracting the top
      sec                          ;of the player's bounding box from the bottom
      sbc !BoundingBox_UL_YPos      ;of the platform's bounding box
      cmp #$04                     ;if difference too large or negative,
      bcs CHKFORTOPCOLLISION       ;branch, do not alter vertical speed of player
      lda !Player_Y_Speed           ;check to see if player's vertical speed is moving down
      bpl CHKFORTOPCOLLISION       ;if so, don't mess with it
      lda #$01                     ;otherwise, set vertical
      sta !Player_Y_Speed           ;speed of player to kill jump

CHKFORTOPCOLLISION:
      lda !BoundingBox_DR_YPos      ;get difference by subtracting the top
      sec                          ;of the platform's bounding box from the bottom
      sbc !BoundingBox_UL_YPos,y    ;of the player's bounding box
      cmp #$06
      bcs PLATFORMSIDECOLLISIONS   ;if difference not close enough, skip all of this
      lda !Player_Y_Speed
      bmi PLATFORMSIDECOLLISIONS   ;if player's vertical speed moving upwards, skip this
      lda $00                      ;get saved bounding box counter from earlier
      ldy !Enemy_ID,x
      cpy #$2b                     ;if either of the two small platform objects are found,
      beq SETCOLLISIONFLAG         ;regardless of which one, branch to use bounding box counter
      cpy #$2c                     ;as contents of collision flag
      beq SETCOLLISIONFLAG
      txa                          ;otherwise use enemy object buffer offset

SETCOLLISIONFLAG:
      ldx !ObjectOffset             ;get enemy object buffer offset
      sta !PlatformCollisionFlag,x  ;save either bounding box counter or enemy offset here
      lda #$00
      sta !Player_State             ;set player state to normal then leave
      rts

PLATFORMSIDECOLLISIONS:
         lda #$01                   ;set value here to indicate possible horizontal
         sta $00                    ;collision on left side of platform
         lda !BoundingBox_DR_XPos    ;get difference by subtracting platform's left edge
         sec                        ;from player's right edge
         sbc !BoundingBox_UL_XPos,y
         cmp #$08                   ;if difference close enough, skip all of this
         bcc SIDEC
         inc $00                    ;otherwise increment value set here for right side collision
         lda !BoundingBox_DR_XPos,y  ;get difference by subtracting player's left edge
         clc                        ;from platform's right edge
         sbc !BoundingBox_UL_XPos
         cmp #$09                   ;if difference not close enough, skip subroutine
         bcs NOSIDEC                ;and instead branch to leave (no collision)
SIDEC:   jsr IMPEDEPLAYERMOVE       ;deal with horizontal collision
NOSIDEC: ldx !ObjectOffset           ;return with enemy object buffer offset
         rts

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

PLAYERPOSSPLATDATA:
      db $80, $00

POSITIONPLAYERONS_PLAT:
      tay                        ;use bounding box counter saved in collision flag
      lda !Enemy_Y_Position,x     ;for offset
      clc                        ;add positioning data using offset to the vertical
      adc PLAYERPOSSPLATDATA-1,y ;coordinate
      db $2c                    ;BIT instruction opcode

POSITIONPLAYERONVPLAT:
         lda !Enemy_Y_Position,x    ;get vertical coordinate
         ldy !GameEngineSubroutine
         cpy #$0b                  ;if certain routine being executed on this frame,
         beq EXPLPOS               ;skip all of this
         ldy !Enemy_Y_HighPos,x
         cpy #$01                  ;if vertical high byte offscreen, skip this
         bne EXPLPOS
         sec                       ;subtract 32 pixels from vertical coordinate
         sbc #$20                  ;for the player object's height
         sta !Player_Y_Position     ;save as player's new vertical coordinate
         tya
         sbc #$00                  ;subtract borrow and store as player's
         sta !Player_Y_HighPos      ;new vertical high byte
         lda #$00
         sta !Player_Y_Speed        ;initialize vertical speed and low byte of force
         sta !Player_Y_MoveForce    ;and then leave
EXPLPOS: rts

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

CHECKPLAYERVERTICAL:
       lda !Player_OffscreenBits  ;if player object is completely offscreen
       cmp #$f0                  ;vertically, leave this routine
       bcs EXCPV
       ldy !Player_Y_HighPos      ;if player high vertical byte is not
       dey                       ;within the screen, leave this routine
       bne EXCPV
       lda !Player_Y_Position     ;if on the screen, check to see how far down
       cmp #$d0                  ;the player is vertically
EXCPV: rts

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

GETENEMYBOUNDBOXOFS:
      lda !ObjectOffset         ;get enemy object buffer offset

GETENEMYBOUNDBOXOFSARG:
      asl                      ;multiply A by four, then add four
      asl                      ;to skip player's bounding box
      clc
      adc #$04
      tay                      ;send to Y
      lda !Enemy_OffscreenBits  ;get offscreen bits for enemy object
      and #%00001111           ;save low nybble
      cmp #%00001111           ;check for all bits set
      rts

;-------------------------------------------------------------------------------------
;$00-$01 - used to hold many values, essentially temp variables
;$04 - holds lower nybble of vertical coordinate from block buffer routine
;$eb - used to hold block buffer adder

PLAYERBGUPPEREXTENT:
      db $20, $10

PLAYERBGCOLLISION:
          lda !DisableCollisionDet   ;if collision detection disabled flag set,
          bne EXPBGCOL              ;branch to leave
          lda !GameEngineSubroutine
          cmp #$0b                  ;if running routine #11 or $0b
          beq EXPBGCOL              ;branch to leave
          cmp #$04
          bcc EXPBGCOL              ;if running routines $00-$03 branch to leave
          lda #$01                  ;load default player state for swimming
          ldy !SwimmingFlag          ;if swimming flag set,
          bne SETPSTE               ;branch ahead to set default state
          lda !Player_State          ;if player in normal state,
          beq SETFALLS              ;branch to set default state for falling
          cmp #$03
          bne CHKONSCR              ;if in any other state besides climbing, skip to next part
SETFALLS: lda #$02                  ;load default player state for falling
SETPSTE:  sta !Player_State          ;set whatever player state is appropriate
CHKONSCR: lda !Player_Y_HighPos
          cmp #$01                  ;check player's vertical high byte for still on the screen
          bne EXPBGCOL              ;branch to leave if not
          lda #$ff
          sta !Player_CollisionBits  ;initialize player's collision flag
          lda !Player_Y_Position
          cmp #$cf                  ;check player's vertical coordinate
          bcc CHKCOLLSIZE           ;if not too close to the bottom of screen, continue
EXPBGCOL: rts                       ;otherwise leave

CHKCOLLSIZE:
         ldy #$02                    ;load default offset
         lda !CrouchingFlag
         bne GBBADR                  ;if player crouching, skip ahead
         lda !PlayerSize
         bne GBBADR                  ;if player small, skip ahead
         dey                         ;otherwise decrement offset for big player not crouching
         lda !SwimmingFlag
         bne GBBADR                  ;if swimming flag set, skip ahead
         dey                         ;otherwise decrement offset
GBBADR:  lda BLOCKBUFFERADDERDATA,y  ;get value using offset
         sta $eb                     ;store value here
         tay                         ;put value into Y, as offset for block buffer routine
         ldx !PlayerSize              ;get player's size as offset
         lda !CrouchingFlag
         beq HEADCHK                 ;if player not crouching, branch ahead
         inx                         ;otherwise increment size as offset
HEADCHK: lda !Player_Y_Position       ;get player's vertical coordinate
         cmp PLAYERBGUPPEREXTENT,x   ;compare with upper extent value based on offset
         bcc DOFOOTCHECK             ;if player is too high, skip this part
         jsr BLOCKBUFFERCOLLI_HEAD   ;do player-to-bg collision detection on top of
         beq DOFOOTCHECK             ;player, and branch if nothing above player's head
         jsr CHECKFORCOINMTILES      ;check to see if player touched coin with their head
         bcs AWARDTOUCHEDCOIN        ;if so, branch to some other part of code
         ldy !Player_Y_Speed          ;check player's vertical speed
         bpl DOFOOTCHECK             ;if player not moving upwards, branch elsewhere
         ldy $04                     ;check lower nybble of vertical coordinate returned
         cpy #$04                    ;from collision detection routine
         bcc DOFOOTCHECK             ;if low nybble < 4, branch
         jsr CHECKFORSOLIDMTILES     ;check to see what player's head bumped on
         bcs SOLIDORCLIMB            ;if player collided with solid metatile, branch
         ldy !AreaType                ;otherwise check area type
         beq NYSPD                   ;if water level, branch ahead
         ldy !BlockBounceTimer        ;if block bounce timer not expired,
         bne NYSPD                   ;branch ahead, do not process collision
         jsr PLAYERHEADCOLLISION     ;otherwise do a sub to process collision
         jmp DOFOOTCHECK             ;jump ahead to skip these other parts here

SOLIDORCLIMB:
       cmp #$26               ;if climbing metatile,
       beq NYSPD              ;branch ahead and do not play sound
       lda #!Sfx_Bump
       sta !1DF9  ;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 !1DF9      ;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
	LDA #!Sfx_Flagpole
      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 !1DF9     ;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 !1DFC ;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 !1DF9  ;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 !1DF9       ;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
	INY
         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 #%00000010             ;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 $26, $26, $E6, $E6

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 #$22;#$01
	STZ $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_Y_Position+20,y      ;dump A contents
      sta !Sprite_Y_Position+16,y      ;into third row sprites

DUMPFOURSPR:
      sta !Sprite_Y_Position+12,y      ;into second row sprites

DUMPTHREESPR:
      sta !Sprite_Y_Position+8,y

DUMPTWOSPR:
      sta !Sprite_Y_Position+4,y       ;and into first row sprites
      sta !Sprite_Y_Position,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 #$24                    ;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 #$24
          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 #$24
         sta !Sprite_Attributes,y     ;set attribute byte in first sprite
         lda #$A4
         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 $04, $02, $04, $02

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
	EOR #$20
      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 #%00000110             ;mask out all but d1 and d0 (previously d2 and d1)
        ora !Enemy_SprAttrib+5      ;add background priority bit if any set
	EOR #$20
        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 $02, $04, $06, $04, $02, $02, $06, $06
      db $06, $02, $02, $04, $04, $22, $02, $04
      db $02, $02, $04, $ff, $04, $04, $02, $02
      db $04, $04, $04

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 #$06
       ldy !EnemyFrameTimer,x       ;get timer for enemy object
       beq SBBAT                   ;if expired, do not set priority bit
	ORA #$20              ;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
	EOR #$20
             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 #$64                    ;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 #%10100110
        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 set
        lda !Sprite_Attributes+16,y  ;save vertical flip and palette bits
        and #%10100010              ;in third row left sprite
        sta !Sprite_Attributes+16,y
        lda !Sprite_Attributes+20,y  ;set horizontal flip and palette bits
        ora #%01100010              ;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 #%10100010
        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 #%10100010
        sta !Sprite_Attributes,y     ;save vertical flip and palette bits
        lda !Sprite_Attributes+4,y   ;get first row right sprite attributes
        ora #%01100010              ;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 #$A4
      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_Y_Position+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 #$26
           sta $04                       ;set attribute byte here
	LDA #$01
           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 #$26                      ;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
	LDA #$22
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 #%10100110
           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 #$24                   ;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 #$26                   ;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 #$24                 ;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 #$24                    ;set palette attributes for all sprites, but
      sta !Sprite_Attributes,y     ;set no flip at all for first sprite
      lda #$A4
      sta !Sprite_Attributes+4,y   ;set vertical flip for second sprite
      lda #$64
      sta !Sprite_Attributes+8,y   ;set horizontal flip for third sprite
      lda #$E4
      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 #$24                    ;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 #$24
        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 PLAYERGRAPHICSTABLE+$9E  ;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, $20, $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+28       ;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
	EOR #$20
        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 offset to sprite object data
;$05 - used as adder in DIVIDEPDIFF
;$06 - used to store constant used to compare to pixel difference in $07
;$07 - used to store pixel difference between X positions 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 page location of object position from it
          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 to get tile difference
          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

SPCComm:
	LDA !OperMode
	BEQ .skip
	LDA !PauseSoundQueue
	BEQ +
	CLC
	ADC #$10
	STA $2140
	STZ !PauseSoundQueue
	BRA .end
+
	LDA !1DF9
	STA $2140
	LDA !1DFA
	STA $2141
	LDA !1DFC
	STA $2143
	LDA !EventMusicBuffer
	BEQ +
	DEC !EventMusicBuffer
+
	LDA !EventMusicQueue
	BEQ ++
	BPL +++
	LDA #$FF
	STA $2140
	BRA ++++
+++
	LDY #$00
	CMP #$09
	BEQ +++
	CMP #$0C
	BNE +++++
+++
	LDY #$B4
	STY !EventMusicBuffer
+++++
	STA $2142
++++
	STZ !EventMusicQueue
	BRA +++
++
	LDA !AreaMusicQueue
	BEQ .end
	STA $2142
	STA !AreaMusicBuffer
+++
	STZ !AreaMusicQueue
.end
	STZ !1DF9
	STZ !1DFA
	STZ !1DFC
.skip
	RTS

TITLESCREEN:
db $20,$A6,$54,$26
db $20,$C6,$54,$26
db $20,$E6,$54,$26
db $21,$06,$54,$26
db $20,$85,$01,$44
db $20,$86,$54,$48
db $20,$9A,$01,$49
db $20,$A5,$89,$46 ;!
db $46,$46,$46,$46,$46,$46,$46,$46
db $20,$BA,$89,$4A ;!
db $4A,$4A,$4A,$4A,$4A,$4A,$4A,$4A
db $20,$A6,$0A,$D0,$D1,$D8,$D8,$DE
db $D1,$D0,$DA,$DE,$D1
db $20,$C6,$0A,$D2,$D3,$DB,$DB,$DB
db $D9,$DB,$DC,$DB,$DF
db $20,$E6,$0A,$D4,$D5,$D4,$D9,$DB
db $E2,$D4,$DA,$DB,$E0
db $21,$06,$0A,$D6,$D7,$D6,$D7,$E1
db $26,$D6,$DD,$E1,$E1
db $21,$26,$14,$D0,$E8,$D1,$D0,$D1
db $DE,$D1,$D8,$D0,$D1,$26,$DE,$D1
db $DE,$D1,$D0,$D1,$D0,$D1,$26
db $21,$46,$14,$DB,$42,$42,$DB,$42
db $DB,$42,$DB,$DB,$42,$26,$DB,$42
db $DB,$42,$DB,$42,$DB,$42,$26
db $21,$66,$46,$DB
db $21,$6C,$0E,$DF,$DB,$DB,$DB,$26
db $DB,$DF,$DB,$DF,$DB,$DB,$E4,$E5
db $26
db $21,$86,$14,$DB,$DB,$DB,$DE,$43
db $DB,$E0,$DB,$DB,$DB,$26,$DB,$E3
db $DB,$E0,$DB,$DB,$E6,$E3,$26
db $21,$A6,$14,$DB,$DB,$DB,$DB,$42
db $DB,$DB,$DB,$D4,$D9,$26,$DB,$D9
db $DB,$DB,$D4,$D9,$D4,$D9,$E7
db $21,$C5,$16,$5F,$95,$95,$95,$95
db $95,$95,$95,$95,$97,$98,$78,$95
db $96,$95,$95,$97,$98,$97,$98,$95
db $7A
db $21,$ED,$0E,$CF,$01,$09,$08,$05
db $24,$17,$12,$17,$1D,$0E,$17,$0D
db $18
db $22,$4B,$0D,$01,$24,$19,$15,$0A
db $22,$0E,$1B,$24,$10,$0A,$16,$0E
db $22,$8B,$0D,$02,$24,$19,$15,$0A
db $22,$0E,$1B,$24,$10,$0A,$16,$0E
db $22,$EC,$04,$1D,$18,$19,$28
db $22,$F6,$01,$00
;db $23,$C9,$56,$55
;db $23,$E2,$04,$99,$AA,$AA,$AA
;db $23,$EA,$04,$99,$AA,$AA,$AA
db $00

back to listings