PRGROM

by asmagicianmaks

Game: SMB

Description:

!SRAM = $7007E6
UploadPRG:
	LDA !carl_initialized
	BEQ +
if !sa1
	STZ $3010
else
	STZ $0D10
endif
	RTL
+
	REP #$20
	LDA $7FC700	;check for CHR module
	CMP #$ADDE
	BNE +
	LDA $7FC702
	CMP #$EFBE
	BEQ ++
	RTL
++
	SEP #$20
	LDA #$80	;disable display
	STA !carl_initialized
	STA $2100
	STZ $420C
	STZ $4200

	TXA			;unload all modules besides this and CHR
	LSR #3
	STA $00
	LDA $7FC707
	STA $01
	LDX #$0C
	LDA #$00
-
	CPX $00
	BEQ +
	CPX $01
	BEQ +
	STA.l !SRAM,x
+
	DEX
	BPL -

	LDA $7FC706
	STA $F2
	STA $F5
	REP #$30
	LDA $7FC704
	STA $F0
	SEP #$20

	LDA #$11		;sprites and layer 1 main and sub screen
	STA $212C
	STA $212D
	LDA #$04		;overscan
	STA $2133
	LDA #$23		;64x64 tilemap at $2000
	STA $2107
	STZ $2101		;8x8 and 16x16 sprites, sprites tiles at $0000
	LDA #$01
	STA $210B		;layer 1 tiles at $1000
	STZ $2106		;no mosaic
	LDA $F2
	STA $8C
	LDA.b #$7F
	STA $02
	REP #$20
	STZ $00
	LDA [$F0]
	PHK
	PEA .decomp-1
	PEA $804C
	JML $00B8DA|!bank
.decomp		;AXY 8 bit
	LDA #$80
	STA $2115
	LDX #$00
	REP #$30
	STZ $2116
---
	LDY #$0007
-
	LDA.l $7F0000,x
	STA $2118
	INX
	INX
	DEY
	BPL -
	LDY #$0007
--
	STZ $2118
	DEY
	BPL --
	CPX #$2000
	BCC ---

	LDA #$F000	;clear OAM RAM
	LDX #$01FE
-
	STA $0800,x
	DEX
	DEX
	BPL -
	LDX #$001E	;additional OAM RAM
-
	STZ $0A00,x
	DEX
	DEX
	BPL -

	LDA.l $0E8000	;AMK detection
	CMP #$4140
	BNE +
	LDA.l $0E8002
	CMP #$4B4D
	BNE +
	SEP #$20
	LDA #$FF
	STA $2142
	STA $1E00|!addr
	BRA .skipspc
+
	LDY #$0002
	LDA [$F0],y
	STA $00
	SEP #$30
	LDA $F2
	STA $02
	PHK
	PEA .spc-1
	PEA $804C
	LDA #$FF
	STA $2141
	JML $0080F7|!bank
.spc
	LDX #$03
-
	STZ $1DF9|!addr,x
	STZ $1DFD|!addr,x
	DEX
	BPL -
.skipspc
	REP #$30
	LDA #$0000
	TCD
	SEP #$30
	JML PRGStart

;Addresses that have to be set for NMI
;$xx10	has to be not 0
;$xx40
;$xx41
;$xx42
;$xx43
;$xx44
;$0D9B	has to be $02
;$0D9F
;$0DAE
;$13C6
;$1DF9 if not using AMK
;$1DFA
;$1DFB
;$1DFC
;$1DFF

;if !AMK
;	!1DFA = $ff
;	!Sfx_SmallJump			= $2B
;	!Sfx_BigJump			= $2B
;!Silence				= $FF
;!StarPowerMusic		= $05
;!PipeIntroMusic		= $08
;!CloudMusic			= $06
;!CastleMusic			= $0A
;!UndergroundMusic		= $0A
;!WaterMusic			= $0A
;!GroundMusic			= $0A
;!TimeRunningOutMusic	= $80
;!EndOfLevelMusic		= $04
;!AltGameOverMusic		= $02
;!EndOfCastleMusic		= $03
;!VictoryMusic			= $07
;!GameOverMusic			= $02
;!DeathMusic			= $01
;
;endif

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

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

!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 #$0a						;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.b #%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.b #%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.b #%01111111			;save all other bits
				sta !Mirror_PPU_CTRL_REG1
				lda !Mirror_PPU_CTRL_REG2  ;disable OAM and background display by default
				and.b #%11100110
				ldy !DisableScreenFlag		;get screen disable flag
				bne SCREENOFF			;if set, used bits as-is
				ora.b #%00011110
SCREENOFF:		sta !Mirror_PPU_CTRL_REG2  ;save bits for later but not in register at the moment
	LDA #$80
	STA $2100
	STZ $210D	;reset layer 1 pos
	STZ $210D
	STZ $210E
	STZ $210E

	STZ $4300	;do OAM DMA
	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.b #%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 $1E00|!addr
		BNE .amk
	LDA #$12
	STA $2140
.amk
	LDA #$80
	STA $2100
	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.b #%00000010			;mask out all but d1
				sta $00					;save here
				lda !PseudoRandomBitReg+1  ;get second memory location
				and.b #%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:
	PHD				;run vanilla NMI and CARL
	REP #$20
if !sa1
	LDA #$3000
else
	LDA #$0D00
endif
	TCD
	STZ $41
	STZ $43
	SEP #$20
	LDA #$20
	STA $40
if !sa1
	STZ $317F
endif
	STZ $13C6|!addr
	STZ $0D9F|!addr
	LDA #$02
	STA $10
	STA $0D9B|!addr
	LDA #$80
	STA $0DAE|!addr
	STA $4200
	BRA +
-
	WAI
+
if !sa1
	LDA $3010
else
	LDA $0D10
endif
	BNE -
	STZ $4200
	PLD
	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.b #!Start_Button		;on controller 1
				beq CLRPAUSETIMER
				lda !GamePauseStatus		;check to see if timer flag is set
				and.b #%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.b #%00000001			;invert d0 and set d7
				ora.b #%10000000
				bne SETPAUSE				;unconditional branch
CLRPAUSETIMER: lda !GamePauseStatus		;clear timer flag if timer is at zero and START button
				and.b #%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.b #!Start_Button
				beq STARTGAME
				cmp.b #!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.b #!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 #!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.b #%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.b #%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 #!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.b #%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.b #%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.b #%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.b #%00000001				;mask out all but LSB, then invert LSB, multiply by 2
			eor.b #%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.b #%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.b #%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.b #%00011111				;to be written to, mask out all but 5 LSB,
			sec						;subtract four
			sbc #$04
			and.b #%00011111				;mask out bits again and store
			sta $01
			lda !CurrentNTAddr_High   ;get high byte and branch if borrow not set
			bcs SETATHIGH
			eor.b #%00000100				;otherwise invert d2
SETATHIGH:	and.b #%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.b #%00010000
		and.b #%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.b #%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.b #%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.b #%01000000
	BEQ +
	ORA #$80
+
	AND.b #%10110000
	PHA
	TXA
	AND.b #%00000011
	ORA $01,s
	STA $2121
	STA $01,s
	INY
	BIT.b #%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.b #%00000011
	BNE -
	TAX
	LDA $01,s
	CLC
	ADC #$10
	BIT.b #%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.b #%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.b #%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.b #%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.b #%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.b #!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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%01000000
		bne ALTER2					;branch if d6 is set
		pla
		pha						;pull and push offset to copy to A
		and.b #%00001111				;mask out high nybble and store as
		sta !TerrainControl		;new terrain height type bits
		pla
		and.b #%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.b #%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.b #%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.w !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.b #%00001111
		sta $07					;save row location
		iny
		lda (!AreaData),y			;get next byte, save lower nybble (length or height)
		and.b #%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.b #%00001111			;pull from stack, mask out high nybble
		clc
		adc BLOCKBUFFERADDR,y		;add to low byte
		sta $06					;store here and leave
		rts

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

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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%00001111			;mask out all but lower nybble
			sta !TerrainControl
			pla						;pull and push byte to copy it to A
			pha
			and.b #%00110000			;save 2 MSB for background scenery type
			lsr
			lsr						;shift bits to LSBs
			lsr
			lsr
			sta !BackgroundScenery		;save as background scenery
			pla
			and.b #%11000000
			clc
			rol						;rotate bits over to LSBs
			rol
			rol
			cmp.b #%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

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

;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.b #%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.b #%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.b #%11000000
			sta !A_B_Buttons
			lda !SavedJoypadBits		;store left and right buttons in $0c
			and.b #%00000011
			sta !Left_Right_Buttons
			lda !SavedJoypadBits		;store up and down buttons in $0b
			and.b #%00001100
			sta !Up_Down_Buttons
			and.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #!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.b #%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.b #%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.b #!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 #!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.b #%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 #!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.b #%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.b #%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.b #%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.b #!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.w !Block_PageLoc,y
		sta !Enemy_PageLoc,x		;copy page location from previous object
		lda.w !Block_X_Position,y
		sta !Enemy_X_Position,x		;copy horizontal coordinate from previous object
		lda.w !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.b #%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.b #%00001100			;mask out bits
			cmp.b #%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.b #%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.b #%00000111			;second part of LSFR
			bne SETMOFS				;if any bits are set, branch and use as offset
			lda !PseudoRandomBitReg+1
			and.b #%00001000			;get d3 from same part of LSFR
SETMOFS:  tay						;use either d3 or d2-d0 for offset here
			lda.w !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.w !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.b #%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.w !Enemy_State,y			;get enemy object state
			and.b #%11110111			;mask out d3
			sta.w !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.w !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.w !Enemy_PageLoc,y			;get enemy's page location
			adc #$00					;add carry
			sta !Misc_PageLoc,x			;store as hammer's page location
			lda.w !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.w !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.w !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.w !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.w !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.w !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.w !Misc_Y_Position,y	;store as vertical coordinate
JCOINC: lda #$fb
		sta.w !Misc_Y_Speed,y		;set vertical speed
		lda #$01
		sta.w !Misc_Y_HighPos,y	;set vertical high byte
		sta.w !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.w !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.b #%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.b #%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.b #%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.b #%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.b #%00001111			;mask out high nybble
			tay
			lda.w !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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.w !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.w !Enemy_State,y			;if lakitu is not in normal state, branch to leave
			bne EXLSHAND
			lda.w !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.w !Enemy_X_Position,y
			sta !Enemy_X_Position,x
			lda #$01					;put spiny within vertical screen unit
			sta !Enemy_Y_HighPos,x
			lda.w !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.b #%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.b #%00000011			;get one of the LSFR parts and save the 2 LSB
			beq USEPOSV				;branch if neither bits are set
			tya
			eor.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.w !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.b #%10000000			;store with d7 set as flag in new enemy
		sta.w !Enemy_Flag,y		;slot as well as enemy offset
		lda !Enemy_PageLoc,x
		sta.w !Enemy_PageLoc,y	;copy page location and horizontal coordinates
		lda !Enemy_X_Position,x	;from original enemy to new enemy
		sta.w !Enemy_X_Position,y
		lda #$01
		sta !Enemy_Flag,x		;set flag as normal for original enemy
		sta.w !Enemy_Y_HighPos,y	;set high vertical byte for new enemy
		lda !Enemy_Y_Position,x
		sta.w !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.w !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.b #%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.w !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.w !Enemy_PageLoc,y
		sta !Enemy_PageLoc,x			;copy page location from bowser to flame
		lda.w !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.b #%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.w !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.w !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.w !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.w !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.b #%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.b #%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.b #%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.w !Enemy_Flag,y				;if enemy buffer flag not set,
			beq BB_SLOOP				;loop back and check another slot
			lda.w !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.w !Enemy_ID,y			;check enemy identifiers
			cmp #!Lakitu				;for lakitu
			bne NEXTFSLOT
			lda #$01					;if found, set state
			sta.w !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.b #%10000000			;set d7
		sta !Enemy_Y_MoveForce,x		;store as movement force
		and.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%00100000				;check enemy state for d5 set
		bne MOVEDEFEATEDENEMY		;if set, branch to move defeated enemy object
		lda !Enemy_State,x
		and.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%00000010				;check for d1 set
		bne CHKFORFLOATDOWN			;branch if set
		lda !FrameCounter
		and.b #%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.b #%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.b #%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.b #%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.b #%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.b #%00001111				;mask out low nybble
			cmp #$09
			bcc GETHADDER				;if lower than $09, branch ahead
			eor.b #%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.b #%00001111				;mask out high nybble
			cmp #$09						;if lower than $09, branch ahead
			bcc GETVADDER
			eor.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%00000001				;to control bowser's feet
			sta !BowserBodyControls
RESETMDR:  lda !FrameCounter			;check frame counter
			and.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.w !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.w !Enemy_Y_Position,y	;for bowser's rear
			lda !Enemy_State,x
			sta.w !Enemy_State,y		;copy enemy state directly from front to rear
			lda !Enemy_MovingDir,x
			sta.w !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.b #%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.b #%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.b #%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.b #%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.w !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.w !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.w !Enemy_Y_Position,y		;add difference to vertical coordinate of other
		sta.w !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.w !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.w !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.w !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.w !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.w !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.w !Enemy_PageLoc,y
		adc #$00				;add carry to page location
		sta $02					;and save here
		pla						;pull modified horizontal coordinate
		and.b #%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.b #%00000011			;mask out all bits but d7 and d6, then set
		ora.b #%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.b #%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.w !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.b #%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.w !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.b #%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

;-------------------------------------------------------------------------------------
;$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.b #%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.b #%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.b #%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.b #%00011111			;mask out 2 MSB of enemy object's state
		ora.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.w !Enemy_State,y			;check both enemy states for d7 set
		and.b #%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.w !Enemy_State,y		;check both enemy states for d5 set
		ora !Enemy_State,x
		and.b #%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.w !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.w !Enemy_State,y		;if first enemy state < $06, branch elsewhere
		cmp #$06
		bcc MOVEEOFS
		lda.w !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.b #%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.b #%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.b #%00001111			;save low nybble
		cmp.b #%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.b #%00100000
			sta !Player_SprAttrib			;set background priority bit in player attributes
			lda !Player_X_Position
			and.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%11110000			;save high nybble
			ora.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%11110000			;save high nybble of vertical coordinate, and
		ora.b #%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.b #%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.b #%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.b #%00001111				;and mask out high nybble
		sta $04						;store masked out result here
		lda $03						;get saved content of block buffer
		rts							;and leave

;-------------------------------------------------------------------------------------
;$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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%01000000				;set horizontal flip bit for top right sprite
		sta !Sprite_Attributes+4,y
		lda !Sprite_Attributes+12,y
		ora.b #%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.b #%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.b #%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.b #%00001000		;check for every eighth frame
			bne CHECKBOWSERFRONT
			lda $03
			eor.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%10000000
		sta !Sprite_Attributes+8,y	;store bits with vertical flip in
		sta !Sprite_Attributes+16,y	;second and third row left sprites
		ora.b #%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.b #%10100010				;in third row left sprite
		sta !Sprite_Attributes+16,y
		lda !Sprite_Attributes+20,y	;set horizontal flip and palette bits
		ora.b #%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.b #%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.b #%10100010
		sta !Sprite_Attributes,y	;save vertical flip and palette bits
		lda !Sprite_Attributes+4,y	;get first row right sprite attributes
		ora.b #%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.b #%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.b #%01000000
			sta !Sprite_Attributes+4,y		;set horizontal flip bit for second sprite
			ora.b #%10000000
			sta !Sprite_Attributes+12,y		;set both flip bits for fourth sprite
			and.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%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.b #%00111111
			ora.b #%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.b #%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.b #%00111111
			ora.b #%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
		LDY $1E00|!addr
		BNE .amk0
	STA $2140
.amk0
	STZ !PauseSoundQueue
	BRA .end
+
		LDA $1E00|!addr
		BNE .amk
	LDA !1DF9
	STA $2140
	LDA !1DFA
	STA $2141
	LDA !1DFC
	STA $2143
.amk
	LDA !EventMusicBuffer
	BEQ +
	DEC !EventMusicBuffer
+
	LDA !EventMusicQueue
	BEQ ++
	BPL +++
		LDA $1E00|!addr
		BNE .amk2
	LDA #$FF
	STA $2140
.amk2
	BRA ++++
+++
	LDY #$00
	CMP #!DeathMusic
	BEQ +++
	CMP #!EndOfLevelMusic
	BNE +++++
+++
	LDY #$B4
	STY !EventMusicBuffer
+++++
		LDY $1E00|!addr
		BNE .amk3
	STA $2142
.amk3
++++
	STZ !EventMusicQueue
	BRA +++
++
	LDA !AreaMusicQueue
	BEQ .end
		LDY $1E00|!addr
		BNE .amk4
	STA $2142
.amk4
	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