JOUST 7800 NTSC Source Code

From 8BitDev.org - Atari 7800 Development Wiki
Jump to: navigation, search

Joust 7800 NTSC Source Code

This reverse engineered 7800 Joust code contains helpful context-comments and builds a 1:1 with the official BIOS ROM. In DASM syntax.


;------------------------------------------------------------------------------
; Disassembly of Joust NTSC for the Atari 7800 console
;
; 2025-08-20 - Mike Saarna
;   * Initial public release. 
;     * Game logic and all routines identified and documented.
;
; "Our goal at GCC was to bring arcade-quality games home. With the 7800,
; we wanted it to feel as close as possible to what you remembered from
; the arcade."
;                                                   --Steve Golson, GCC
;
; Joust 7800 is a faithful recreation of the arcade classic. The brilliant
; minds at GCC reverse engineered the original game to craft this stellar 
; port. To produce this disassembly I had to reverse engineer a cloaked
; machine-generated listing, revealing GCC’s masterful work. Reverseception.
;
;                                                   --Mike Saarna
;------------------------------------------------------------------------------

;------------------------------------------------------------------------------
; NOTABLE GAME DESIGN FEATURES:
;
; 1. Game Object System:
;    The game manages all on-screen entities (players, enemies, eggs, 
;    pterodactyls) through a powerful "Game Object" system. Instead of unique
;    code for each entity, properties like position, velocity, and state are
;    stored in parallel arrays in RAM. A single index represents a specific 
;    object. The game logic iterates through these objects, applying common 
;    routines for physics, rendering, and AI. 
;
; 2. Event Scheduler (ES):
;    The ESRoutineScheduler system manages timed and sequential game events 
;    without introducing purpose-specific code in the main game loop. It uses 
;    a list of timers and function pointers. When a timer expires, its 
;    corresponding routine is executed. This allows for complex, multi-frame 
;    sequences like the dissolving platforms, lava rising, and timed text 
;    messages to occur gracefully in the background while the main game action
;    continues.
;
; 3. Sprites for the upcoming frame are added to render lists, in a format
;    for eventual quick dumping to the DLs. This is a sort of pseudo double-
;    buffer functionality.
;
; 4. 16-bit Sub-Pixel Physics:
;    To achieve smooth, arcade-accurate movement, the game uses 16-bit values
;    for object coordinates and velocities. This allows for fine-grained 
;    control over acceleration and movement.
;
;------------------------------------------------------------------------------

 processor 6502

;------------------------------------------------------------------------------
; TIA/MARIA/RIOT Hardware Registers 
;------------------------------------------------------------------------------
INPTCTRL = $01
INPT4    = $0C
INPT5    = $0D
AUDC0    = $15
AUDF0    = $17
AUDV0    = $19
AUDV1    = $1A
BACKGRND = $20
P0C1     = $21
P0C2     = $22
P0C3     = $23
WSYNC    = $24
P1C1     = $25
P1C2     = $26
P1C3     = $27
MSTAT    = $28
P2C1     = $29
P2C2     = $2A
P2C3     = $2B
DPPH     = $2C
P3C1     = $2D
P3C2     = $2E
P3C3     = $2F
DPPL     = $30
P4C1     = $31
P4C2     = $32
P4C3     = $33
CHARBASE = $34
P5C1     = $35
P5C2     = $36
P5C3     = $37
OFFSET   = $38
P6C1     = $39
P6C2     = $3A
P6C3     = $3B
CTRL     = $3C
P7C1     = $3D
P7C2     = $3E
P7C3     = $3F
SWCHA    = $0280
SWACNT   = $0281
SWCHB    = $0282
SWBCNT   = $0283

; ****************** VARIABLES

P0Score          = $40  ; to $42
P0ExtraLifeScore = $43  ; to $44  Score threshold for bonus life award
P1Score          = $45  ; to $47
P1ExtraLifeScore = $48  ; to $49  Score threshold for bonus life award

PlayerCount = $4A
DemoMode    = $4B

Difficulty           = $4C
TimerHi              = $4D
TimerLo              = $4E
IncrementingRandSeed = $4F

P0LastFire    = $50
P1LastFire    = $51
P0CurrentFire = $52
P1CurrentFire = $53

MenuJoyDebounceTimer    = $54
MenuSelectDebounceTimer = $55
MenuResetTimer          = $56
PrevPausedState         = $57
PrevResetState          = $58
PrevSelectState         = $59

TempPoint5ACurrentESRoutineLo  = $5A
TempPointLo5A                  = $5A
TempPoint5ACurrentESRoutineHi  = $5B
TempPointHi5B                  = $5B

TempPoint5CLo = $5C
TempPoint5DHi = $5D

; Note that I've defined aliases for temps that are reused for
; various routines, to help maintain clarity in local routines.

Temp5E                        = $5E
Temp5EGfxDataLo               = $5E
TempPterryCoordY              = $5E
TempSavedYForHunterBirdSpawn  = $5E
Temp5F                        = $5F
Temp5FGfxDataHi               = $5F
TempPterryCoordX              = $5F
Temp60                        = $60
Temp60ScoreAdd                = $60
Temp60PaletteAndWidth         = $60
TempPterryScoreValue          = $60
Temp60MinPhysicsXSpeedIndex   = $60

Temp61                      = $61
Temp61ScoreAdd              = $61
Temp61SpriteXCoord          = $61
Temp61MaxPhysicsXSpeedIndex = $61

Temp62                   = $62
Temp62ScoreAdd           = $62
Temp62MaxFallSpeed       = $62
Temp63                   = $63
Temp63SpriteYCoord       = $63
Temp63MaxRiseSpeedNeg    = $63
Temp64                   = $64
Temp64SpriteYOffset      = $64
Temp64WeightFactor       = $64
Temp65                   = $65
Temp66                   = $66

TempCollisionOverlap = $67  ; the magnitude of overlap from collision checks

Temp68 = $68
Temp69 = $69

Temp6ASFXIndex = $6A

SFXChannel0Index = $6B  ; Current sound effect playing on audio channel 0
SFXChannel1Index = $6C  ; Current sound effect playing on audio channel 1

BridgeBurningWidth      = $6D
ESTemp6E                = $6E
ESTemp6ELavaRiseCounter = $6E

; ??? = $6F  appears unused

LeftFlameAnimationIndex  = $70
RightFlameAnimationIndex = $71

TitleColorIndex           = $70
TitleColorChangeCountdown = $71

EggsPresentCount = $72
ColorCycleIndex  = $72  ; reused for title screen
EnemyBirdCount   = $73
ColorChangeTimer = $73  ; reused for title screen

TrollState = $74  ; 0=hiding in lave, $FF=jumping out, $02=holding a bird

TrollXCoord      = $75
TrollYCoord      = $76
TrollTargetIndex = $77
TrollGfxOffset   = $78

PterryCountdownLo     = $79
EasterEggJoyDownCount = $79
PterryCountdownHi     = $7A
EasterEggJoyLeftCount = $7A
PterryAliveCount      = $7B

EnemyAITargetDelayBase   = $7C
TrollPullingVelocity     = $7D
TrollMinimumGrabYCoord   = $7E
TrollMinimumTargetYCoord = $7F

BirdPhysicsIndexMinByType = $80  ; to $84
BirdPhysicsIndexMaxByType = $85  ; to $89

; Eggs collected so far, to a max of 3. Used as an index to determine value
; of egg pickup. This gets reset on death or new wave.
P0EggPointLevel = $8A
P1EggPointLevel = $8B

P0Lives = $8C
P1Lives = $8D

GamePacingTimer = $8E  ; ensures game logic runs every other frame
DemoTimer       = $8F

GameEndDelayTimer      = $90
EnemyIsSpawning        = $91
PlatformPresentBitmask = $92

Level    = $93
LevelBCD = $94

GameActiveFlag = $95  ; 0=normal, 1=game is ending

; Game logic writes sprite data to a render list, and then the render list is 
; used to update the DL in one shot.
RenderListCount = $96  ; The number of sprites in the render list
RenderListDirty = $97  ; A flag to indicate if the render list was
                       ; updated since the last DL update.

LevelWasSurvived   = $98  ; keep track to award survival points
PlayersWereATeam   = $99  ; keep track to award team points
PlayerWasGladiator = $9A  ; keep track to award gladiator points
PterryWave         = $9B

IsEggWave        = $9C
PlatformsInLevel = $9D

; The PlatformPresentBitmask from the previous wave...
OldPlatformPresentBitmask = $9E

MaxActiveEnemyIndex = $9F

SpawnPlatformDissolved0 = $A0
SpawnPlatformDissolved1 = $A1
SpawnPlatformDissolved2 = $A2
SpawnPlatformDissolved3 = $A3

MaxEnemyBirdCount = $A4

EggWaveEnemyType = $A5

P0DeathState = $A6
P1DeathState = $A7

; When a player bird dies, there's an explostion/particle cloud. 
; These are the Coordinates for the cloud.
PlayerExplosionXCoord = $A8  ; to $A9
PlayerExplosionYCoord = $AA  ; to $AB

; A collection of event scheduler (ES) routine indexes.
ESRoutineIndex = $AC  ; to $BD

; A collection of event scheduler (ES) routine timers.
ESRoutineTimer = $BE  ; to $CF 18 timers total


GameObjectState = $D0
  ; $D0 | P0 0=dead 3=born1 4=normal 5=hit 6=invincible 7=born2 8=troll has bird
  ; $D1 | P1 0=dead 3=born1 4=normal 5=hit 6=invincible 7=born2 8=troll has bird
  ; $D2 - $D9 | EnemyBird(8) 0=dead 3=born1 4=normal 5=hit 6=invincible 7=born2 8=troll has bird $14=bird seeking rider
  ; $DA - $DC | Pterry(3) 0=dead A=normal B=charging C=leaving
  ; $DD - $E8 | Egg(12) 0=none 1=egg 2=hatching 3=rider+shell 4=rider
PterryState1 = $DA
PterryState2 = $DB
PterryState3 = $DC
EggState     = $DD

GameObjectAIMode = $E9
  ; $E9 : P0
  ; $EA : P1
  ; $EB - $F2 : EnemyBird(8)

GameObjectAlive = $F3
  ; $F3 : P0
  ; $F4 : P1
  ; $F5 - $FC : EnemyBird(8)

NMIModeFlag = $FD
  ; $00: Normal game NMI (in-game, title screen animation) - sound, input, render list processing.
  ; $01: 320A portion of split screen NMI mode. Automatically changes NMIModeFlag to $FF. 
  ; $FF: 160A portion of split screen NMI mode. Automatically changes NMIModeFlag to $01.

  ; When a player bird enters a platform a bit low, it initiates a bounce/skip
  ; across the platform instead of walking. 0=not skipping, 1=skipping
GameObjectPlatformSkipState = $14A
  ; $14A : P0
  ; $14B : P1
  ; $14C - $153 : EnemyBird(8)

GameObjectXCoordHi = $154
  ; $154 : P0
  ; $155 : P1
  ; $156 - $15D : EnemyBird(8)
  ; $15E - $160 : Pterry(3)
  ; $161 - $16C : Egg(12)
EggX = $161

GameObjectYCoordHi = $16D
  ; $16D : P0
  ; $16E : P1
  ; $16F - $176 : EnemyBird(8)
  ; $177 - $179 : Pterry(3)
  ; $17A - $185 : Egg(12)
EggY = $17A

  ; The palette+width used to eventually render the bird.
BirdObjectPaletteAndWidth = $186
  ; $186 : P0
  ; $187 : P1
  ; $188 - $18F : EnemyBird(8)

PaletteInRam = $0190           ; to $01A7
P0C1_Ram     = $0190
P0C2_Ram     = $0191
P0C3_Ram     = $0192
P1C1_Ram     = $0193
P1C2_Ram     = $0194
P1C3_Ram     = $0195
P2C1_Ram     = $0196
P2C2_Ram     = $0197
P2C3_Ram     = $0198
P3C1_Ram     = $0199
P3C2_Ram     = $019A
P3C3_Ram     = $019B
P4C1_Ram     = $019C
P4C2_Ram     = $019D
P4C3_Ram     = $019E
P5C1_Ram     = $019F
P5C2_Ram     = $01A0
P5C3_Ram     = $01A1
P6C1_Ram     = $01A2
P6C2_Ram     = $01A3
P6C3_Ram     = $01A4
P7C1_Ram     = $01A5
P7C2_Ram     = $01A6
P7C3_Ram     = $01A7

Platform56GfxLo        = $1A13
Platform56PaletteWidth = $1A14
Platform4GfxLo         = $1A6D
Platform4PaletteWidth  = $1A6E
Platform0GfxLo         = $1D43
Platform0PaletteWidth  = $1D44

Platform4XCoordinate   = $1A70  ; DL object's X coordinate
SpawnPoint4XCoordinate = $1A74  ; DL object's X coordinate

Platform5XCoordinate = $1A16   ; DL object's X coordinate
Platform6XCoordinate = $1A1A   ; DL object's X coordinate

Platform0XCoordinate = $1D46   ; DL object's X coordinate

EasterEggXCoordinate1 = $1D96
EasterEggXCoordinate2 = $1DF0
EasterEggXCoordinate3 = $1E4B

; DL X-coordinate bytes for menu texts.
MenuTwoPlayerTextXCoord  = $1EA5
MenuDifficultyTextXCoord = $1EFF
MenuOnePlayerTextXCoord  = $1F59

BridgeRightGfxByte1  = $1F4F
BridgeLeftGfxByte1   = $1F53
BridgeRightGfxByte2  = $1F57
BridgeLeftGfxByte2   = $1F5B
BridgeRightGfxByte3  = $1FA5
BridgeLeftGfxByte3   = $1FA9

; I think these are probably just graphics data hi bytes of lava sprites 
; in a larger DL for the bottom level, and the lava level for each gets 
; raised by changing them to scroll them out a DMA hole.

LeftLavaLevelWave3  = $1FB0
RightLavaLevelWave3 = $1FAC

LeftLavaLevelWave2  = $1FC3
RightLavaLevelWave2 = $1FC7

LeftLavaLevelWave1  = $1FD9
RightLavaLevelWave1 = $1FDD

  ; Used to derive bird walk cycle frame
GameObjectFootstepAnimIndex = $1FF2
  ; $1FF2 : P0
  ; $1FF3 : P1
  ; $1FF4 - $1FFB : EnemyBird(8)

GameObjectFootstepAnimTimer = $1FFC
  ; $1FFC : P0
  ; $1FFD : P1
  ; $1FFE - $2005 : EnemyBird(8)

UnusedBirdObjectArray1 = $2006
  ; A bird object sized array that gets cleared with others,
  ; but it's never actually used after that.
  ; $2006 : P0
  ; $2007 : P1
  ; $2008 - $200F : EnemyBird(8)

  ; A bird timer delaying AI target re-evaluation
GameObjectAITargetDelayTimer = $2010
  ; $2010 : P0
  ; $2011 : P1
  ; $2012 - $2019 : EnemyBird(8)

GameObjectSpawnPlatformIndex = $201A
  ; $201A : P0
  ; $201B : P1
  ; $201C - $2023 : EnemyBird(8)

BirdInvulnerable = $2024  ; to 2025
  ; When the bird spawns on a platform, this flag cycles:
  ; $FF, $02, $02, $01, $01, $00, $00
  ; $2024 : P0
  ; $2025 : P1
  ; $2026 - $202D : EnemyBird(8)

  ; When the player tries to reverse direction when running on a platform,
  ; it initiates a skid until the bird comes to a stop. This location holds
  ; the skid direction/flag, or zero when not-skidding.
GameObjectSkidDirection = $202E
  ; $202E : P0
  ; $202F : P1
  ; $2030 - $2037 : EnemyBird(8)
P0SkidDirection = $202E
P1SkidDirection = $202F

MessageLine0Enable = $2038

; $2040 to $20FF: 7800 console ram block 0 shadow

MessageLine0Timer = $2108
MessageLine1Timer = $2109
MessageLine2Timer = $210A
MessageLine3Timer = $210B

; These unused player state bytes are cleared but never used.
UnusedPlayerStateArray = $210C ; to $210D

TempSpawnUnsafeCounter = $2110 ; to $2113

P0JoyDelay = $2114
P1JoyDelay = $2115

P0FireDebounce = $2116
P1FireDebounce = $2117

; $2140 to $21FF: 7800 console ram block 1 shadow

; Character strings for the animated titlescreen border
TitleBorderStr0 = $2200
TitleBorderStr1 = $2201
TitleBorderStr2 = $2202
TitleBorderStr3 = $2203
TitleBorderStr4 = $2204       ; to $2223
TitleBorderStr5 = $2205       ; to $2223

ObjectWiggleAnimation = $220E  ; only used for egg wiggle animation
                               ; $220E : P0
                               ; $220F : P1
                               ; $2210 - $2217 : EnemyBird(8)
                               ; $2218 - $221A : Pterry(3)
                               ; $221B - $2226 : Egg(12)

GameObjectXCoordLo = $2227  ; sub-pixel X
                            ; $2227 : P0
                            ; $2228 : P1
                            ; $2229 - $2230 : EnemyBird(8)
                            ; $2231 - $2233 : Pterry(3)
                            ; $2234 - $223F : Egg(12)

GameObjectYCoordLo = $2240  ; sub-pixel Y
                            ; $2240 : P0
                            ; $2241 : P1
                            ; $2242 - $2249 : EnemyBird(8)
                            ; $224A - $224C : Pterry(3)
                            ; $224D - $2258 : Egg(12)

TitleCopyrightCharBuffer  = $2244 ; to $225F
TitleScreenLogoCharBuffer = $2260 ; to $2347

ObjectBirdLevelAndType = $2259
  ; 0:red-bounder, 1:gray-hunter, 2:blue-shadowlord, 3:??, 4:pterry
  ; $2259 : P0
  ; $225A : P1
  ; $225B - $2262 : EnemyBird(8)
  ; $2263 - $2265 : Pterry(3)
  ; $2266 - $2271 : Egg(12)
EggLevelAndType = $2266

GameObjectInAir = $2272  ; ... i.e. not standing on a platform.
  ; $2272 : P0
  ; $2273 : P1
  ; $2274 - $227B : EnemyBird(8)
  ; $227C - $227E : Pterry(3)
  ; $227F - $228A : Egg(12)

GameObjectYVelocityLo = $228B
  ; $228B : P0
  ; $228C : P1
  ; $228D - $2294 : EnemyBird(8)
  ; $2295 - $2297 : Pterry(3)
  ; $2298 - $22A3 : Egg(12)

GameObjectYVelocityHi = $22A4
  ; $22A4 : P0
  ; $22A5 : P1
  ; $22A6 - $22AD : EnemyBird(8)
  ; $22AE - $22B0 : Pterry(3)
  ; $22B1 - $22BC : Egg(12)

GameObjectXVelocity = $22BD
  ; $22BD : P0
  ; $22BE : P1
  ; $22BF - $22C6 : EnemyBird(8)
  ; $22C7 - $22C9 : Pterry(3)
  ; $22CA - $22D5 : Egg(12)

GameObjectHunterIndex = $22D6
  ; When one game object tagets another, the index of the hunter is 
  ; stored here. E.g. the index of a riderless bird targetting a reborn rider
  ; There's a corresponding GameObjectPreyIndex that would store the index
  ; of the reborn rider.
  ; $22D6 : P0 
  ; $22D7 : P1
  ; $22D8 - $22DF : EnemyBird(8)
  ; $22E0 - $22E2 : Pterry(3)
  ; $22E3 - $22EE : Egg(12)

UnusedGameObjectArray2 = $22EF
  ; A game object sized array that gets cleared with others
  ; but it's never actually used after that.
  ; $22EF : P0
  ; $22F0 : P1
  ; $22F1 - $22F8 : EnemyBird(8)
  ; $22F9 - $22FB : Pterry(3)
  ; $22FC - $2307 : Egg(12)

GameObjectCountdownHi = $2308
  ; $2308 : P0
  ; $2309 : P1
  ; $230A - $2311 : EnemyBird(8)
  ; $2312 - $2314 : Pterry(3)
  ; $2315 - $2320 : Egg(12)

GameObjectCountdownLo = $2321
  ; $2321 : P0
  ; $2322 : P1
  ; $2323 - $232A : EnemyBird(8)
  ; $232B - $232D : Pterry(3)
  ; $232E - $2339 : Egg(12)

GameObjectDirection = $233A
  ; $233A : P0
  ; $233B : P1
  ; $233C - $2343 : EnemyBird(8)
  ; $2344 - $2346 : Pterry(3)
  ; $2347 - $2352 : Egg(12)

GameObjectAnimationFrame = $2360
  ; $2360 : P0
  ; $2361 : P1
  ; $2362 - $2369 : EnemyBird(8)
  ; $236A - $236C : Pterry(3)
  ; $236D - $2378 : Egg(12)

; ??? = $2379 ; unused?

GameObjectPreyIndex = $237A
  ; $237A : P0
  ; $237B : P1
  ; $237C - $2383 : EnemyBird(8)
  ; $2384 - $2385 : Pterry(3)

GameObjectTimerResetValue = $2387
  ; $2387 : P0
  ; $2388 : P1
  ; $2389 - $2390 : EnemyBird(8)
  ; $2391 - $2393 : Pterry(3)

GameObjectEventTimer1 = $2394
  ; $2394 : P0
  ; $2395 : P1
  ; $2396 - $239D : EnemyBird(8)
  ; $239E - $23A0 : Pterry(3)

GameObjectAnimationCounter = $23A1  ; Seems to only be used by pterry.
  ; A animation countdown that's used to derive the flap frame used.
  ; $23A1 : P0
  ; $23A2 : P1
  ; $23A3 - $23AA : EnemyBird(8)
  ; $23AB - $23AD : Pterry(3)

GameObjectAnimationDelay = $23AE  ; Also only used by pterry.
  ; Used to add delay between the pterry flap frames.
  ; $23AE : P0
  ; $23AF : P1
  ; $23B0 - $23B7 : EnemyBird(8)
  ; $23B8 - $23BA : Pterry(3)

GameObjectChargingTimer = $23BB
  ; Used for timing of pterry charging at the player, 
  ; and riderless birds sliding at riders.
  ; $23BB : P0
  ; $23BC : P1
  ; $23BD - $23C4 : EnemyBird(8)
  ; $23C5 - $23C7 : Pterry(3)

PterryDefeatedFlag = $23C8 ; to $23CA (Pterry objects only)
  ; $23C8 - $23C4 : Pterry(3)

; Note this is an alias for the same underlying memory as PterryDefeatedFlag.
EnemyBirdTypesForLevel = $23C8 ; to ????

PterryAgressionFlag = $23D2 ; to $23D4
; forces faster respawn on higher levels

; $23D5 - $23D9 don't seem to be used, other than getting cleared 
; with other game objects at the game start/end.

GameObjectTurnCheckTimerLo = $23DA  
; seems to get read when the dead bird flys off-screen
  ; $23DA : P0
  ; $23DB : P1
  ; $23DC - $23E3 : EnemyBird(8)

GameObjectTurnCheckTimerHi = $23E4
  ; $23E4 : P0
  ; $23E5 : P1
  ; $23E6 - $23ED : EnemyBird(8)

  ; Index into tables that load turn timer values.
GameObjectTurnTimerIndex = $23EE
  ; $23EE : P0
  ; $23EF : P1
  ; $23F0 - $23F7 : EnemyBird(8)

  ; A Hi timer controlling the frequency of AI decision-making for birds.
  ; For details see the comments below for it's Lo timer counterpart.
GameObjectAITimerHi = $23F8
  ; $23F8 : P0
  ; $23F9 : P1
  ; $23FA - $2401 : EnemyBird(8)

; A Lo timer controlling the frequency of AI decision-making for birds
; and pterodactyls. Birds use this lo timer and it's hi counterpart to,
; for example, randomly turn around. Pterrys use it, for example, to home
; in on prey.
GameObjectAITimerLo = $2402
  ; $2402 : P0
  ; $2403 : P1
  ; $2404 - $240B : EnemyBird(8)
  ; $240C - $240E : Pterry(3)

; Flags for platform dissolving sprite animation
PlatformDissolveAnimationFlag0 = $240F
PlatformDissolveAnimationFlag1 = $2410
PlatformDissolveAnimationFlag2 = $2411
PlatformDissolveAnimationFlag3 = $2412
PlatformDissolveAnimationFlag4 = $2413
PlatformDissolveAnimationFlag5 = $2414

PlatformDissolveAnimationXCoord0 = $2415
PlatformDissolveAnimationXCoord1 = $2416
PlatformDissolveAnimationXCoord2 = $2417
PlatformDissolveAnimationXCoord3 = $2418
PlatformDissolveAnimationXCoord4 = $2419
PlatformDissolveAnimationXCoord5 = $241A

PlatformDissolveAnimationYCoord0 = $241B
PlatformDissolveAnimationYCoord1 = $241C
PlatformDissolveAnimationYCoord2 = $241D
PlatformDissolveAnimationYCoord3 = $241E
PlatformDissolveAnimationYCoord4 = $241F
PlatformDissolveAnimationYCoord5 = $2420

RiderObjectPaletteAndWidth = $2421
  ; $2421 : P0
  ; $2422 : P1
  ; $2423 - $242A : EnemyBird(8)

; RAM structures containing DL addresses
DisplayListLo = $242B  ; to $2442
DisplayListHi = $2443  ; to $245A

; Game logic writes sprite data to a render list, and then the render list is 
; used to update the DLs in one shot. 
RenderListDLLo            = $245B  ; to $24AF
RenderListDLHi            = $24B0  ; to $2504
RenderListGfxDataLo       = $2505  ; to $2559
RenderListGfxDataHi       = $255A  ; to $25AE
RenderListPaletteAndWidth = $25AF  ; to $2603
RenderListXCoord          = $2604  ; to $2658

HudDisplayStringBuffer    = $2659  ; to ???

HudWaveNumberDigit1 = $267C
HudWaveNumberDigit2 = $267D

MenuLine1CharBuffer = $2685 ; "TWO PLAYER" text
MenuLine2CharBuffer = $2699 ; "ONE PLAYER" text

DifficultyLevelCharBuffer = $26AD

SurvivalWaveFlag  = $26C1
TeamWaveFlag      = $26C2
GladiatorWaveFlag = $26C3

ScorePopup_ActiveTimerBase = $26C4  ; Base for active timers for score popups (12 bytes)
ScorePopup_ZoneIndexBase   = $26DC  ; Base for zone index for score popups (12 bytes)
ScorePopup_CharDataLoBase  = $26E8  ; Base for character data (lo byte) for score popups (12 bytes)
ScorePopup_XCoordBase      = $26D0  ; Base for X coordinate for score popups (12 bytes)

FinalDLL_PointerStorage_Hi = $275E
FinalDLL_PointerStorage_Lo = $275F

CurrentPlayer = $27AE

DisplayEasterEggFlag = $27CE

; ****************** CONSTANTS

;------------------------------------------------------------------------------
; Difficulty Level Constants
; Used with the 'Difficulty' variable ($4C) and for text display.
;------------------------------------------------------------------------------
Difficulty_Beginner     = $00
Difficulty_Intermediate = $01
Difficulty_Advanced     = $02
Difficulty_Expert       = $03

PLAYER1_OBJECT_INDEX             = $01
PLAYER1_COLLISION_HEIGHT_SPECIAL = $0B  ; Specific height check value for Player 1

;------------------------------------------------------------------------------
; GameObjectState Constants
; Defines the various states a game object can be in.
;
; Player and Enemy Bird States ($D0 - $D9):
; Shared states for player-controlled birds and enemy AI birds.
;------------------------------------------------------------------------------
State_Dead         = $00  ; Object is inactive, dead, or not present.
State_Born1        = $03  ; First phase of spawning/birth animation.
State_Normal       = $04  ; Normal gameplay state (player controllable, AI active).
State_Hit          = $05  ; Object has been hit (e.g., by a lance, preparing to die/drop egg).
State_Invincible   = $06  ; Object is temporarily invincible (e.g., during spawn).
State_Born2        = $07  ; Second phase of spawning/birth animation.
State_TrollHasBird = $08  ; Object (bird) is currently held by the troll.

                              ; EnemyBird Specific State ($D2 - $D9):
State_BirdSeekingRider = $14  ; Enemy bird is actively seeking a rider (that hatched from an egg).

;------------------------------------------------------------------------------
; Pterodactyl (Pterry) States ($DA - $DC):
;------------------------------------------------------------------------------
                                  ; State_Dead is $00, shared with above.
PterryState_Normal         = $0A  ; Pterry is flying normally.
PterryState_Charging       = $0B  ; Pterry is actively charging/attacking a player.
PterryState_Leaving        = $0C  ; Pterry is leaving the screen (e.g., after being hit or timer expiry).
PterryState_DefeatedVisual = $0D  ; Pterry being visually defeated on screen
;------------------------------------------------------------------------------
; Egg States ($DD - $E8):
;------------------------------------------------------------------------------
EggState_None          = $00  ; Egg slot is empty, no egg present.
EggState_Egg           = $01  ; A laid egg, waiting to hatch.
EggState_Hatching      = $02  ; Egg is actively wiggling/hatching.
EggState_RiderOnShell  = $03  ; Rider has hatched and is standing on the broken egg shell.
EggState_StandingRider = $04  ; Rider is standing alone, shell is gone (will attract a hunter bird).
                              ; Note: This value is the same as State_Normal for birds.

;------------------------------------------------------------------------------
; Egg Physics Parameter Constants
; These define the specific physics characteristics for an airborne egg.
;------------------------------------------------------------------------------
EGG_WEIGHT_FACTOR               = $04  ; Gravity effect on egg (lower is lighter)
EGG_MAX_RISE_SPEED_NEG          = $FF  ; Max upward Y-velocity (effectively -1, very slow rise)
EGG_MAX_FALL_SPEED              = $01  ; Max downward Y-velocity (slow fall)
EGG_X_SPEED_LUT_MAX_CLAMP_INDEX = $02  ; Smallest numerical index for X-speed LUT (fastest left drift)
                                       ; Note: "Max" in Temp61MaxPhysicsXSpeedIndex refers to its role in clamping logic,
                                       ; not necessarily the numerically largest index value.
EGG_X_SPEED_LUT_MIN_CLAMP_INDEX = $08  ; Largest numerical index for X-speed LUT (fastest right drift)
                                       ; Note: "Min" in MinPhysicsXSpeedLUTIndex refers to its role in clamping logic.
                                       ; For eggs, this value is numerically larger than EGG_X_SPEED_LUT_MAX_CLAMP_INDEX,
                                       ; defining the allowed index range [$02..$08] for X-movement.

PTERRY_SPAWN_Y_COORD_MASK = %00000011  ; Mask for selecting Y coordinate from PterrySpawnYCoordLUT
PTERRY_SPAWN_XDIRVEL_MASK = %00000001  ; Mask for selecting from X/Dir/XVel LUTs
PTERRY_MIN_SPAWN_DIST_X = $30          ; Minimum X distance from player for Pterry spawn
PTERRY_MIN_SPAWN_DIST_Y = $20          ; Minimum Y distance from player for Pterry spawn

MinEnemyBirdObjectIndex = $02         ; Smallest index for an enemy bird object
MaxEnemyBirdSlots       = $0A         ; Number of enemy bird slots to check (index 02 to 09)

InitialTimerHiByteValue   = $00       ; Initial value for the high byte of the timer.
DifficultyLoopCounterBase = $03       ; Base value for calculating difficulty-dependent loop iterations.
                                      ; Max Difficulty is 3. Loop runs (Base - Difficulty + 1) times.
TimerLevelCalcBaseValue = $27         ; Baseline value for calculating level-dependent part of timer.
RandomTimerLoopAddend   = $40         ; Value added to timer in each difficulty-scaled loop iteration.

; AI Action Categories (values from BirdAIActionCategoryByProximityLUT lookup)
AIActionCat_CloseDefensive = $00
AIActionCat_OptimalAttack  = $01
AIActionCat_ModerateEngage = $02
AIActionCat_FarApproach    = $03

                                 ; Constants for Score Popup Display List Object
ScorePopup_DLModeByte EQU $60    ; Indirect Character Mode for DL
ScorePopup_DLCharHiByte EQU $27  ; High byte of character data address for HSC font

;------------------------------------------------------------------------------
; Sound Effect (SFX) Index Constants
; Used as arguments to ScheduleSFX and for identifying sounds.
; The actual sound data is pointed to by SFX_FreqAndControlDataPtrLoLUT/Hi tables.
;------------------------------------------------------------------------------
SFX_CoinDrop              = $00  ; Sound for coin drop (attract mode?)
SFX_FreeBird              = $01  ; Sound when a player earns an extra life.
SFX_PickupEgg             = $02  ; Sound when a player picks up an egg.
SFX_GameStart             = $03  ; Sound played at the beginning of a new game.
SFX_BirdPlatformBounce    = $04  ; Sound when a bird bounces off a platform edge.
SFX_BirdBirdBounce        = $05  ; Sound when two birds collide and bounce off each other.
SFX_PlayerFootstep        = $06  ; Sound of a player's bird walking on a platform.
SFX_PterryDefeated        = $07  ; Sound when a Pterodactyl is defeated.
SFX_PterryCharging        = $08  ; Sound when a Pterodactyl starts its charging attack.
SFX_PlayerBorn            = $09  ; Sound when a player's bird spawns/re-spawns.
SFX_Invulnerable          = $0A  ; Sound indicating player invulnerability (e.g., during spawn).
SFX_EggWigglingHatching   = $0B  ; Sound of an egg wiggling before hatching.
SFX_EnemyBirdBorn         = $0C  ; Sound when an enemy bird spawns.
SFX_TrollJumping          = $0D  ; Sound of the troll jumping out of the lava.
SFX_PlatformDissolve      = $0E  ; Sound when a platform starts to dissolve.
SFX_Skid                  = $0F  ; Sound of a bird skidding to a stop on a platform.
SFX_BirdExplosion         = $10  ; Sound when a bird (player or enemy) dies in an explosion.
SFX_CheepCheep            = $11  ; A "cheep cheep" sound, possibly unused or for a minor event.
SFX_PlayerBirdFlap        = $12  ; Sound of a player's bird flapping its wings.
SFX_ChannelAvailable      = $FF

HunterBirdXVel_Right        = $02  ; X-velocity for hunter bird moving right
HunterBirdXVel_Left         = $FD  ; X-velocity for hunter bird moving left (-3)
HunterBirdX_OffscreenLeft   = $1A  ; Spawn X-coordinate for hunter bird appearing from left
HunterBirdX_OffscreenRight  = $BE  ; Spawn X-coordinate for hunter bird appearing from right
HunterBirdYOffsetAboveRider = $0F  ; Vertical offset (15 pixels) for hunter bird above rider

X_VEL_TO_LUT_INDEX_OFFSET = $05   ; Offset to convert signed X-velocity to 0-based LUT index (e.g., index 5 = zero movement)

Direction_Right = $01
Direction_Left  = $FF

TOP_SCREEN_Y_BOUNDARY = $10         ; Y-coordinate of the top screen edge for bounce

SKID_VELOCITY_THRESHOLD_RIGHT = $03  ; Min X-velocity (e.g. +3) to initiate a skid when turning left
SKID_VELOCITY_THRESHOLD_LEFT  = $FE  ; Max X-velocity (e.g. -2) to initiate a skid when turning right
                                     ; (Max left speed is -3, which is $FD)
SKID_ANIMATION_FRAME = $04           ; Footstep animation index when skidding

                               ; NMIModeFlag Values
NMIMode_NormalGame      = $00  ; Normal in-game NMI processing
NMIMode_SplitScreen160A = $01  ; 160A portion of split screen NMI
NMIMode_SplitScreen320A = $FF  ; 320A portion of split screen NMI

                               ; Console Switch Masks (from SWCHB - Active Low)
SWCHB_Reset_Mask = %00000001   ; Bit 0 for Reset
SWCHB_Select_Mask = %00000010  ; Bit 1 for Select
SWCHB_Pause_Mask = %00001000   ; Bit 3 for Pause

                         ; Control Register Values
CTRL_DMA_ON_160AB = $40  ; single-width chars, 160A/B, border-off
CTRL_DMA_OFF      = $60
CTRL_DMA_ON_320AC = $4B  ; single-width chars, 320A/C, border-on

                                    ; Input Register Bitmasks
Input_FireButtonMask EQU %10000000  ; Bit 7 for fire button



; ****************** MACROS

; ASCII to HSC text encoding...
 mac WATKINS
 dc.b {1}-"A"
 endm


; ****************** ROM 

  ORG $8000
;------------------------------------------------------------------------------
; SetBirdRandomTurnTimers
; Sets the 16-bit turn timer for an AI bird (index in X) to a new random value
; based on its TurnTimerIndex. Also toggles the bird's direction.
;------------------------------------------------------------------------------
SetBirdRandomTurnTimers
  ADC #$01
  STA GameObjectDirection,X

  LDY GameObjectTurnTimerIndex,X
  JSR ReturnRandInA
  AND #$0F
  CLC
  ADC TurnCheckTimerValuesLoLUT,Y
  STA GameObjectTurnCheckTimerLo,X
  LDA TurnCheckTimerValuesHiLUT,Y
  STA GameObjectTurnCheckTimerHi,X
  RTS

;------------------------------------------------------------------------------
; RunEggObjectProcessing
; Main routine to update the state and behavior of an egg object (index in X).
; This is a state machine that handles an egg's lifecycle:
; - State 1 (Egg): A stationary egg on a platform, counting down to hatch.
; - State 2 (Hatching): The egg is wiggling, counting down to reveal the rider.
; - State 3 (RiderOnShell): A rider stands on the broken shell for a short time.
; - State 4 (StandingRider): The rider stands alone, waiting for a hunter bird.
; It also handles the physics for an egg when it is airborne.
;------------------------------------------------------------------------------
RunEggObjectProcessing
  LDA GameObjectState,X               ; Get the current state of the egg
  BEQ ExitEggProcessing_A             ; If state is 0 (EggState_None), skip processing

  LDA GameObjectInAir,X                       ; Is the egg currently airborne?
  BEQ ProcessEggOnPlatform                    ; If not (on a platform), handle platform logic.
  JSR EggIsAirborne                           ; If airborne, set up egg-specific physics parameters...
  JMP UpdateEggPhysicsAndPlatformInteraction  ; ...and jump to the physics/platform interaction handler.

ProcessEggOnPlatform
  LDA LevelBCD                       ; Get current level in BCD format
  CMP #$03                           ; Is it Level 3 or higher? (BCD comparison)
  BNE ProcessEgg_StandardBehavior    ; If not, eggs behave normally.
  JSR CheckIfEggSlippingOffPlatform  ; On Level 3+, platforms are slippery. Check if egg is near an edge.
  LDA Temp60                         ; Check result from the slip check.
  BNE ProcessEgg_StandardBehavior    ; If not slipping, proceed with standard behavior.

; Egg is on Level 3+ AND is slipping off the platform's edge.
  LDA #$01               ; Set InAir flag to true.
  STA GameObjectInAir,X  ; Make the egg airborne, causing it to fall.
                         ; Falls through to ExitEggProcessing_A (RTS).
ExitEggProcessing_A
  RTS

ProcessEgg_StandardBehavior
; This is the main state machine for an egg that is resting on a platform.
  LDA GameObjectState,X         ; Get current state of the egg.
  CMP #EggState_StandingRider   ; State 4: Rider is standing alone. No further action needed here.
  BEQ ExitEggProcessing_A       ; Its fate is now tied to a hunter bird.
  CMP #EggState_Hatching        ; State 2: Egg is actively hatching.
  BEQ ProcessHatchingEggTimer   ; Process its hatching animation timer.
  CMP #EggState_RiderOnShell    ; State 3: Rider is on the shell.
  BEQ ProcessRiderOnShellTimer  ; Process its timer for shedding the shell.

                               ; --- State 1: Egg is waiting to hatch ---
  DEC GameObjectCountdownLo,X  ; Decrement low byte of the 16-bit hatch timer.
  BEQ DecrementHatchTimerHi    ; If low byte wrapped, handle the high byte.
  RTS                          ; Timer still active, nothing more to do.

DecrementHatchTimerHi
  DEC GameObjectCountdownHi,X  ; Decrement high byte of hatch timer.
  BEQ AttemptToHatchEgg        ; If timer expired, try to hatch.
  BMI AttemptToHatchEgg        ; Also handle underflow just in case.
ExitEggProcessing_B
  RTS

AttemptToHatchEgg
  LDA Difficulty           ; Get current game difficulty.
  BEQ ExitEggProcessing_B  ; On Beginner difficulty, eggs never hatch.
  LDA EnemyBirdCount       ; Get current number of active enemy birds.
  CMP MaxEnemyBirdCount    ; Compare with the maximum allowed on screen for this level.
  BCC ProceedWithHatching  ; If we are below the max, the egg can hatch.

; Max enemy birds reached, cannot hatch now. Reset a short timer to try again soon.
  LDA #$0A  ; Reset countdown low byte.
  STA GameObjectCountdownLo,X
  LDA #$00  ; Reset countdown high byte.
  STA GameObjectCountdownHi,X
  RTS

ProceedWithHatching
  INC EnemyBirdCount            ; One more enemy bird will be active (the rider).
  LDA #EggState_Hatching        ; Set egg state to 'hatching' (state 2).
  STA GameObjectState,X
  LDA #$05                      ; Set initial low byte for hatching animation timer.
  STA GameObjectCountdownLo,X
  LDA #$0A                      ; Set initial high byte for hatching animation timer.
  STA GameObjectCountdownHi,X
  LDA #SFX_EggWigglingHatching  ; Schedule the egg wiggling/hatching sound effect.
  JSR ScheduleSFX
  RTS

ProcessHatchingEggTimer         ; --- State 2: Egg is actively hatching ---
  DEC GameObjectCountdownLo,X   ; Decrement low byte of hatching animation timer.
  BEQ ContinueHatchingEggTimer  ; If low byte wrapped, process high byte and animation.
  RTS

ContinueHatchingEggTimer
  LDA GameObjectCountdownHi,X
  AND #$07                      ; Check lower 3 bits of high byte timer.
  BEQ UpdateEggWiggleAnimation  ; If zero, it's time to update the wiggle animation frame.
                                ; Otherwise, play the sound effect (every time Lo wraps, unless Hi&7 is 0).
  LDA #SFX_EggWigglingHatching  ; Schedule egg wiggling/hatching sound effect.
  JSR ScheduleSFX

UpdateEggWiggleAnimation
  DEC GameObjectCountdownHi,X   ; Decrement high byte of hatching animation timer.
  BEQ TransitionToRiderOnShell  ; If timer fully expired, transition to the next state.
  LDA ObjectWiggleAnimation,X   ; Get current wiggle frame.
  EOR #$01                      ; Toggle wiggle frame (0 <-> 1).
  STA ObjectWiggleAnimation,X   ; Store new wiggle frame.
  LDA #$0A                      ; Reset low byte of hatching animation timer for the next wiggle cycle.
  STA GameObjectCountdownLo,X
  RTS

TransitionToRiderOnShell
  LDA #EggState_RiderOnShell   ; Egg is now 'rider standing on shell' (state 3).
  STA GameObjectState,X
  LDA #$0F                     ; Set timer for how long rider stays on the shell.
  STA GameObjectCountdownLo,X  ; (High byte is implicitly 0 from the previous countdown).
  RTS

ProcessRiderOnShellTimer       ; --- State 3: Rider standing on broken shell ---
  DEC GameObjectCountdownLo,X  ; Decrement timer for rider on shell.
  BEQ EggIsRiderNoShell        ; If timer expires, transition to rider without shell.
  RTS

;------------------------------------------------------------------------------
; EggIsRiderNoShell
; Called when an egg (object X) has progressed to the state of being just a 
; rider (state 4), having shed its shell. This routine finds an available enemy
; bird slot and spawns a new "hunter" bird (object Y) to fly in and retrieve
; this rider. X contains the index of the egg object (now the rider).
;------------------------------------------------------------------------------
EggIsRiderNoShell
  LDA #EggState_StandingRider    ; Set state to: rider standing without shell.
  STA GameObjectState,X          ; Update the former egg object's state.

                                 ; Find an available (dead) enemy bird slot to spawn the hunter bird.
  LDY #MinEnemyBirdObjectIndex   ; Start from the first enemy bird slot (index 2).
FindDeadEnemyBirdSlotLoop
  LDA.w GameObjectState,Y        ; Get state of potential enemy bird Y.
  BEQ FoundDeadEnemyBirdSlot     ; If state is 0 (dead), the slot is available.
  INY                            ; Try next bird slot.
  CPY #MaxEnemyBirdSlots         ; Have we checked all enemy bird slots (up to 9)?
  BNE FindDeadEnemyBirdSlotLoop  ; If not, loop.

FoundDeadEnemyBirdSlot
                                  ; Initialize the new hunter bird (object Y) based on the rider (object X).
  LDA ObjectBirdLevelAndType,X    ; Get rider's original bird type/level (e.g., Bounder, Hunter, Shadow Lord).
  STA ObjectBirdLevelAndType,Y    ; Set the new hunter bird's type/level to match.

  TYA                             ; Bird Y (the hunter) is now hunting Rider X (the prey).
  STA GameObjectHunterIndex,X     ; Link: Rider X is being hunted by Bird Y.
  TXA                             ;
  STA GameObjectPreyIndex,Y       ; Link: Bird Y is hunting Rider X.

  STY TempSavedYForHunterBirdSpawn         ; Save Y (hunter bird index) as it will be clobbered by the JSR.
  JSR DetermineInitialHunterBirdDirection  ; Determine which side of the screen the bird should spawn from.
  LDY TempSavedYForHunterBirdSpawn         ; Restore Y.

  STA GameObjectDirection,Y                ; Set hunter bird's initial direction.
  LDA GameObjectDirection,Y                ; Load direction again to check sign.
  BMI SpawnHunterBirdFacingLeft

SpawnHunterBirdFacingRight
  LDA #HunterBirdXVel_Right       ; Set X velocity for right-facing bird.
  STA GameObjectXVelocity,Y
  LDA #HunterBirdX_OffscreenLeft  ; Spawn off-screen to the left.
  JMP SetHunterBirdInitialXPosition

SpawnHunterBirdFacingLeft
  LDA #HunterBirdXVel_Left         ; Set X velocity for left-facing bird.
  STA GameObjectXVelocity,Y
  LDA #HunterBirdX_OffscreenRight  ; Spawn off-screen to the right.

SetHunterBirdInitialXPosition
  STA GameObjectXCoordHi,Y  ; Set hunter bird's initial X coordinate (high byte).

; Set hunter bird's initial Y coordinate to be slightly above the rider.
  LDA GameObjectYCoordHi,X          ; Get rider's Y coordinate.
  SEC
  SBC #HunterBirdYOffsetAboveRider  ; Spawn 15 pixels above the rider.
  STA GameObjectYCoordHi,Y          ; Set hunter bird's Y coordinate.

; Initialize various properties for the newly spawned hunter bird.
  LDA #$01                            ; True for boolean flags.
  STA GameObjectInAir,Y               ; Hunter bird starts in the air.
  STA.w GameObjectAIMode,Y            ; Set hunter bird's AI mode to seeking.
  LDA #$00                            ; Zero for timers and some physics components.
  STA GameObjectChargingTimer,Y       ; Clear charging timer.
  STA GameObjectFootstepAnimTimer,Y   ; Clear footstep animation timer.
  STA GameObjectFootstepAnimIndex,Y   ; Reset footstep animation frame.
  STA GameObjectYVelocityLo,Y         ; Clear Y velocity (low byte).
  STA GameObjectYVelocityHi,Y         ; Clear Y velocity (high byte).
  STA GameObjectPlatformSkipState,Y   ; Not skipping on a platform.
  STA GameObjectSkidDirection,Y       ; Not skidding.
  STA GameObjectAnimationFrame,Y      ; Reset animation frame.
  STA.w GameObjectAlive,Y             ; Mark as alive (though its state defines behavior).
  STA GameObjectAITargetDelayTimer,X  ; Reset AI target delay for the rider it was targeting.

  LDA #$01                           ; Default timer reset value.
  STA GameObjectTimerResetValue,Y    ; Set timer reset value for hunter bird.
  STA GameObjectEventTimer1,Y        ; Set event timer 1 for hunter bird.

  LDA #State_BirdSeekingRider        ; Set state to: bird is actively seeking its rider.
  STA GameObjectState,Y              ; Update hunter bird's state.
  RTS

;------------------------------------------------------------------------------
; DetermineInitialHunterBirdDirection
; Determines the initial direction for a newly spawned hunter bird. The hunter
; pursues the rider (object index in X). The routine checks which platform the
; rider is on to decide if the hunter should enter from the left or right.
; Returns:
; A = Direction_Right ($01) if rider is on a known platform.
; A = Direction_Left ($FF) if rider is not found on a known platform.
; Preserves: X / Modifies: Y, A
;------------------------------------------------------------------------------
DetermineInitialHunterBirdDirection
  LDY #$06                     ; Start with the last platform index (Platform 6).
  LDA GameObjectYCoordHi,X     ; Get rider's Y coordinate (high byte).
FindRiderPlatformLoop
  CMP PlatformTopCoordinate,Y  ; Is rider at the Y level of this platform's top?
  BNE RiderNotOnThisPlatform   ; If not, check the next platform.

; Rider is at the correct Y level, now check X bounds.
  LDA GameObjectXCoordHi,X           ; Get rider's X coordinate (high byte).
  CMP PlatformLeftsideCoordinate,Y   ; Is rider to the right of the platform's left edge?
  BCC RiderNotOnThisPlatform         ; If rider is left of the left edge, not on this platform.
  CMP PlatformRightsideCoordinate,Y  ; Is rider to the left of the platform's right edge?
  BCS RiderNotOnThisPlatform         ; If rider is right of the right edge, not on this platform.

; Rider is on this platform.
  LDA #Direction_Right  ; Return direction: RIGHT.
  RTS

RiderNotOnThisPlatform
  DEY                        ; Next platform index.
  BPL FindRiderPlatformLoop  ; If platform index is still valid, loop.

; Rider was not found on any of the checked platforms.
  LDA #Direction_Left  ; Return direction: LEFT.
  RTS

;------------------------------------------------------------------------------
; EggIsAirborne
; Sets up the physics parameters in Zero Page specifically for an airborne egg
; object. Eggs have unique, floaty physics compared to birds. After setting
; the parameters, it jumps to the common airborne physics update routine.
; X = index of the egg GameObject
;------------------------------------------------------------------------------
EggIsAirborne
; Set egg's gravity (WeightFactor). A lower value means less gravity.
  LDA #EGG_WEIGHT_FACTOR
  STA Temp64WeightFactor

; Set egg's maximum upward speed (a small negative value for a slow rise).
  LDA #EGG_MAX_RISE_SPEED_NEG
  STA Temp63MaxRiseSpeedNeg

; Set egg's maximum downward speed (a small positive value for a slow fall).
  LDA #EGG_MAX_FALL_SPEED
  STA Temp62MaxFallSpeed

; Set egg's X-axis speed Look-Up Table index range. This controls its
; horizontal drift. For eggs, this range is narrow, resulting in slow drift.
  LDA #EGG_X_SPEED_LUT_MAX_CLAMP_INDEX
  STA Temp61MaxPhysicsXSpeedIndex         ; Smallest X-speed LUT index egg can use.
  LDA #EGG_X_SPEED_LUT_MIN_CLAMP_INDEX
  STA Temp60MinPhysicsXSpeedIndex         ; Largest X-speed LUT index egg can use.

  JMP UpdateAirborneXPositionAndVelocity  ; Proceed to common X-axis physics update.

;------------------------------------------------------------------------------
; UpdateAirborneObjectPhysics
; Main physics routine for airborne birds and pterodactyls. It loads the
; appropriate physics parameters for the object type and then jumps to the
; common X and Y position update logic.
; Input: X = GameObject index
;------------------------------------------------------------------------------
UpdateAirborneObjectPhysics
  LDA GameObjectState,X
  CMP #$08             ; check if state=troll holding the bird
  BNE ObjectIsAirborne
  JMP TrollHoldingTheBird
ObjectIsAirborne
  LDY ObjectBirdLevelAndType,X ; Get bird type for index with physics LUTs
  LDA.w BirdPhysicsIndexMinByType,Y
  STA Temp60
  LDA.w BirdPhysicsIndexMaxByType,Y
  STA Temp61
  LDA BirdWeightByTypeLUT,Y
  STA Temp64
  LDA BirdMaxDownVelocityByTypeLUT,Y
  STA Temp62
  LDA BirdMaxUpVelocityByTypeLUT,Y
  STA Temp63

;------------------------------------------------------------------------------
; UpdateAirborneXPositionAndVelocity
; This is the core 16-bit physics update routine for all airborne objects.
; It's jumped to after physics parameters have been loaded into ZP temps.
; It handles:
; 1. X-velocity to LUT index conversion and clamping.
; 2. 16-bit X-position update from LUT.
; 3. Horizontal screen wrapping.
; 4. 16-bit Y-velocity update (applying gravity).
; 5. 16-bit Y-position update from velocity.
; Input: X = GameObject index. ZP temps $60-$64 must be pre-loaded.
;------------------------------------------------------------------------------
UpdateAirborneXPositionAndVelocity
  LDA GameObjectXVelocity,X                 ; Load current X velocity component
  CLC
  ADC #X_VEL_TO_LUT_INDEX_OFFSET            ; Convert signed X velocity to an unsigned LUT index base.
                                            ; E.g., if XVel is -5 to +5, and OFFSET is 5, result is 0 to 10.
                                            ; This resulting index (in Y) is then adjusted by specific min/max allowed indices.
  TAY                                       ; Y = initial candidate X speed LUT index

  BMI XSpeedIndexIsNegative_SetToMaxAllowed       ; If Y < 0 (unlikely for typical Xvel range), use MaxAllowedIndex.
                                                  ; else (Y >= 0)
XSpeedIndex_CheckAgainstMaxAllowed
  CPY Temp61MaxPhysicsXSpeedIndex                 ; Compare Y with Temp61MaxPhysicsXSpeedIndex
  BCS XSpeedIndex_AtOrExceedsMax_CheckMinAllowed  ; If Y >= Temp61MaxPhysicsXSpeedIndex, Y is used as is for min check.
                                                  ; else (Y < Temp61MaxPhysicsXSpeedIndex)
XSpeedIndexIsNegative_SetToMaxAllowed             ; also reached if Y < MaxAllowedIndex
  LDY Temp61MaxPhysicsXSpeedIndex                 ; Y = Temp61MaxPhysicsXSpeedIndex. If Y was < Max, it's forced to Max.

XSpeedIndex_AtOrExceedsMax_CheckMinAllowed
  CPY Temp60MinPhysicsXSpeedIndex ; Compare Y (now possibly MaxAllowedIndex) with Temp60MinPhysicsXSpeedIndex
  BEQ XSpeedIndex_IsFinalized     ; If Y == MinAllowedIndex, the index is finalized.
  BCC XSpeedIndex_IsFinalized     ; If Y < MinAllowedIndex, the index is also finalized.
                                  ; (This implies that values < MinAllowedIndex are treated as MinAllowedIndex by table structure or subsequent logic)
                                  ; else (Y > MinAllowedIndex)
  LDY Temp60MinPhysicsXSpeedIndex ; Y = Temp60MinPhysicsXSpeedIndex. If Y was > Min, it's forced to Min.

XSpeedIndex_IsFinalized
  TYA                             ; A = final Y (the determined LUT index)
  SEC
  SBC #X_VEL_TO_LUT_INDEX_OFFSET  ; Convert the LUT index back to a signed X velocity value
  STA GameObjectXVelocity,X       ; Store the (potentially modified by LUT index logic) X velocity

UpdateXPositionFromLUT
  CLC
  LDA GameObjectXCoordLo,X            ; Load current X sub-pixel coordinate
  ADC XPositionIncrementTableLoLUT,Y  ; Add X sub-pixel increment from LUT using final index Y
  STA GameObjectXCoordLo,X
  LDA GameObjectXCoordHi,X            ; Load current X pixel coordinate
  ADC XPositionIncrementTableHiLUT,Y  ; Add X pixel increment from LUT (with carry from Lo)
  STA GameObjectXCoordHi,X

HandleXScreenWrap
  CMP ObjectScreenWrapXRightEdgeLUT,X  ; Compare X with right edge for wrapping
  BCC CheckLeftEdgeWrap                ; If X < RightEdge, no right-to-left wrap.
  BEQ CheckLeftEdgeWrap                ; If X == RightEdge, also no right-to-left wrap.
                                       ; else (X > RightEdge) -> wrap from right to left
  LDA ObjectScreenWrapXLeftEdgeLUT,X   ; Load left edge X coordinate
  JMP StoreWrappedXAndCheckObjectRemoval

CheckLeftEdgeWrap
  CMP ObjectScreenWrapXLeftEdgeLUT,X   ; Compare X with left edge for wrapping
  BCS XWrapHandled_ProceedToYPhysics   ; If X >= LeftEdge, no left-to-right wrap.
                                       ; else (X < LeftEdge) -> wrap from left to right
  LDA ObjectScreenWrapXRightEdgeLUT,X  ; Load right edge X coordinate

StoreWrappedXAndCheckObjectRemoval
  STA GameObjectXCoordHi,X  ; Store wrapped X pixel coordinate

  CPX #$0D                            ; Check if object index is Pterry or Egg (Indices $0A-$0C for Pterry, $0D-$18 for Egg)
                                      ; Player/Enemy birds are $00-$09.
  BCS XWrapHandled_ProceedToYPhysics  ; If Pterry or Egg, skip removal check.
                                      ; Object is a Player or Enemy Bird that wrapped
  LDA GameObjectState,X
  CMP #State_Hit                      ; is state=bird hit? ($05)
  BEQ KillBird                        ; If hit bird wrapped, remove it.
  CMP #PterryState_Leaving            ; is state=pterry leaving? ($0C) - This check is for birds, but PterryState is used.
                                      ; This implies a bird object could be in a "leaving" state, perhaps an unridden enemy bird.
  BEQ RemovePterry                    ; If "leaving" bird wrapped, remove it.

XWrapHandled_ProceedToYPhysics
  LDA ObjectBirdLevelAndType,X         ; Get object's type (0-2 Enemy, 3 Player, 4 Pterry)
  CMP #$04                             ; Is it Pterry? (Type 4)
  BEQ ApplyGravityAndYVelocity         ; If Pterry, always apply Y-velocity updates.
                                       ; Not Pterry (Player or Enemy Bird)
  LDA GameObjectInAir,X                ; Check if the bird is airborne (not on a platform)
  BEQ UpdateYPositionFromVelocityOnly  ; If on a platform (InAir == 0), skip Y-velocity changes due to gravity.
                                       ; Only update Y position based on its current (likely zeroed on landing) Y velocity.
ApplyGravityAndYVelocity
; Apply Y-velocity changes (gravity and clamping)
  LDA GameObjectYVelocityLo,X
  CLC
  ADC Temp64WeightFactor
  STA GameObjectYVelocityLo,X
  LDA #$00                             ; Prepare to add carry to Hi byte
  ADC GameObjectYVelocityHi,X          ; A = Y_Vel_Hi + Carry
  BMI YVelocityIsUpward_CheckMaxRise   ; If Y-velocity is negative (moving up).

; Y-velocity is positive (moving downward) or zero
  CMP Temp62MaxFallSpeed
  BEQ StoreYVelocityHi   ; If Y_Vel_Hi == Temp62MaxFallSpeed, store it.
  BCC StoreYVelocityHi   ; If Y_Vel_Hi < Temp62MaxFallSpeed, store it (falling slower than max is fine).
                         ; Else (Y_Vel_Hi > Temp62MaxFallSpeed - falling too fast)
  LDA #$00               ; Reset Lo byte of Y-velocity as Hi is being clamped
  STA GameObjectYVelocityLo,X
  LDA Temp62MaxFallSpeed ; Load Temp62MaxFallSpeed to clamp
  JMP StoreYVelocityHi

YVelocityIsUpward_CheckMaxRise
  CMP Temp63MaxRiseSpeedNeg
                            ; e.g. Y_Vel_Hi=$FE(-2), Temp63MaxRiseSpeedNeg=$FC(-4). $FE >= $FC (less negative), so BCS is taken.
  BCS StoreYVelocityHi      ; If Y_Vel_Hi >= Temp63MaxRiseSpeedNeg (rising slower or at max rise), store it.
                            ; Else (Y_Vel_Hi < Temp63MaxRiseSpeedNeg - rising too fast, i.e., more negative)
  LDA #$00                  ; Reset Lo byte of Y-velocity
  STA GameObjectYVelocityLo,X
  LDA Temp63MaxRiseSpeedNeg ; Load Temp63MaxRiseSpeedNeg to clamp

StoreYVelocityHi
  STA GameObjectYVelocityHi,X  ; Store the (clamped) Hi byte of Y-velocity

UpdateYPositionFromVelocityOnly
; Update Y-coordinates from Y-velocity
  LDA GameObjectYVelocityLo,X
  CLC
  ADC GameObjectYCoordLo,X            ; Add Y_Vel_Lo to Y_Coord_Lo
  STA GameObjectYCoordLo,X
  LDA GameObjectYCoordHi,X
  ADC GameObjectYVelocityHi,X         ; Add Y_Vel_Hi to Y_Coord_Hi (with carry)
  STA GameObjectYCoordHi,X
  JMP HandleObjectTopScreenCollision  ; Jump to process Y boundary conditions (top of screen bounce, troll grab)

RemovePterry
  LDA #$00               ; Pterry is dead and gone
  STA GameObjectState,X
  LDA PterryAliveCount
  BEQ SkipPterryDecrement
  DEC PterryAliveCount
SkipPterryDecrement
  JSR UpdatePterryRespawnTimer
  LSR PterryCountdownHi  ; divide pterry 
  ROR PterryCountdownLo  ; counter by two
  RTS

KillBird
  LDA #$00
  STA GameObjectState,X
  STA GameObjectAlive,X
  CPX #$02  ; players are X=0,1
  BCC KillPlayerBird
  DEC EnemyBirdCount
  BNE MoreEnemyBirdsLeft
  LDA EggsPresentCount
  BNE MoreEnemyBirdsLeft
  JMP SetupNextLevel
MoreEnemyBirdsLeft
  RTS

KillPlayerBird
  LDA P0Lives,X
  BPL SetupNewPlayerBird
  LDA #$00
  STA GameObjectState,X
  RTS

SetupNewPlayerBird
  JMP SpawnNewBird


;------------------------------------------------------------------------------
; HandleObjectTopScreenCollision
; Checks if an object has moved above the top screen boundary. If so, it reverses
; and dampens its Y velocity for a bounce effect and clamps its Y position.
; Input: X = GameObject index
;------------------------------------------------------------------------------
HandleObjectTopScreenCollision

  LDA GameObjectYCoordHi,X                ; Get object's current Y position (high byte)
  CMP #TOP_SCREEN_Y_BOUNDARY              ; Compare with the top screen boundary
  BCS ProcessTrollGrab_Or_FurtherYChecks  ; If Y >= TOP_BOUNDARY (i.e., below or at safe margin),
                                          ; object is not at/above top, skip bounce logic.
                                          ; Branch to ProcessTrollGrab_Or_FurtherYChecks

                                  ; Object is at or above the top screen boundary (Y < TOP_SCREEN_Y_BOUNDARY)
  LDA GameObjectYVelocityHi,X     ; Get Y velocity (high byte)
  BPL ClampYAtTopBoundaryAndExit  ; If velocity is positive or zero (moving down/still),
                                  ; no upward bounce needed, just clamp position.

            ; Object is moving upwards into the top boundary (Y velocity is negative)
            ; Reverse and dampen Y velocity for bounce effect.
  EOR #$FF  ; Negate Y-velocity (High Byte), part 1
  CLC
  ADC #$01  ; Negate Y-velocity (High Byte), part 2 (Two's complement)
  STA GameObjectYVelocityHi,X

  LDA GameObjectYVelocityLo,X  ; Get Y velocity (low byte)
  EOR #$FF                     ; Negate Y-velocity (Low Byte), part 1
  CLC
  ADC #$01                     ; Negate Y-velocity (Low Byte), part 2
  STA GameObjectYVelocityLo,X

                               ; Dampen the bounce velocity: Divide Y-velocity by 4
  CLC
  ROR GameObjectYVelocityHi,X  ; Shift combined 16-bit velocity right (effectively VelY / 2)
  ROR GameObjectYVelocityLo,X
  CLC
  ROR GameObjectYVelocityHi,X  ; Shift again (effectively VelY / 4 total)
  ROR GameObjectYVelocityLo,X

ClampYAtTopBoundaryAndExit
  LDA #TOP_SCREEN_Y_BOUNDARY   ; Load the top boundary Y coordinate
  STA GameObjectYCoordHi,X     ; Clamp object's Y position to the top boundary
  RTS                          ; Return from NMI or subroutine

ProcessTrollGrab_Or_FurtherYChecks
  LDA GameObjectState,X
  CMP #$08
  BEQ TrollHoldingTheBird
  LDA GameObjectYCoordHi,X
  CMP TrollGrabHeight,X
  BCS TrollJustGrabbedTheBird
  RTS

TrollJustGrabbedTheBird
  CPX #$0D  ; Eggs can't be grabbed (but Pterry can?!?)
  BCS TrollKilledEnemyBird
  LDA #$08
  STA GameObjectState,X
  RTS

TrollHoldingTheBird
  LDA GameObjectYCoordHi,X
  CMP #$BB                  ; See if the bird pulled down far enough to die
  BCS TrollKilledThBird
  INC GameObjectYCoordHi,X  ; Pull the bird down some more
  RTS

TrollKilledThBird
  CPX #$02
  BCS GotoKillBird  ; branch if non-player enemy bird
  LDA #$00
  STA GameObjectState,X
  JMP StartPlayerDeath
GotoKillBird
  JMP KillBird

TrollKilledEnemyBird
; a shortened version of the KillBird routine
  LDA #$00
  STA GameObjectState,X
  DEC EggsPresentCount
  BNE ExitTrollKillRoutine
  LDA EnemyBirdCount
  BNE ExitTrollKillRoutine
  JMP SetupNextLevel

ExitTrollKillRoutine
  RTS

;------------------------------------------------------------------------------
; ProcessBirdToBirdCollisionLoop
; Checks for collisions between the bird object in X and all other birds with a
; lower index. Handles simple horizontal bounces for non-player birds.
; Input: X = Index of the primary bird for collision check
;------------------------------------------------------------------------------
ProcessBirdToBirdCollisionLoop
  TXA                           ; A = ObjX_idx
  TAY                           ; Y_reg = ObjX_idx (used as the initial counter for ObjY loop)
  PHA                           ; Push ObjX_idx onto stack (will be retrieved by PLA in Bird2BirdIterateAndCheckObjY)
  JMP Bird2BirdIterateAndCheckObjY  ; Jump to the main loop logic for iterating through ObjY

; Start of the logic for a specific ObjX vs ObjY pair
Bird2BirdFilterObjYAndDetectCollision
  LDA.w GameObjectState,Y           ; Get state of ObjY (current object in inner loop, index in Y_REG)
  BNE Bird2BirdCheckIfObjYBorn1     ; If not State_Dead ($00), check further
  JMP Bird2BirdIterateAndCheckObjY  ; ObjY is dead, skip to next ObjY

Bird2BirdCheckIfObjYBorn1
  CMP #State_Born1                    ; Is ObjY in State_Born1 ($03)?
  BNE Bird2BirdCheckIfObjYInvincible  ; If not, check next state
  JMP Bird2BirdIterateAndCheckObjY    ; ObjY is in Born1 state, skip to next ObjY

Bird2BirdCheckIfObjYInvincible
  CMP #State_Invincible             ; Is ObjY in State_Invincible ($06)?
  BNE Bird2BirdCheckIfObjYBorn2     ; If not, check next state
  JMP Bird2BirdIterateAndCheckObjY  ; ObjY is invincible, skip to next ObjY

Bird2BirdCheckIfObjYBorn2
  CMP #State_Born2                  ; Is ObjY in State_Born2 ($07)?
  BNE Bird2BirdCheckIfObjYAlive     ; If not, check if alive
  JMP Bird2BirdIterateAndCheckObjY  ; ObjY is in Born2 state, skip to next ObjY

Bird2BirdCheckIfObjYAlive
  LDA.w GameObjectAlive,Y                 ; Is ObjY alive?
  BNE Bird2BirdPerformCollisionDetection  ; If alive flag is non-zero, proceed to collision detection
  JMP Bird2BirdIterateAndCheckObjY        ; ObjY is not alive (e.g. riderless bird leaving), skip to next ObjY

Bird2BirdPerformCollisionDetection
  JSR GameObjCollisionDetection      ; Check collision between ObjX (in X_REG) and ObjY (in Y_REG)
                                     ; Carry will be CLEAR if collision, SET if no collision.
  BCS Bird2BirdIterateAndCheckObjY   ; If Carry SET (no collision), skip to next ObjY

; Collision detected between ObjX and ObjY (Carry is CLEAR)
  CPX #MinEnemyBirdObjectIndex  ; Is ObjX a player (index < 2)?
  BCS GameObject1NotAPlayer     ; If ObjX_idx >= 2 (enemy bird), check ObjY
  JMP CheckPlayerAndBirdY       ; ObjX is a player, jump to player-involved collision logic

GameObject1NotAPlayer
  CPY #MinEnemyBirdObjectIndex  ; Is ObjY a player (index < 2)?
  BCS GameObject2NotAPlayer     ; If ObjY_idx >= 2 (enemy bird), both are non-players
  JMP CheckPlayerAndBirdY       ; ObjY is a player, ObjX is enemy, jump to player-involved logic

GameObject2NotAPlayer           ; Both ObjX and ObjY are non-player birds (indices >= 2)
BirdsAtTheSameHeight
  LDA #SFX_BirdBirdBounce       ; Sound effect for bird-to-bird bounce
  JSR ScheduleSFX

BirdsHaveBounced   ; Collision response for two non-player birds
  PLA              ; A = Pop ObjY_idx from stack (pushed at Bird2BirdIterateAndCheckObjY)
  TAY              ; Y_REG = ObjY_idx (the object ObjX collided with)
                   ; X_REG still holds ObjX_idx

  LDA TempCollisionOverlap  ; Load Y-axis overlap magnitude (from GameObjCollisionDetection)
  CMP #$07                  ; Collision_MinYOverlapForHorizBounce (value from original code)
  BCS HandleVerticalBounceCollision      ; If Y_overlap >= 7, jump to vertical bounce logic

                                         ; Y_overlap < 7: Handle as a primarily horizontal collision (glancing blow)
                                         ; Nudge objects apart based on their relative X positions.
  LDA GameObjectXCoordHi,X               ; ObjX's X coordinate
  CMP GameObjectXCoordHi,Y               ; Compare with ObjY's X coordinate
  BCC Bird2BirdBounceObjXLeft_ObjYRight  ; If ObjX is to the left of ObjY

                                         ; ObjX is to the right of (or at same X as) ObjY.
                                         ; Nudge ObjY left, ObjX right.
  LDA #Direction_Left                    ; Set ObjY direction to Left
  STA GameObjectDirection,Y
  LDA GameObjectXCoordHi,Y
  SEC
  SBC #$01                                      ; ObjY_X--
  STA GameObjectXCoordHi,Y
  LDA #Direction_Right                          ; Set ObjX direction to Right
  STA GameObjectDirection,X
  INC GameObjectXCoordHi,X                      ; ObjX_X++
  JMP Bird2BirdApplyHorizontalBounceVelocities  ; Proceed to adjust velocities

Bird2BirdBounceObjXLeft_ObjYRight
                                                ; Nudge ObjY right, ObjX left.
  LDA #Direction_Right                          ; Set ObjY direction to Right
  STA GameObjectDirection,Y
  LDA GameObjectXCoordHi,Y
  CLC
  ADC #$01                                      ; ObjY_X++
  STA GameObjectXCoordHi,Y
  LDA #Direction_Left                           ; Set ObjX direction to Left
  STA GameObjectDirection,X
  DEC GameObjectXCoordHi,X                      ; ObjX_X--
                                                ; Fall through to Bird2BirdApplyHorizontalBounceVelocities

Bird2BirdApplyHorizontalBounceVelocities
                                       ; Adjust X-velocities for both collided objects.
                                       ; The logic effectively reverses direction and slightly dampens.
                                       ; e.g., Vel +2 becomes -1; Vel -2 becomes +1. Stops if Vel was +/-1.
Bird2BirdAdjustObjX_Vel
  LDA GameObjectXVelocity,X            ; Get ObjX's current X-velocity
  BEQ Bird2BirdStoreAdjustedObjX_Vel   ; If velocity is zero, no change needed
  EOR #$FF                             ; Invert all bits (NOT operation)
  BPL Bird2BirdStoreAdjustedObjX_Vel   ; If result is positive (original was negative or zero), use this value
                                       ; (e.g., if original was -1 ($FF), result is $00)
                                       ; (e.g., if original was -2 ($FE), result is $01)
  CLC
  ADC #$02                             ; If result was negative (original was positive), add 2 to complete pseudo-negation
                                       ; (e.g., if original was +1 ($01), NOT is $FE. $FE + 2 = $00)
                                       ; (e.g., if original was +2 ($02), NOT is $FD. $FD + 2 = $FF (-1))
Bird2BirdStoreAdjustedObjX_Vel
  STA GameObjectXVelocity,X            ; Store new X-velocity for ObjX

Bird2BirdAdjustObjY_Vel
  LDA GameObjectXVelocity,Y                    ; Get ObjY's current X-velocity
  BEQ Bird2BirdStoreAdjustedObjY_Vel           ; If velocity is zero, no change
  EOR #$FF                                     ; Invert all bits
  BPL Bird2BirdStoreAdjustedObjY_Vel           ; If result positive, use it
  CLC
  ADC #$02                                     ; Else, add 2
Bird2BirdStoreAdjustedObjY_Vel
  STA GameObjectXVelocity,Y                    ; Store new X-velocity for ObjY

  LDA GameObjectInAir,Y                        ; Check if ObjY is in the air
  BEQ Bird2BirdFinishHorizontalBounceResponse  ; If ObjY is on a platform, this horizontal bounce is sufficient.

  LDA TempCollisionOverlap                ; ObjY is airborne, check Y-overlap again
  CMP #$03
  BCS HandleVerticalBounceCollision       ; If Y_overlap >= 3, might need vertical bounce (external logic)
                                          ; Fall through to Bird2BirdFinishHorizontalBounceResponse if Y_overlap < 3 and ObjY airborne

Bird2BirdFinishHorizontalBounceResponse
  RTS  ; Return from ProcessBirdToBirdCollisionLoop

Bird2BirdIterateAndCheckObjY
  PLA
  DEY
  TYA
  BMI Bird2BirdFinishHorizontalBounceResponse
  PHA
  JMP Bird2BirdFilterObjYAndDetectCollision
HandleVerticalBounceCollision
  LDA GameObjectYVelocityLo,X
  EOR #$FF
  ADC #$01
  STA GameObjectYVelocityLo,X
  LDA GameObjectYVelocityLo,Y
  EOR #$FF
  ADC #$01
  STA GameObjectYVelocityLo,Y
  LDA GameObjectYVelocityHi,X
  EOR #$FF
  ADC #$01
  STA GameObjectYVelocityHi,X
  LDA GameObjectYVelocityHi,Y
  EOR #$FF
  ADC #$01
  STA GameObjectYVelocityHi,Y
  LDA GameObjectYVelocityHi,X
  ROL
  ROR GameObjectYVelocityHi,X
  ROR GameObjectYVelocityLo,X
  LDA GameObjectYVelocityHi,Y
  ROL
  LDA GameObjectYVelocityHi,Y
  ROR
  STA GameObjectYVelocityHi,Y
  LDA GameObjectYVelocityLo,Y
  ROR
  STA GameObjectYVelocityLo,Y
  LDA GameObjectYCoordHi,X
  CMP GameObjectYCoordHi,Y
  BCC HandleBouncePhysicsForUpperObject
  LDA GameObjectYVelocityHi,Y
  BPL ClampWinnerVelocityOnBounce
  CMP #$FF
  BCS ClampWinnerVelocityOnBounce
  RTS

ClampWinnerVelocityOnBounce
  LDA #$FF
  STA GameObjectYVelocityHi,Y
  RTS

HandleBouncePhysicsForUpperObject
  LDA GameObjectYVelocityHi,X
  BPL ClampBouncedObjectVelocityHi
  CMP #$FF
  BCS ClampBouncedObjectVelocityHi
  RTS

ClampBouncedObjectVelocityHi
  LDA #$FF
  STA GameObjectYVelocityHi,X
  RTS

;------------------------------------------------------------------------------
; CheckPlayerAndBirdY
; Handles the complex logic for when a player collides with another bird
; (either another player or an enemy). Determines who wins the joust based on
; vertical position, awards points, and initiates death sequences.
;------------------------------------------------------------------------------
CheckPlayerAndBirdY
; A player and enemy bird -or- player and player have collided
  LDA GameObjectYCoordHi,X
  CMP GameObjectYCoordHi,Y
  BNE DetermineJoustWinner
  JMP BirdsAtTheSameHeight
DetermineJoustWinner
  TXA
  PHA
  LDA #SFX_BirdExplosion
  JSR ScheduleSFX
  CPY #$02
  BCS WhichBirdWasHigher ; branch if non-player enemy bird
  CPX #$02
  BCS WhichBirdWasHigher ; branch if non-player enemy bird
  LDA #$00
  STA PlayersWereATeam
  LDA #$02
  STA Temp61ScoreAdd
  LDA #$00
  STA Temp62ScoreAdd
  LDA PlayerWasGladiator
  BEQ CalculateStandardPlayerJoustScore
  LDA #$03
  STA Temp61ScoreAdd
  LDA #$00
  STA Temp62ScoreAdd
CalculateStandardPlayerJoustScore
  LDA GameObjectYCoordHi,X
  CMP GameObjectYCoordHi,Y
  BCS PlayerXLosesJoustToPlayerY
  JSR UpdatePlayerScoreAndBonus
  LDA PlayerWasGladiator
  BEQ WhichBirdWasHigher
  LDA #$00
  STA PlayerWasGladiator
  LDA GameObjectYCoordHi,Y
  CLC
  ADC #$04
  STA Temp5E
  LDA GameObjectXCoordHi,Y
  CLC
  ADC #$0E
  STA Temp5F
  LDA #$05
  STA Temp60ScoreAdd
  JSR CreateScorePopupAtObjectLocation
  JMP WhichBirdWasHigher

PlayerXLosesJoustToPlayerY
  TXA
  PHA
  TYA
  TAX
  JSR UpdatePlayerScoreAndBonus
  LDA PlayerWasGladiator
  BEQ PlayerYWinsJoustVsPlayerX
  LDA #$00
  STA PlayerWasGladiator
  LDA GameObjectYCoordHi,X
  CLC
  ADC #$04
  STA Temp5E
  LDA GameObjectXCoordHi,X
  CLC
  ADC #$0E
  STA Temp5F
  LDA #$05
  STA Temp60
  JSR CreateScorePopupAtObjectLocation
PlayerYWinsJoustVsPlayerX
  PLA
  TAX
WhichBirdWasHigher
  LDA GameObjectYCoordHi,X
  CMP GameObjectYCoordHi,Y
  BCS JoustLoserIdentified
  TYA
  TAX
JoustLoserIdentified
  CPX #$02
  BCS PlayerKilledEnemyBird
  JSR StartPlayerDeath
  PLA
  TAX
  JMP BirdsHaveBounced
PlayerKilledEnemyBird
  TYA
  PHA
  JSR ConvertKilledBirdToEgg
  LDA ObjectBirdLevelAndType,X
  BNE CheckIfKilledBirdIsHunter
  LDA #$00
  STA Temp61ScoreAdd
  LDA #$50
  JMP StoreCalculatedScoreValue
CheckIfKilledBirdIsHunter
  CMP #$01
  BNE CalculateShadowLordKillScore
  LDA #$01
  STA Temp61ScoreAdd
  LDA #$00
  JMP StoreCalculatedScoreValue
SetDefaultKillScore
  LDA #$00
  STA Temp61ScoreAdd
  LDA #$05
  JMP StoreCalculatedScoreValue
CalculateShadowLordKillScore
  CMP #$02
  BNE SetDefaultKillScore
  LDA #$01
  STA Temp61ScoreAdd
  LDA #$50
StoreCalculatedScoreValue
  STA Temp62ScoreAdd
  PLA
  TAX
  JSR UpdatePlayerScoreAndBonus
  PLA
  TAX
  JMP BirdsHaveBounced

;------------------------------------------------------------------------------
; CheckPlayerEggCollision
; Iterates through all egg objects and checks for collision with a player bird.
; Handles egg pickup, scoring, and sound effects.
; Input: X = Player object index
;------------------------------------------------------------------------------
CheckPlayerEggCollision
  LDY #$0D
CheckPlayerEggCollisionLoop
  LDA.w GameObjectState,Y
  BEQ SkipThisEggCheck
  JSR GameObjCollisionDetection  ; carry is clear on collision
  BCC PlayerPickedUpAnEgg
  LDA GameObjectInAir,X
  BNE CheckAirbornePlayerEggCollision
  LDA GameObjectYCoordHi,Y
  STA Temp63
  SEC
  SBC #$05
  STA GameObjectYCoordHi,Y
  JSR GameObjCollisionDetection  ; carry is clear on collision
  BCC PlayerPickedUpAnEgg
  LDA Temp63
  STA GameObjectYCoordHi,Y
  JMP SkipThisEggCheck
CheckAirbornePlayerEggCollision
  LDA.w GameObjectState,Y
  CMP #$04
  BNE SkipThisEggCheck
  LDA GameObjectYCoordHi,Y
  STA Temp63
  SEC
  SBC #$03
  STA GameObjectYCoordHi,Y
  JSR GameObjCollisionDetection  ; carry is clear on collision
  BCC PlayerPickedUpAnEgg
  LDA GameObjectXCoordHi,Y
  STA Temp64
  CLC
  ADC #$04
  STA GameObjectXCoordHi,Y
  JSR GameObjCollisionDetection  ; carry is clear on collision
  BCC PlayerPickedUpAnEgg
  LDA Temp63
  STA GameObjectYCoordHi,Y
  JSR GameObjCollisionDetection  ; carry is clear on collision
  BCC PlayerPickedUpAnEgg
  LDA Temp64
  STA GameObjectXCoordHi,Y
SkipThisEggCheck
  INY
  CPY #$19
  BEQ AllEggsChecked
  JMP CheckPlayerEggCollisionLoop
AllEggsChecked
  RTS

PlayerPickedUpAnEgg
  LDA #SFX_PickupEgg
  JSR ScheduleSFX
  LDA.w GameObjectState,Y
  CMP #$03
  BEQ DecrementEnemyCountForHatchingEggPickup
  CMP #$02
  BNE HandleEggPickupState
DecrementEnemyCountForHatchingEggPickup
  DEC EnemyBirdCount
HandleEggPickupState
  CMP #$04
  BNE ClearPickedUpEggState
  STX Temp65
  LDX GameObjectHunterIndex,Y
  LDA #$05
  STA GameObjectState,X
  LDA #$01
  STA GameObjectTimerResetValue,X
  LDX Temp65
ClearPickedUpEggState
  LDA #$00
  STA.w GameObjectState,Y
  DEC EggsPresentCount
  BNE CalculateEggPickupScore
  LDA EnemyBirdCount
  BNE CalculateEggPickupScore
  JSR SetupNextLevel
CalculateEggPickupScore
  LDA GameObjectDirection,Y
  BEQ CalculateScoreForEggPickup
  LDA GameObjectYCoordHi,Y
  SEC
  SBC #$08
  STA Temp5E
  LDA GameObjectXCoordHi,Y
  STA Temp5F
  LDA #$04
  STA Temp60ScoreAdd
  JSR CreateScorePopupAtObjectLocation
  LDA #$00
  STA Temp61ScoreAdd
  LDA #$50
  STA Temp62ScoreAdd
  JSR UpdatePlayerScoreAndBonus
CalculateScoreForEggPickup
  LDA GameObjectYCoordHi,Y
  STA Temp5E
  LDA GameObjectXCoordHi,Y
  STA Temp5F
  LDA P0EggPointLevel,X
  STA Temp60ScoreAdd
  JSR CreateScorePopupAtObjectLocation
  LDA P0EggPointLevel,X
  TAY
  LDA NextEggPointLevelLUT,Y
  STA P0EggPointLevel,X
  LDA EggPointLevelScoreHiLUT,Y
  STA Temp61ScoreAdd
  LDA EggPointLevelScoreLoLUT,Y
  STA Temp62ScoreAdd
  JMP UpdatePlayerScoreAndBonus

NextEggPointLevelLUT
 .byte $01,$02,$03,$03

; Consecutive egg pickups score 250,500,750,1000,1000,1000...
EggPointLevelScoreHiLUT
 .byte >$0025,>$0050,>$0075,>$0100
EggPointLevelScoreLoLUT
 .byte <$0025,<$0050,<$0075,<$0100

;------------------------------------------------------------------------------
; ProcessBirdPlatformInteraction
; Handles all physics and state changes for a bird object (index in X)
; related to its interaction with platforms.
;
; This routine has two main paths:
; 1. On-Platform Logic: If the bird is on a platform (InAir flag is false),
;    it checks for proximity to edges, handles skidding, and can initiate a fall.
; 2. Airborne Logic: If the bird is airborne (InAir flag is true), it checks
;    for collisions with platforms, which can result in landing, bouncing, or
;    a "skip" across the surface.
;------------------------------------------------------------------------------
ProcessBirdPlatformInteraction
    LDA GameObjectInAir,X                 ; Is the bird currently airborne?
    BNE BirdIsAirborne_CheckForCollision  ; If yes, branch to airborne logic path.

; --- On-Platform Logic ---
    JSR CheckIfBirdNearPlatformEdge                          ; Check for proximity to a platform edge.
    LDA Temp60                         ; Load the result of the edge check.
    BNE OnPlatform_EdgeOrSideDetected  ; If non-zero, an edge was detected.

; Bird is on a platform but not near an edge.
    LDA GameObjectSkidDirection,X              ; Is the bird currently skidding?
    BEQ BirdNotSkidding1                       ; If not, proceed to check if it should fall.
    JSR ClearSkidStateAndSound                 ; If it is skidding (but not near an edge), clear the skid state.
BirdNotSkidding1
    JMP CheckIfBirdIsUnsupportedAndShouldFall  ; Check if bird is on a valid platform spot, otherwise fall.

OnPlatform_EdgeOrSideDetected
    JSR SelectCollisionPointsAndCheckPlatform  ; Perform a more detailed platform collision check.
    CMP #$08                      ; Is the collision type a side impact?
    BEQ OnPlatform_BounceOffSide  ; If yes, handle bouncing off the side.
    RTS                           ; Otherwise, no action needed this frame.

OnPlatform_BounceOffSide
    INC GameObjectXCoordHi,X         ; Nudge the bird slightly away from the platform.
    LDA #SFX_BirdPlatformBounce      ; Schedule the bounce sound effect.
    JSR ScheduleSFX
    LDA GameObjectXVelocity,X        ; Reverse X-velocity.
    EOR #$FF
    CLC
    ADC #$01
    STA GameObjectXVelocity,X
    LDA GameObjectDirection,X        ; Reverse facing direction.
    EOR #$FF
    CLC
    ADC #$01
    STA GameObjectDirection,X
    LDA GameObjectSkidDirection,X    ; Reverse skid direction if skidding.
    EOR #$FF
    CLC
    ADC #$01
    STA GameObjectSkidDirection,X
    RTS

; --- Airborne Logic ---
BirdIsAirborne_CheckForCollision
   JSR SelectCollisionPointsAndCheckPlatform   ; Check for collision with any platform.
    BEQ Airborne_NoCollision_ClearSkipState    ; If no collision, clear skip state and continue.
    BMI Airborne_HandleLanding                 ; If result is negative, it's a landing on top of a platform.
    CMP #$01
    BEQ Airborne_CheckForPlatformSkip          ; A result of 1 or 8 indicates a shallow landing or side hit.
    CMP #$08
    BEQ Airborne_CheckForPlatformSkip
    JMP CheckIfBirdIsUnsupportedAndShouldFall  ; For other collision types, check if it should fall.

Airborne_HandleLanding
    LDA GameObjectPlatformSkipState,X          ; Is the bird in a "skip" state from a previous frame?
    BNE CheckIfBirdIsUnsupportedAndShouldFall  ; If yes, don't land yet, let it fall/continue skipping.
    LDA #$02                                   ; Set animation frame to standing/walking.
    STA GameObjectAnimationFrame,X
    LDA #$00                                   ; Bird is no longer in the air.
    STA GameObjectInAir,X
    STA GameObjectYVelocityHi,X                ; Zero out Y-velocity.
    STA GameObjectYVelocityLo,X
    LDA GameObjectXVelocity,X                  ; Check X-velocity to set facing direction.
    BEQ Airborne_Landing_Finalize              ; If stopped, branch to the JMP trampoline.
    BMI Airborne_Landing_FaceLeft
    LDA #Direction_Right                       ; Moving right, so face right.
    STA GameObjectDirection,X
    JMP Airborne_Landing_Finalize              ; Jump to the JMP trampoline.
Airborne_Landing_FaceLeft
    LDA #Direction_Left                        ; Moving left, so face left.
    STA GameObjectDirection,X
                                               ; Fall through to the JMP trampoline.
Airborne_Landing_Finalize
    JMP UpdateCoordsFromTempAndExit_B          ; This JMP acts as a trampoline for the short branches above.

Airborne_CheckForPlatformSkip
    JSR CheckForShallowPlatformCollision       ; Another check, possibly to confirm the nature of the shallow landing.
    BNE Airborne_ProcessBounce                 ; If it's a bounce, process it.
    LDA GameObjectYCoordHi,X                   ; Check bird's Y coordinate.
    CMP #$92                                   ; Is it above a certain height?
    BCS CheckIfBirdIsUnsupportedAndShouldFall  ; If too low, treat as a fall.
    LDA #$01                                   ; Otherwise, initiate a "skip" across the platform surface.
    STA GameObjectPlatformSkipState,X
    BPL CheckIfBirdIsUnsupportedAndShouldFall  ; Always branches.

Airborne_NoCollision_ClearSkipState
    LDA #$00                                   ; No collision, so clear any platform skip state.
    STA GameObjectPlatformSkipState,X

CheckIfBirdIsUnsupportedAndShouldFall
    LDA #$01                                   ; Set InAir flag to TRUE, making the bird subject to gravity.
    STA GameObjectInAir,X
    LDA GameObjectYCoordHi,X
    CMP #TOP_SCREEN_Y_BOUNDARY                 ; Has the bird hit the top of the screen?
    BCC Airborne_BounceOffTopScreen            ; If Y < boundary, it has.
    JSR CheckForShallowPlatformCollision       ; Check for platform support again.
    BNE Airborne_ProcessBounce                 ; If a bounce condition is met, process it.
    JMP Exit_ProcessBirdPlatformInteraction    ; Otherwise, bird is airborne and falling, exit.

Airborne_BounceOffTopScreen
    LDA GameObjectYVelocityHi,X                ; Get Y-velocity.
    BMI Airborne_ReverseYVelocityForBounce     ; If moving up (negative), reverse it for a bounce.
    RTS                                        ; If moving down, just let it fall away from the top.

Airborne_ReverseYVelocityForBounce
    EOR #$FF                            ; Reverse Y-velocity to bounce down.
    STA GameObjectYVelocityHi,X
    LDA GameObjectYVelocityLo,X
    EOR #$FF
    ADC #$01
    STA GameObjectYVelocityLo,X
    RTS

Airborne_ProcessBounce
    TAY                                 ; A holds collision type, save in Y.
    BPL Airborne_Bounce_PositiveResult  ; Handle different bounce types based on sign.
                                        ; Negative result: Bounce off the bottom of a platform.
    LDA #SFX_BirdPlatformBounce
    JSR ScheduleSFX
    LDA #$80                            ; Set Y-velocity to a strong upward value.
    STA GameObjectYVelocityLo,X
    LDA #$FF
    STA GameObjectYVelocityHi,X
    JMP UpdateCoordsFromTempAndExit_B

Airborne_Bounce_PositiveResult
    LDA #SFX_BirdPlatformBounce
    JSR ScheduleSFX
    TYA                                  ; Restore collision type from Y.
    LSR
    BCS Airborne_Bounce_SideImpactRight  ; Bit 0 set -> hit right side of platform
    LSR
    BCS Airborne_Bounce_GlancingBlow     ; Bit 1 set -> glancing blow, small bounce
    LSR
    BCS Airborne_Bounce_GlancingBlow     ; Bit 2 set -> also a glancing blow
                                         ; Fall-through for other bounce types
    LDA GameObjectXVelocity,X
    BPL UpdateCoordsFromTempAndExit_B    ; If moving right, no X-velocity change.
    EOR #$FF                             ; If moving left, dampen X-velocity (divide by 2).
    CLC
    ADC #$01
    ROR
    JMP Airborne_Bounce_StoreNewXVel

Airborne_Bounce_SideImpactRight
    LDA GameObjectXVelocity,X
    BEQ UpdateCoordsFromTempAndExit_B   ; If stopped, no change.
    BMI UpdateCoordsFromTempAndExit_B   ; If moving left (away from impact), no change.
    EOR #$FF                            ; Moving right into platform, reverse and dampen velocity.
    CLC
    ADC #$01
    SEC
    ROR
Airborne_Bounce_StoreNewXVel
    STA GameObjectXVelocity,X
    LDA GameObjectState,X
    CMP #State_Hit                                            ; Is the bird in the "hit" state?
    BNE UpdateCoordsFromTempAndExit_A                         ; If not, just update coords.
    LDY ObjectBirdLevelAndType,X                              ; If it is, reset its AI timer to make it
    LDA AITimerDistantPassive_Or_ApproachAggressive_ByType,Y  ; react more quickly after the bounce.
    STA GameObjectTimerResetValue,X
    STA GameObjectEventTimer1,X
UpdateCoordsFromTempAndExit_A
    RTS

Airborne_Bounce_GlancingBlow
    LDA #$50                         ; Set Y-velocity to a moderate upward value.
    STA GameObjectYVelocityLo,X
    LDA #$00
    STA GameObjectYVelocityHi,X

UpdateCoordsFromTempAndExit_B
    LDA Temp65                       ; Load new X coordinate calculated by collision routine.
    STA GameObjectXCoordHi,X
    LDA Temp66                       ; Load new Y coordinate.
    STA GameObjectYCoordHi,X

Exit_ProcessBirdPlatformInteraction
    RTS

;------------------------------------------------------------------------------
; UpdateEggPhysicsAndPlatformInteraction
; Handles physics and platform interaction specifically for an egg object.
; Input: X = Egg object index
;------------------------------------------------------------------------------
UpdateEggPhysicsAndPlatformInteraction
  JSR SelectEggCollisionPointAndCheck
  BEQ EggNoPlatformCollision
  STA Temp5E
  LDA #$00
  STA GameObjectDirection,X
  LDA Temp5E
  BMI HandlePlatformTopCollisionEgg
  LSR
  BCS EggBounceOffRightSideOfPlatform
  LSR
  BCS NegateEggYVelocityOnBounce
  LSR
  BCS NegateEggYVelocityOnBounce
  LSR
  BCS EggAirborne_BounceOffSideOfPlatform
EggNoPlatformCollision
  RTS

EggAirborne_BounceOffSideOfPlatform
  LDA GameObjectXVelocity,X
  BMI ReverseEggXVelocityOnBounce
  JMP UpdateEggCoordsAfterCollision
ReverseEggXVelocityOnBounce
  EOR #$FF
  CLC
  ADC #$01
  STA GameObjectXVelocity,X
  JMP UpdateEggCoordsAfterCollision
EggBounceOffRightSideOfPlatform
  LDA GameObjectXVelocity,X
  BPL ReverseEggXVelocityOnBounce
  JMP UpdateEggCoordsAfterCollision
NegateEggYVelocityOnBounce
  LDA GameObjectYVelocityHi,X
  BPL EggBounceYVelocityAlreadyDownward
  SEC
  LDA #$00
  SBC GameObjectYVelocityLo,X
  STA GameObjectYVelocityLo,X
  LDA #$00
  SBC GameObjectYVelocityHi,X
  STA GameObjectYVelocityHi,X
EggBounceYVelocityAlreadyDownward
  LDA GameObjectXVelocity,X
DecelerateBouncingEggXVelocity
  BMI DecelerateBouncingEggLeft
  DEC GameObjectXVelocity,X
  JMP UpdateEggCoordsAfterCollision
DecelerateBouncingEggLeft
  INC GameObjectXVelocity,X
  JMP UpdateEggCoordsAfterCollision
HandlePlatformTopCollisionEgg
  LDA GameObjectYVelocityHi,X
  BNE EggIsMovingBounce
  LDA GameObjectXVelocity,X
  BNE EggIsMovingBounce
  LDA GameObjectYVelocityLo,X
  CMP #$28
  BCC EggLandedOnPlatform
EggIsMovingBounce
  SEC
  LDA #$00
  SBC GameObjectYVelocityLo,X
  STA GameObjectYVelocityLo,X
  LDA #$00
  SBC GameObjectYVelocityHi,X
  STA GameObjectYVelocityHi,X
  ROL
  ROR GameObjectYVelocityHi,X
  ROR GameObjectYVelocityLo,X
  LDA GameObjectXVelocity,X
  BNE DecelerateBouncingEggXVelocity
  RTS

EggLandedOnPlatform
  LDA #$00
  STA GameObjectDirection,X
  STA ObjectWiggleAnimation,X
  STA GameObjectInAir,X
  STA GameObjectYVelocityLo,X
  JSR CalculateLevelDifficultyRandomTimer
  LDA Temp5E
  STA GameObjectCountdownLo,X
  LDA Temp5F
  STA GameObjectCountdownHi,X
UpdateEggCoordsAfterCollision
  LDA Temp65
  STA GameObjectXCoordHi,X
  LDA Temp66
  STA GameObjectYCoordHi,X
  RTS

;------------------------------------------------------------------------------
; SpawnPointSafetyCheck
; Checks if a spawn point is safe for a new bird to appear. It iterates through
; all other birds to see if any are too close to the target spawn point.
; Input: X = index of bird to be spawned.
; Output: Temp60 = 0 if safe, non-zero if unsafe.
;------------------------------------------------------------------------------
SpawnPointSafetyCheck
  TXA
  PHA                      ; save X
  CPX #$02
  BCC SafetyCheckForPlayer
SafetyCheckForEnemy
  LDX #$03
  JMP SafetyCheckLoop
SafetyCheckForPlayer
  LDX #$00
SafetyCheckLoop
  LDA SpawnPlatformDissolved0,X
  BEQ HandleSpawnOnDissolvedPlatform
  LDY #$09
  LDA #$00
  STA TempSpawnUnsafeCounter,X
CheckProximityToOtherObjectsLoop
  LDA.w GameObjectState,Y
  BEQ SpawnSafetyCheckDoNextObject
  CMP #$05
  BEQ SpawnSafetyCheckDoNextObject
  CMP #$03
  BEQ SpawnSafetyCheckDoNextObject
  CMP #$07
  BEQ SpawnSafetyCheckDoNextObject
  CMP #$06
  BNE ObjectStateIsValidForProximityCheck
  LDA GameObjectSpawnPlatformIndex,Y
  STX Temp5F
  CMP Temp5F
  BNE ObjectStateIsValidForProximityCheck
HandleSpawnOnDissolvedPlatform
  INC TempSpawnUnsafeCounter,X
  JMP MarkSpawnPointUnsafeAndContinueCheck
ObjectStateIsValidForProximityCheck
  LDA GameObjectYCoordHi,Y
  SEC
  SBC PlayerRespawnY,X
  BPL CompareAbsoluteYDistanceToMinimum
  EOR #$FF
  CLC
  ADC #$01
CompareAbsoluteYDistanceToMinimum
  CMP #$1E
  BCS SpawnSafetyCheckDoNextObject
  LDA GameObjectXCoordHi,Y
  SEC
  SBC PlayerRespawnX,X
  BPL CompareAbsoluteXDistanceToMinimum
  EOR #$FF
  CLC
  ADC #$01
CompareAbsoluteXDistanceToMinimum
  CMP #$28
  BCS SpawnSafetyCheckDoNextObject
  JMP MarkSpawnPointUnsafeAndContinueCheck
SpawnSafetyCheckDoNextObject
  DEY
  BPL CheckProximityToOtherObjectsLoop
  STX Temp60
  PLA
  TAX
  RTS

MarkSpawnPointUnsafeAndContinueCheck
  PLA
  PHA
  CMP #$02
  BCS SafetyCheckLoop_Player
  INX
  CPX #$04
  BCC SafetyCheckLoop
  JMP MarkSpawnPointUnsafeAndExit
SafetyCheckLoop_Player
  DEX
  BPL SafetyCheckLoop
MarkSpawnPointUnsafeAndExit
  LDA #$FF
  STA Temp60
  PLA
  TAX
  RTS

;------------------------------------------------------------------------------
; SetupEggWave
; Initializes all egg objects for an Egg Wave. It randomly places eggs on
; available platforms, ensuring they are not too clustered.
;------------------------------------------------------------------------------
SetupEggWave
  LDA #$00
  STA EnemyBirdCount
  LDA #$07
  TAX
  LDY #$0C
  STY EggsPresentCount
  DEY
; this is the start of the random egg placement code.
  JSR ReturnRandInA
GetNewEggPosition
  STX Temp64
  CMP NewEggXMaximum,X
  BCC EggXRangeFitsPlatform
GetNewEggPositionLoop
  LDX Temp64
  JSR ReturnRandInA
  DEX
  BPL GetNewEggPosition
  LDX #$07
  JMP GetNewEggPosition
EggXRangeFitsPlatform
  CLC                            ; The width of the random X range used fits
  ADC NewEggXOffset,X            ; the platform width, so we just need to add
  STA EggX,Y                     ; to EggX to reach the X of the platform.
  LDA NewEggYCoordinate,X
  STA EggY,Y
  CPY #$0B                       ; first egg? if so then skip
  BEQ EggLocationValidationDone  ; egg position validation

EggLocationValidation
; We first ensure that no more than 5 eggs already exist on the same
; platform. Otherwise we fetch new egg coordinates.
  LDA #$05
  STA Temp65
  LDX #$0B
  LDA EggY,Y
EggYCountCheckLoop
  CMP EggY,X
  BNE EggNotAtSameY1
  DEC Temp65
  BEQ GetNewEggPositionLoop
EggNotAtSameY1
  DEX
  STX Temp66
  CPY Temp66             ; only check eggs up until the current one
  BNE EggYCountCheckLoop

; Now we ensure that our egg is at least 5 pixels away from any other
; eggs on the same platform. Otherwise we fetch new egg coordinates.
  LDX #$0B
EggDistanceCheckLoop
  LDA EggY,Y
  CMP EggY,X
  BNE EggNotAtSameY2
  LDA EggX,Y
  SEC
  SBC EggX,X                ; Distance is OurEggX - ThisEggX
  BPL SkipDistanceInversion
; if we have a negative distance, we need to invert it (A=255-A)
  EOR #$FF
  CLC
  ADC #$01
SkipDistanceInversion
  CMP #$05
  BCC GetNewEggPositionLoop
EggNotAtSameY2
  DEX
  STX Temp66
  CPY Temp66
  BNE EggDistanceCheckLoop

EggLocationValidationDone
  LDA EggWaveEnemyType
  STA EggLevelAndType,Y
  LDA #$01
  STA.w EggState,Y
  JSR CalculateLevelDifficultyRandomTimer
  LDA Temp5E
  STA $232E,Y
  LDA Temp5F
  STA $2315,Y
  LDA #$00
  STA $2347,Y
  STA $221B,Y
  STA $227F,Y
  STA $22CA,Y
  STA $22FC,Y
  STA $2298,Y
  STA $22B1,Y
  DEY
  BMI ExitEggWaveSetup
  JMP GetNewEggPositionLoop
ExitEggWaveSetup
  RTS

;------------------------------------------------------------------------------
; CalculateLevelDifficultyRandomTimer
; Calculates a 16-bit random timer value influenced by game level and difficulty.
; Used for Pterodactyl appearance delays and egg hatching countdowns.
; The final timer is primarily based on a random value plus difficulty-scaled
; additions, with an initial offset to the high byte determined by the level.
; Outputs: Temp5E (Lo), Temp5F (Hi)
;------------------------------------------------------------------------------
CalculateLevelDifficultyRandomTimer
  TXA  ; Preserve X register
  PHA
  TYA  ; Preserve Y register
  PHA

  LDA #InitialTimerHiByteValue  ; Initialize high byte of timer
  STA Temp5F                    ; Temp5F will act as the high byte accumulator

; Calculate loop counter for difficulty-based additions.
; Loop runs (DifficultyLoopCounterBase - Difficulty + 1) times.
  LDA #DifficultyLoopCounterBase
  SEC
  SBC Difficulty  ; A = DifficultyLoopCounterBase - Difficulty
  TAX             ; X will be the loop counter (0 to 3)

; Calculate an 8-bit term based on the current level.
  LDA #TimerLevelCalcBaseValue
  SEC
  SBC Level  ; A = TimerLevelCalcBaseValue - Level

; Multiply this level-based term by 8 (ASL A three times).
; The carry from the third ASL (representing bit 8 of an 9-bit result)
; is added to Temp5F (timer high byte). The 8 LSBs in A are then discarded.
  ASL                                    ; A = A * 2
  ASL                                    ; A = A * 4
  ASL                                    ; A = A * 8. Carry C holds the 9th bit.
  BCC CalculateTimer_LevelComponentDone  ; If C=0, no carry into 9th bit.
  INC Temp5F                             ; C=1, (LevelTerm*8) overflowed 8 bits. Add to TimerHi.

CalculateTimer_LevelComponentDone

; A currently holds (TimerLevelCalcBaseValue - Level)*8 AND $FF. This value is about to be overwritten.
; Temp5F holds 0 or 1 based on the carry from the above calculation.

  JSR ReturnRandInA                  ; A = RandomByte
                                     ; The subsequent ADC loop will start with this random byte as the LSB,
                                     ; and Temp5F as the initial MSB.
CalculateTimer_DifficultyLoop        ; Loop (X+1) times, adding to the (Temp5F,A) 16-bit value.
  ADC #RandomTimerLoopAddend         ; A = A + RandomTimerLoopAddend + CarryFromPreviousADC (initially 0 for first ADC)
  BCC CalculateTimer_EndLoopIter     ; If no carry from ADC to high byte
  INC Temp5F                         ; Add carry to high byte of timer
CalculateTimer_EndLoopIter
  DEX                                ; Decrement difficulty-based loop counter
  BPL CalculateTimer_DifficultyLoop  ; Loop if X >= 0

  STA Temp5E                         ; Store final low byte of timer in Temp5E

  PLA                                ; Restore Y register
  TAY
  PLA                                ; Restore X register
  TAX
  RTS

;------------------------------------------------------------------------------
; RunPterryCountdown
; Main logic for managing the Pterodactyl appearance timer and spawning.
;------------------------------------------------------------------------------
RunPterryCountdown
  LDA Difficulty
  BEQ RunPterryCountdownExit
  LDA PterryAliveCount
  CMP #$03  ; 3 Pterrys max
  BEQ RunPterryCountdownExit
  SEC
  LDA PterryCountdownLo
  SBC #$01
  STA PterryCountdownLo
  LDA PterryCountdownHi
  BMI CheckForLiveEnemyBirds
  SBC #$00
  STA PterryCountdownHi
  BMI CheckForLiveEnemyBirds
  RTS

DelayPterry
  INC PterryCountdownLo
RunPterryCountdownExit
  RTS

CheckForLiveEnemyBirds
  LDX #$09         ; last bird object
CheckForLiveEnemyBirdsLoop
  LDA GameObjectState,X
  CMP #$07         ; if a bird is being born2...
  BEQ DelayPterry  ; ...delay spawning a pterry.
  CMP #$06         ; if a bird is transporting-in...
  BEQ DelayPterry  ; ...delay spawning a pterry.
  CMP #$03         ; if a bird is being born1...
  BEQ DelayPterry  ; ...delay spawning a pterry.
  DEX              ; Check another Enemy bird object so long
  CPX #$02         ; as we haven't reached the player birds.
  BCS CheckForLiveEnemyBirdsLoop

  LDA #SFX_PterryCharging
  JSR ScheduleSFX
  INC PterryAliveCount
  LDX #$0A
FindDeadPterryLoop
  LDA GameObjectState,X
  BEQ InitializeNewPterryObject
  INX
  CPX #$0D
  BCC FindDeadPterryLoop
PterrySpawnFailCrashLoop
  BCS PterrySpawnFailCrashLoop  ; crash loop, but shouldn't ever trigger
InitializeNewPterryObject
  LDA #$0A                      ; Normal Pterry state
  STA GameObjectState,X
  LDA #$04
  STA ObjectBirdLevelAndType,X
  LDA #$00
  STA GameObjectYVelocityHi,X
  STA GameObjectYVelocityLo,X
  LDA #$03
  STA GameObjectAnimationCounter,X
  LDA #$02
  STA GameObjectAnimationFrame,X
  LDA #$FA
  STA GameObjectChargingTimer,X
  LDA #$02
  STA GameObjectAITimerLo,X
  JSR SetPterryInitialSpawnParameters
  CPX #$0A
  BNE UpdatePterryRespawnTimerAndExit
  LDA PterryAgressionFlag
  CMP #$02
  BNE UpdatePterryRespawnTimerAndExit
  LDA PterryAgressionFlag+1
  CMP #$02
  BEQ ResetPterryCountdownForced
UpdatePterryRespawnTimerAndExit
  JSR UpdatePterryRespawnTimer
  LSR PterryCountdownHi  ; divide the pterry
  ROR PterryCountdownLo  ; countdown by two
  RTS

ResetPterryCountdownForced
  LDA #$00
  STA PterryCountdownHi
  LDA #$14
  STA PterryCountdownLo
  RTS

; Look-up table for initial Y coordinates for Pterodactyl spawn.
; Indexed by (RandomByte AND PTERRY_SPAWN_Y_COORD_MASK).
PterrySpawnYCoordLUT
 .byte $94, $52, $52, $15

; Look-up table for initial X coordinates for Pterodactyl spawn.
; Indexed by (RandomByte AND PTERRY_SPAWN_XDIRVEL_MASK).
PterrySpawnXCoordLUT
 .byte $15, $BA

; Look-up table for initial facing direction for Pterodactyl spawn.
; $01 = Right, $FF = Left.
; Indexed by (RandomByte AND PTERRY_SPAWN_XDIRVEL_MASK).
PterrySpawnDirectionLUT
 .byte Direction_Right, Direction_Left

; Look-up table for initial X velocities for Pterodactyl spawn.
PterrySpawnXVelocityLUT
 .byte $05, $FB

;------------------------------------------------------------------------------
; SetPterryInitialSpawnParameters
; Sets the initial spawn parameters for a new Pterodactyl and ensures it does
; not spawn too close to a player. Re-randomizes if it's too close.
; Input: X = Pterodactyl object index
;------------------------------------------------------------------------------
SetPterryInitialSpawnParameters
TryNewPterrySpawnParameters
  JSR ReturnRandInA               ; Get a random byte in A
  AND #PTERRY_SPAWN_Y_COORD_MASK  ; Mask to get index 0-3 for Y LUT
  TAY                             ; Y = random index for Y coordinate
  LDA PterrySpawnYCoordLUT,Y      ; Load Y coordinate
  STA GameObjectYCoordHi,X        ; Set Pterry's initial Y

  JSR ReturnRandInA               ; Get another random byte
  AND #PTERRY_SPAWN_XDIRVEL_MASK  ; Mask to get index 0-1 for X/Dir/XVel LUTs
  TAY                             ; Y = random index for X, Direction, X-Velocity
  LDA PterrySpawnXCoordLUT,Y      ; Load X coordinate
  STA GameObjectXCoordHi,X        ; Set Pterry's initial X
  LDA PterrySpawnDirectionLUT,Y   ; Load direction
  STA GameObjectDirection,X       ; Set Pterry's initial direction
  LDA PterrySpawnXVelocityLUT,Y   ; Load X-velocity
  STA GameObjectXVelocity,X       ; Set Pterry's initial X-velocity

  LDY PlayerCount                           ; Y = number of players (0 for 1P, 1 for 2P; used as player object index)
                                            ; Player 0 is index 0, Player 1 is index 1.
CheckPterrySpawnProximityToPlayerLoop
; Calculate absolute X distance to current player Y
  LDA GameObjectXCoordHi,X                  ; Pterry's X
  SEC
  SBC GameObjectXCoordHi,Y                  ; Pterry's X - Player Y's X
  BMI PterryXDistIsNegativeOrZero           ; If result is negative (PlayerX > PterryX)
  EOR #$FF                                  ; Make positive (abs value part 1)
  CLC
  ADC #$01                                  ; Make positive (abs value part 2)
PterryXDistIsNegativeOrZero
  CMP #PTERRY_MIN_SPAWN_DIST_X              ; Compare |PterryX - PlayerX| with $30
  BCS PterrySpawnIsFarEnoughFromThisPlayer  ; If dist >= $30, X is okay for this player.

                                            ; X distance < PTERRY_MIN_SPAWN_DIST_X, now check Y distance
  LDA GameObjectYCoordHi,X                  ; Pterry's Y
  SEC
  SBC GameObjectYCoordHi,Y                  ; Pterry's Y - Player Y's Y
  BMI PterryYDistIsNegativeOrZero           ; If result is negative (PlayerY > PterryY)
  EOR #$FF                                  ; Make positive (abs value part 1)
PterryYDistIsNegativeOrZero                 ; Note: CLC ADC #$01 for abs value missing if BMI not taken.
                                            ; However, since we only care about CMP < $20, a large positive
                                            ; (if PterryY > PlayerY) will correctly fail the BCC.
                                            ; If PterryY < PlayerY, BMI is taken, and it becomes positive small.
  CMP #PTERRY_MIN_SPAWN_DIST_Y              ; Compare |PterryY - PlayerY| with $20
  BCC SetPterryInitialSpawnParameters       ; If dist < $20, Pterry is too close. Re-randomize.

PterrySpawnIsFarEnoughFromThisPlayer
  DEY                                       ; Next player index (from PlayerCount down to -1)
  BPL CheckPterrySpawnProximityToPlayerLoop ; If Y >= 0, check next player.

  RTS                                       ; Pterry spawn position is okay for all players.

;------------------------------------------------------------------------------
; RunPterryProcessing
; Main routine to update the state and behavior of a Pterodactyl object.
; This is a state machine handling its movement, AI, and collision.
; Input: X = Pterodactyl object index
;------------------------------------------------------------------------------
RunPterryProcessing
  LDA GameObjectState,X
  BNE PterryIsAliveAndNotDeadState
  JMP ExitPterryProcessing_NoAction   ; If state is 0 (dead), do nothing

PterryIsAliveAndNotDeadState
  CMP #PterryState_DefeatedVisual     ; Is Pterry in its "defeated" visual effect state ($0D)?
  BNE PterryNotInDefeatedVisualState  ; If not, proceed to normal state processing

; Pterry is in "defeated" visual state (e.g., spinning off screen)
  DEC GameObjectXCoordLo,X                    ; Update sub-pixel X for visual effect
  BEQ PterryDefeatedVisual_UpdateY_And_Frame  ; If XCoordLo wrapped, update Y and frame
  RTS                                         ; Otherwise, visual effect continues

PterryDefeatedVisual_UpdateY_And_Frame
  INC GameObjectAnimationFrame,X           ; Advance animation frame for defeat visual
  DEC GameObjectYCoordLo,X                 ; Update sub-pixel Y for visual effect
  BEQ PterryDefeatedVisual_FullyOffscreen  ; If YCoordLo wrapped, Pterry is gone
  LDA #$0A                                 ; Reset XCoordLo for next part of visual effect
  STA GameObjectXCoordLo,X
  RTS

PterryDefeatedVisual_FullyOffscreen
  LDA #State_Dead                       ; Pterry is now fully dead and off-screen
  STA GameObjectState,X
  DEC PterryAliveCount                  ; Decrement count of active Pterodactyls
  LDA #$01                              ; Flag related to Pterry state or scoring?
  STA PterryDefeatedFlag,X              ; Store this flag (unknown purpose, maybe related to next Pterry spawn timing)
  LDA GameObjectYCoordHi,X              ; Store Pterry's last Y position
  STA TempPterryCoordY
  LDA GameObjectXCoordHi,X              ; Store Pterry's last X position
  STA TempPterryCoordX
  LDA #$03
  STA TempPterryScoreValue
  JMP CreateScorePopupAtObjectLocation  ; Create a score popup

PterryNotInDefeatedVisualState
  JSR CheckPterryPlatformCollision  ; Returns collision type in A, Temp65/Temp66 updated with new coords if bounced
  BEQ PterryNoPlatformCollision     ; If A is 0, no collision or standard fall-through
  BMI PterryHitPlatform_MoveUp      ; If A is negative, hit bottom of platform, bounce up

; Pterry hit side or top of platform (A is positive)
  CLC
  LSR                                           ; A = A / 2 (collision type to direction/action bits)
  BCS PterryHitPlatform_FaceLeftAndReverseXVel  ; Bit 0 set: hit right side
  LSR                                           ; A = A / 2
  BCS PterryHitPlatform_MoveDownSlightly        ; Bit 1 set: hit top, shallow bounce
  LSR                                           ; A = A / 2
  BCS PterryHitPlatform_MoveDownSlightly        ; Bit 2 set: hit top, shallow bounce (alternative condition)
  LSR                                           ; A = A / 2
  BCC PterryNoPlatformCollision                 ; Bit 3 clear: no significant horizontal reaction needed

                             ; Bit 3 set: hit left side
  LDA #Direction_Right       ; Change direction to right
  STA GameObjectDirection,X
  LDA GameObjectXVelocity,X  ; Reverse X velocity
  EOR #$FF
  CLC
  ADC #$01
  STA GameObjectXVelocity,X
  JMP PterryPlatformCollision_FinalizeBounce

PterryHitPlatform_MoveDownSlightly   ; Hit top of platform, shallow bounce down
  LDA GameObjectYCoordHi,X
  SEC
  SBC #$02                           ; Move Pterry down slightly (adjust Y coord up on screen)
  STA GameObjectYCoordHi,X
  JMP PterryPlatformCollision_FinalizeBounce

PterryHitPlatform_MoveUp  ; Hit bottom of platform, bounce up
  LDA GameObjectYCoordHi,X
  CLC
  ADC #$02                 ; Move Pterry up slightly (adjust Y coord down on screen)
  STA GameObjectYCoordHi,X
  JMP PterryPlatformCollision_FinalizeBounce

PterryHitPlatform_FaceLeftAndReverseXVel   ; Hit right side of platform
  LDA #Direction_Left                      ; Change direction to left
  STA GameObjectDirection,X
  LDA GameObjectXVelocity,X                ; Reverse X velocity
  EOR #$FF
  CLC
  ADC #$01
  STA GameObjectXVelocity,X
                                           ; Fall through to PterryPlatformCollision_FinalizeBounce

PterryPlatformCollision_FinalizeBounce     ; Common code after platform collision
  LDA #SFX_BirdPlatformBounce              ; Pterry uses bird bounce sound for platforms
  JSR ScheduleSFX
  LDA Temp65                               ; Temp65 holds adjusted X from CheckPterryPlatformCollision
  STA GameObjectXCoordHi,X
  LDA Temp66                               ; Temp66 holds adjusted Y from CheckPterryPlatformCollision
  STA GameObjectYCoordHi,X

PterryNoPlatformCollision                  ; No platform collision or handled above
  JSR UpdateAirborneObjectPhysics          ; Apply standard airborne physics (gravity, X/Y movement)

CheckPterryStateAfterPhysics
  LDA GameObjectState,X
  CMP #PterryState_Leaving                           ; Is Pterry leaving the screen?
  BNE CheckPterryState_NormalOrCharging
  JSR SetPterryVelocityForChargingScreen             ; Set fast velocity
  JSR AdjustBirdYBasedOnAltitude                     ; Fine-tune Y velocity for leaving
  JMP UpdatePterryTargetingAndVerticalVelocity_Main  ; Further Y velocity adjustments and animation

CheckPterryState_NormalOrCharging
  CMP #PterryState_Normal            ; If Pterry is normal do his regular logic
  BEQ HandlePterryNormalStateLogic

                                     ; Pterry must be in Charging state ($0B)
  LDA GameObjectChargingTimer,X      ; Check charging timer
  BEQ PterryChargeTimerExpired
  DEC GameObjectChargingTimer,X      ; Decrement charging timer
  JMP ExitPterryProcessing_NoAction  ; Continue charging

PterryChargeTimerExpired
  LDA #PterryState_Normal            ; Charge finished, switch to normal state
  STA GameObjectState,X
  LDA #$FA                           ; Reset timer for next charge cycle (long delay)
  STA GameObjectChargingTimer,X

; Set velocity back to normal Pterry speed
  LDA GameObjectDirection,X
  BPL SetPterryNormalVelocity_RightFacing
  LDA #$00                           ; Why? This could have just been LDA #$FD. :|
  SEC                                ; Maybe the velocity was originally a variable
  SBC #$03                           ; instead of a constant.
  JMP SetPterryNormalVelocity_Store
SetPterryNormalVelocity_RightFacing
  LDA #$03  ; Normal speed right
SetPterryNormalVelocity_Store
  STA GameObjectXVelocity,X
ExitPterryProcessing_NoAction
  RTS

HandlePterryNormalStateLogic
  LDA GameObjectChargingTimer,X      ; Timer before Pterry starts charging
  BEQ PterryStartChargingSequence
  DEC GameObjectChargingTimer,X
  JMP ProcessPterryAIAndAnimation

PterryStartChargingSequence
  LDA #SFX_PterryCharging
  JSR ScheduleSFX
  LDA #PterryState_Charging          ; Switch to charging state
  STA GameObjectState,X
  LDA #$02                           ; Initial animation frame for charging
  STA GameObjectAnimationFrame,X
  LDA #$03                           ; Initial animation counter for charging
  STA GameObjectAnimationCounter,X
  LDA #$00                           ; Zero out Y velocity when starting charge
  STA GameObjectYVelocityHi,X
  STA GameObjectYVelocityLo,X
  LDA #$5A                           ; Duration of the charge
  STA GameObjectChargingTimer,X
                                     ; Fall through to SetPterryVelocityForChargingScreen

SetPterryVelocityForChargingScreen
  LDA GameObjectDirection,X
  BPL SetPterryChargeVelocity_RightFacing
  LDA #$00                           ; Again, this could have been LDA #$FB
  SEC                                ; since all of the values are constants,
  SBC #$05                           ; instead of subtracting.
  JMP SetPterryChargeVelocity_Store
SetPterryChargeVelocity_RightFacing
  LDA #$05  ; Charge speed right
SetPterryChargeVelocity_Store
  STA GameObjectXVelocity,X
  RTS

ProcessPterryAIAndAnimation
  LDA GameObjectAITimerLo,X               ; Low byte of AI decision timer
  BEQ PterryAITimerExpired_MakeDecision
  DEC GameObjectAITimerLo,X
  LDA PterryYVelocityZero                 ; Set Y velocity to 0 (ROM const $00)
  JMP UpdatePterryTargetingAndVerticalVelocity_Main

PterryAITimerExpired_MakeDecision
  LDA #$02                                ; Reset AI action timer low byte
  STA GameObjectAITimerLo,X
  JSR FindBestTargetForGameObject         ; Find a player target
  LDY GameObjectPreyIndex,X               ; Y = index of targeted player
  BMI PterryNoValidTarget_AdjustYVel      ; If no target (or target invalid), just adjust Y based on altitude

                                          ; Target acquired, adjust Y velocity to intercept
  LDA GameObjectYCoordHi,Y                ; Target's Y
  CLC
  ADC #$02                                ; Add slight offset to target Y (aim slightly below target)
  CMP GameObjectYCoordHi,X                ; Compare with Pterry's Y
  BCS PterryTargetIsLowerOrSame_FlyDown   ; If Pterry is above target (TargetY >= PterryY), fly down

                                          ; Pterry is below target, needs to fly up
  CLC
  ADC #$01                                ; Add a bit more to make comparison for "slightly up"
  CMP GameObjectYCoordHi,X
  BCC PterryTargetIsHigher_FlyUpSlightly  ; If Pterry is still significantly below, fly up slightly

; Pterry is very close vertically, fly level
  LDA PterryYVelocityZero
  JMP UpdatePterryTargetingAndVerticalVelocity_Main

PterryNoValidTarget_AdjustYVel            ; No target, or lost target
  JSR AdjustBirdYBasedOnAltitude          ; Adjust Y based on general altitude zones
  JMP UpdatePterryTargetingAndVerticalVelocity_Main

PterryTargetIsHigher_FlyUpSlightly
  LDA PterryYVelocitySlightUp             ; Set Y velocity to slight up (ROM const $FF)
  JMP UpdatePterryTargetingAndVerticalVelocity_Main

PterryTargetIsLowerOrSame_FlyDown
  LDA PterryYVelocitySlightDown           ; Set Y velocity to slight down (ROM const $01)
                                          ; Fall through to UpdatePterryTargetingAndVerticalVelocity_Main

UpdatePterryTargetingAndVerticalVelocity_Main
  STA GameObjectYVelocityHi,X             ; Store determined Y velocity

  LDA GameObjectAnimationDelay,X          ; Check animation delay timer
  BEQ PterryAnimationDelay_Expired
  DEC GameObjectAnimationDelay,X
  RTS

PterryAnimationDelay_Expired
  LDY GameObjectAnimationCounter,X     ; Get current animation sequence counter
  LDA PterryFrameIndexAdjustmentLUT,Y  ; Get frame adjustment based on counter
  CLC
  ADC GameObjectAnimationFrame,X       ; Add to current animation frame
  STA GameObjectAnimationFrame,X       ; Store new frame
  DEC GameObjectAnimationCounter,X     ; Decrement animation sequence counter
  BPL SkipPterryAnimCounterWrap
  LDA #$03                             ; Wrap counter if it went negative
  STA GameObjectAnimationCounter,X
SkipPterryAnimCounterWrap
  LDA #$04                             ; Reset animation delay timer
  STA GameObjectAnimationDelay,X
  RTS

;------------------------------------------------------------------------------
; Player vs Pterodactyl Collision
; Input: X = Player index
;------------------------------------------------------------------------------
HandlePlayerVsPterry
  LDY #$0C
PlayerVsPterryCollisionLoop
  LDA.w GameObjectState,Y
  BEQ CheckNextPterryForCollision
  CMP #$0D
  BEQ CheckNextPterryForCollision
  JSR GameObjCollisionDetection  ; returns carry is clear on collision
  BCC PlayerVsPterryDetermineJoustWinner
CheckNextPterryForCollision
  DEY
  CPY #$0A
  BCS PlayerVsPterryCollisionLoop
  RTS

PlayerVsPterryDetermineJoustWinner
; Collision occurred. Check vertical alignment to see who wins.
  LDA GameObjectYCoordHi,Y
  STA Temp5E
  DEC Temp5E
  DEC Temp5E
  LDA GameObjectYCoordHi,X
  CMP Temp5E
  BEQ PterryAndPlayerSameHeight  ; Player is slightly above Pterry
  INC Temp5E
  CMP Temp5E
  BEQ PterryAndPlayerSameHeight  ; Player is level with Pterry
  INC Temp5E
  CMP Temp5E
  BEQ PterryAndPlayerSameHeight  ; Player is slightly below Pterry
  JMP StartPlayerDeath           ; Player is too low, player dies

PterryAndPlayerSameHeight
  LDA GameObjectDirection,X
  CMP GameObjectDirection,Y
  BNE PlayerJoustedPterry
  JMP StartPlayerDeath           ; Player wasn't facing pterry, player dies

PlayerJoustedPterry
  LDA GameObjectDirection,X
  EOR #$FF
  CLC
  ADC #$01
  STA GameObjectDirection,X
  LDA #$00
  STA GameObjectXVelocity,X
  STA GameObjectXVelocity,Y
  LDA #SFX_PterryDefeated
  JSR ScheduleSFX
  LDA #$0D
  STA.w GameObjectState,Y
  LDA #$02
  STA GameObjectYCoordLo,Y
  LDA #$0A
  STA GameObjectXCoordLo,Y
  LDA #$03
  STA GameObjectAnimationFrame,Y
  LDA #$01
  STA Temp61ScoreAdd
  LDA #$00
  STA Temp62ScoreAdd
  JMP UpdatePlayerScoreAndBonus

;------------------------------------------------------------------------------
; SetAnyPterrysToLeaving
; Sets the state of all active Pterodactyls to "Leaving" ($0C).
;------------------------------------------------------------------------------
SetAnyPterrysToLeaving
  LDY #$0C
  STY PterryCountdownHi
SetAnyPterrysToLeavingLoop
  LDA.w GameObjectState,Y
  BEQ SkipPterryLeavingUpdate  ; skip if pterry is dead and gone
  CMP #$0D
  BEQ SkipPterryLeavingUpdate  ; skip if pterry is offscreen
  LDA #$0C
  STA.w GameObjectState,Y
SkipPterryLeavingUpdate
  DEY
  CPY #$0A
  BCS SetAnyPterrysToLeavingLoop
  RTS

;------------------------------------------------------------------------------
; Collision Detection Data
; These LUTs define parameters for the simplified collision detection.
;------------------------------------------------------------------------------
ObjectOffsetXLUT
 .byte $04,$04,$04,$04,$04,$04,$04,$04,$04,$04          ; birds
 .byte $07,$07,$07                                      ; pterrys
 .byte $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02  ; eggs

ObjectOffsetYLUT
 .byte $05,$06,$05,$05,$05,$05,$05,$05,$05,$05          ; birds
 .byte $05,$05,$05                                      ; pterrys
 .byte $03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03  ; eggs

ObjectCollisionWidthLUT
 .byte $07,$07,$07,$07,$07,$07,$07,$07,$07,$07          ; birds
 .byte $0C,$0C,$0C                                      ; pterrys
 .byte $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05  ; eggs

ObjectCollisionHeightLUT
 .byte $09,$0B,$09,$09,$09,$09,$09,$09,$09,$09          ; birds
 .byte $09,$09,$09                                      ; pterrys
 .byte $07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07  ; eggs


;------------------------------------------------------------------------------
; GameObjCollisionDetection
; Performs a simplified Axis-Aligned Bounding Box (AABB) collision check
; between two game objects.
; Input: X = Index of the first game object.
;        Y = Index of the second game object.
; Output: Carry Flag: SET if NO collision, CLEAR if collision.
;         TempCollisionOverlap holds absolute Y-distance on collision.
;------------------------------------------------------------------------------
GameObjCollisionDetection
; Calculate Effective X coordinate for Object X
  LDA GameObjectXCoordHi,X        ; Load ObjX's base X coord
  CLC
  ADC ObjectOffsetXLUT,X          ; Add ObjX's X offset
  STA Temp5E                      ; Store EffX_ObjX in Temp5E

; Calculate absolute difference between Effective X of ObjX and Effective X of ObjY
  LDA GameObjectXCoordHi,Y        ; Load ObjY's base X coord
  CLC
  ADC ObjectOffsetXLUT,Y          ; Add ObjY's X offset (EffX_ObjY)
  SEC                             ; Prepare for subtraction
  SBC Temp5E                      ; A = EffX_ObjY - EffX_ObjX
  BPL CalculateAbsX_IsPositive    ; If result is positive, it's the absolute difference
  EOR #$FF                        ; Negate the result (part 1)
  CLC
  ADC #$01                        ; Negate the result (part 2, to get absolute value)
CalculateAbsX_IsPositive
  STA Temp66                      ; Store absolute X-distance in Temp66

; Check for X-axis overlap using Object Y's collision width threshold
  CMP ObjectCollisionWidthLUT,Y   ; Compare abs X-distance with ObjY's collision width threshold
  BCS NoCollisionDetected_Return  ; If DistX >= Width_Y, no X-overlap. Set Carry and RTS.

; X-axis overlaps, now check Y-axis
; Calculate Effective Y coordinate for Object X
  LDA GameObjectYCoordHi,X        ; Load ObjX's base Y coord
  CLC
  ADC ObjectOffsetYLUT,X          ; Add ObjX's Y offset
  STA Temp5E                      ; Store EffY_ObjX in Temp5E

; Calculate absolute difference between Effective Y of ObjX and Effective Y of ObjY
  LDA GameObjectYCoordHi,Y        ; Load ObjY's base Y coord
  CLC
  ADC ObjectOffsetYLUT,Y          ; Add ObjY's Y offset (EffY_ObjY)
  SEC                             ; Prepare for subtraction
  SBC Temp5E                      ; A = EffY_ObjY - EffY_ObjX
  BPL CalculateAbsY_IsPositive    ; If positive, it's the absolute difference
  EOR #$FF                        ; Negate (part 1)
  CLC
  ADC #$01                        ; Negate (part 2, to get absolute value)
CalculateAbsY_IsPositive
  STA TempCollisionOverlap        ; Store absolute Y-distance

; Special collision height check if Object Y is Player 1
  CPY #PLAYER1_OBJECT_INDEX            ; Is Object Y Player 1?
  BEQ CheckCollision_Player1_Y_Height  ; Yes, branch to P1 specific height check

; Object Y is not Player 1, use its standard collision height from LUT
  CMP ObjectCollisionHeightLUT,Y  ; Compare abs Y-distance with ObjY's collision height threshold
; Carry will be CLEAR if DistY < Height_Y (collision)
; Carry will be SET if DistY >= Height_Y (no collision)
; Fall through to RTS. If BCS was taken (no collision), Carry is SET.
; If CMP results in DistY < Height_Y, Carry is CLEAR (collision).

NoCollisionDetected_Return
  RTS  ; Return with Carry SET (no collision) or CLEAR (collision)

CheckCollision_Player1_Y_Height
; Player 1 specific Y-collision height check (functionally same as LUT for P1)
  CMP #PLAYER1_COLLISION_HEIGHT_SPECIAL  ; Compare abs Y-distance with P1's specific height value
; Carry will be CLEAR if DistY < Value (collision)
; Carry will be SET if DistY >= Value (no collision)
  RTS ; Return with Carry status from CMP


;------------------------------------------------------------------------------
; ESRoutineScheduler
; Event Scheduler main loop. Iterates through 18 event slots.
; If an event's index is positive, it executes the corresponding routine.
; If an event's index is negative, it treats it as a countdown timer. When the
; timer reaches zero, the index is made positive so it will run on the next pass.
;------------------------------------------------------------------------------
ESRoutineScheduler
  INC IncrementingRandSeed
  LDX #$11
ESRoutineSchedulerLoop
  LDA ESRoutineIndex,X
  BEQ ESRoutineSchedulerLoopBottom     ; if index=0 handler is off
  BMI UpdateEventTimers                ; if index<0 update this timer
                                       ; if index>0 run this routine
  ASL
  TAY                                  ; y=index*2 (table has sequential lo+hi pairs)
  LDA ESRoutinesLo,Y
  STA TempPoint5ACurrentESRoutineLo
  LDA ESRoutinesHi,Y
  STA TempPoint5ACurrentESRoutineHi
  JMP (TempPoint5ACurrentESRoutineLo)

UpdateEventTimers
  DEC ESRoutineTimer,X
  BNE EventTimerStillCountingDown
; Once the timer expires, we invert the function index to turn off
; the timer. The next time the handler loop is called, it will run.
  LDA ESRoutineIndex,X
  EOR #$FF
  STA ESRoutineIndex,X
  INC ESRoutineIndex,X
EventTimerStillCountingDown
  JMP ESRoutineSchedulerLoopBottom

ESScheduleNextRoutine
; Common endpoint for many ES Jump table functions. Sets/Resets timer
; and advances and inverts the index. (index=0-index+1) 
  STA ESRoutineTimer,X
  LDA ESRoutineIndex,X
  EOR #$FF
ESUpdateRoutineIndex
  STA ESRoutineIndex,X
ESRoutineSchedulerLoopBottom
  DEX
  BPL ESRoutineSchedulerLoop
  RTS

ESCancelRoutine
; Cancels the currently running event by setting its index to 0.
  LDA #$00
  BEQ ESUpdateRoutineIndex

;------------------------------------------------------------------------------
; Event Scheduler Routine Jump Table
;------------------------------------------------------------------------------
ESRoutinesLo              ; Event Timer jump table
 .byte <ESGameColdInit    ; event index $00
ESRoutinesHi
 .byte >ESGameColdInit

 .byte <ESSetPointedValueFromRandom  ; event index $01
 .byte >ESSetPointedValueFromRandom

 .byte <ES_AdvanceLavaSparkleFrame   ; event index $02
 .byte >ES_AdvanceLavaSparkleFrame

 .byte <ES_ReverseLavaSparkleFrame   ; event index $03
 .byte >ES_ReverseLavaSparkleFrame

 .byte <ES_SetRandomLavaSparkleFrameAndCancel  ; event index $04
 .byte >ES_SetRandomLavaSparkleFrameAndCancel

 .byte <ESSetupFlameAnimation     ; event index $05
 .byte >ESSetupFlameAnimation

 .byte <ESDoFlameAnimation        ; event index $06
 .byte >ESDoFlameAnimation

 .byte <ESSetupBridgeBurning      ; event index $07
 .byte >ESSetupBridgeBurning

 .byte <ESAnimateBridgeBurning    ; event index $08
 .byte >ESAnimateBridgeBurning

 .byte <ESAnimateBridgeReforming  ; event index $09
 .byte >ESAnimateBridgeReforming

 .byte <ESWave1StartRoutines      ; event index $0A 
 .byte >ESWave1StartRoutines

 .byte <ESRaiseLavaWave1          ; event index $0B
 .byte >ESRaiseLavaWave1

 .byte <ESWave2StartRoutines      ; event index $0C
 .byte >ESWave2StartRoutines

 .byte <ESRaiseLavaWave2          ; event index $0D
 .byte >ESRaiseLavaWave2

 .byte <ESWave3StartRoutines      ; event index $0E
 .byte >ESWave3StartRoutines

 .byte <ESRaiseLavaWave3          ; event index $0F
 .byte >ESRaiseLavaWave3

 .byte <ES_StartEndOfWaveBonusDisplayTimer ; event index $10
 .byte >ES_StartEndOfWaveBonusDisplayTimer

 .byte <ESTransitionToNextLevel            ; event index $11
 .byte >ESTransitionToNextLevel

 .byte <ES_StartBuzzardBaitWarningDelay    ; event index $12
 .byte >ES_StartBuzzardBaitWarningDelay

 .byte <ESShowBuzzardBaitText              ; event index $13
 .byte >ESShowBuzzardBaitText

 .byte <ES_StartPreWaveEnemySpawnDelay     ; event index $14
 .byte >ES_StartPreWaveEnemySpawnDelay

 .byte <ESIfEggWaveDoSetup                 ; event index $15
 .byte >ESIfEggWaveDoSetup

 .byte <UNUSED_ES_AdvanceBottomPlatformDissolve  ; event index $16
 .byte >UNUSED_ES_AdvanceBottomPlatformDissolve

 .byte <UNUSED_ES_AdvanceBottomPlatformDissolve  ; event index $17
 .byte >UNUSED_ES_AdvanceBottomPlatformDissolve

 .byte <UNUSED_ES_AdvanceBottomPlatformDissolve  ; event index $18
 .byte >UNUSED_ES_AdvanceBottomPlatformDissolve

 .byte <UNUSED_ES_AdvanceBottomPlatformDissolve  ; event index $19
 .byte >UNUSED_ES_AdvanceBottomPlatformDissolve

 .byte <UNUSED_ES_SetBottomPlatformDissolveDelay ; event index $1A
 .byte >UNUSED_ES_SetBottomPlatformDissolveDelay

 .byte <UNUSED_ES_CleanupBottomPlatformDissolve  ; event index $1B
 .byte >UNUSED_ES_CleanupBottomPlatformDissolve

; this sequence of functions does the top center platform (4) dissolve animation...

 .byte <ESShakePlatform4Left   ; event index $1C 
 .byte >ESShakePlatform4Left

 .byte <ESShakePlatform4Right  ; event index $1D
 .byte >ESShakePlatform4Right

 .byte <ESShakePlatform4Left   ; event index $1E
 .byte >ESShakePlatform4Left

 .byte <ESShakePlatform4Right  ; event index $1F
 .byte >ESShakePlatform4Right

 .byte <ESShakePlatform4Left   ; event index $20
 .byte >ESShakePlatform4Left

 .byte <ESShakePlatform4Right  ; event index $21
 .byte >ESShakePlatform4Right

 .byte <ESShakePlatform4Left   ; event index $22
 .byte >ESShakePlatform4Left

 .byte <ESShakePlatform4Right  ; event index $23
 .byte >ESShakePlatform4Right

 .byte <ESStartPlatform4DissolveVisuals       ; event index $24
 .byte >ESStartPlatform4DissolveVisuals

 .byte <ESUpdatePlatform4DissolveParticles    ; event index $25
 .byte >ESUpdatePlatform4DissolveParticles

 .byte <ESUpdatePlatform4DissolveParticles    ; event index $26
 .byte >ESUpdatePlatform4DissolveParticles

 .byte <ESUpdatePlatform4DissolveParticles    ; event index $27
 .byte >ESUpdatePlatform4DissolveParticles

 .byte <AdvancePlatform4DissolveFrame         ; event index $28
 .byte >AdvancePlatform4DissolveFrame

 .byte <ESUpdatePlatform4DissolveParticles    ; event index $29
 .byte >ESUpdatePlatform4DissolveParticles

 .byte <ESUpdatePlatform4DissolveParticles    ; event index $2A
 .byte >ESUpdatePlatform4DissolveParticles

 .byte <ESUpdatePlatform4DissolveParticles    ; event index $2B
 .byte >ESUpdatePlatform4DissolveParticles

 .byte <ESDissolvingPlatformAnimationCleanup  ; event index $2C
 .byte >ESDissolvingPlatformAnimationCleanup

; this sequence of functions does the top side-wrapping platform (5+6) dissolve animation...
 .byte <ESShakePlatforms56Left   ; event index $2D
 .byte >ESShakePlatforms56Left

 .byte <ESShakePlatforms56Right  ; event index $2E
 .byte >ESShakePlatforms56Right

 .byte <ESShakePlatforms56Left   ; event index $2F
 .byte >ESShakePlatforms56Left

 .byte <ESShakePlatforms56Right  ; event index $30
 .byte >ESShakePlatforms56Right

 .byte <ESShakePlatforms56Left   ; event index $31
 .byte >ESShakePlatforms56Left

 .byte <ESShakePlatforms56Right  ; event index $32
 .byte >ESShakePlatforms56Right

 .byte <ESShakePlatforms56Left   ; event index $33
 .byte >ESShakePlatforms56Left

 .byte <ESShakePlatforms56Right  ; event index $34
 .byte >ESShakePlatforms56Right

 .byte <ESStartPlatforms56DissolveVisuals     ; event index $35
 .byte >ESStartPlatforms56DissolveVisuals

 .byte <ESUpdatePlatforms56DissolveParticles  ; event index $36
 .byte >ESUpdatePlatforms56DissolveParticles

 .byte <ESUpdatePlatforms56DissolveParticles  ; event index $37
 .byte >ESUpdatePlatforms56DissolveParticles

 .byte <ESUpdatePlatforms56DissolveParticles  ; event index $38
 .byte >ESUpdatePlatforms56DissolveParticles

 .byte <ESAdvancePlatforms56DissolveFrame     ; event index $39
 .byte >ESAdvancePlatforms56DissolveFrame

 .byte <ESUpdatePlatforms56DissolveParticles  ; event index $3A
 .byte >ESUpdatePlatforms56DissolveParticles

 .byte <ESUpdatePlatforms56DissolveParticles  ; event index $3B
 .byte >ESUpdatePlatforms56DissolveParticles

 .byte <ESUpdatePlatforms56DissolveParticles  ; event index $3C
 .byte >ESUpdatePlatforms56DissolveParticles

 .byte <ESDissolvingPlatformAnimationCleanup  ; event index $3D
 .byte >ESDissolvingPlatformAnimationCleanup

; this sequence of functions does the low center platform (0) dissolve animation...
 .byte <ESShakePlatform0Left   ; event index $3E
 .byte >ESShakePlatform0Left

 .byte <ESShakePlatform0Right  ; event index $3F
 .byte >ESShakePlatform0Right

 .byte <ESShakePlatform0Left   ; event index $40
 .byte >ESShakePlatform0Left

 .byte <ESShakePlatform0Right  ; event index $41
 .byte >ESShakePlatform0Right

 .byte <ESShakePlatform0Left   ; event index $42
 .byte >ESShakePlatform0Left

 .byte <ESShakePlatform0Right  ; event index $43
 .byte >ESShakePlatform0Right

 .byte <ESShakePlatform0Left   ; event index $44
 .byte >ESShakePlatform0Left

 .byte <ESShakePlatform0Right  ; event index $45
 .byte >ESShakePlatform0Right

 .byte <ESStartPlatform0DissolveVisuals       ; event index $46
 .byte >ESStartPlatform0DissolveVisuals

 .byte <ESUpdatePlatform0DissolveParticles    ; event index $47
 .byte >ESUpdatePlatform0DissolveParticles

 .byte <ESUpdatePlatform0DissolveParticles    ; event index $48
 .byte >ESUpdatePlatform0DissolveParticles

 .byte <ESUpdatePlatform0DissolveParticles    ; event index $49
 .byte >ESUpdatePlatform0DissolveParticles

 .byte <ESAdvancePlatform0DissolveFrame       ; event index $4A
 .byte >ESAdvancePlatform0DissolveFrame

 .byte <ESUpdatePlatform0DissolveParticles    ; event index $4B
 .byte >ESUpdatePlatform0DissolveParticles

 .byte <ESUpdatePlatform0DissolveParticles    ; event index $4C
 .byte >ESUpdatePlatform0DissolveParticles

 .byte <ESUpdatePlatform0DissolveParticles    ; event index $4D
 .byte >ESUpdatePlatform0DissolveParticles

 .byte <ESDissolvingPlatformAnimationCleanup  ; event index $4E
 .byte >ESDissolvingPlatformAnimationCleanup

UNUSED_ES_AdvanceBottomPlatformDissolve
  INC SpawnPlatformDissolved1,X
UNUSED_ES_SetBottomPlatformDissolveDelay
  LDA #$04
  JMP ESScheduleNextRoutine

UNUSED_ES_CleanupBottomPlatformDissolve
  LDA #$00
  STA SpawnPlatformDissolved1,X
  JMP ESCancelRoutine

ES_StartPreWaveEnemySpawnDelay
  LDA #$50
  JMP ESScheduleNextRoutine

ESIfEggWaveDoSetup
  TXA
  PHA
  TYA
  PHA
  JSR InitializeWave
  LDA IsEggWave
  BEQ EggWaveSetupDone
  JSR SetupEggWave
EggWaveSetupDone
  PLA
  TAY
  PLA
  TAX
  JMP ESCancelRoutine

ESDissolvingPlatformAnimationCleanup
  LDA #$00
  LDY #$05
PlatformAnimationCleanupLoop
  STA PlatformDissolveAnimationFlag0,Y  ; reset all 6 flags
  DEY
  BPL PlatformAnimationCleanupLoop
  STA ESRoutineIndex,X
  JMP ESRoutineSchedulerLoopBottom

; Platforms by number:
; 0: Bottom center
; 1: Bottom left (wraps)
; 2: Bottom right (wraps)
; 3: Mid-level platform
; 4: Top center
; 5: Top left (wraps)
; 6: Top right (wraps)

ESShakePlatform4Left
  DEC Platform4XCoordinate
  DEC SpawnPoint4XCoordinate
  LDA #$04
  JMP ESScheduleNextRoutine
ESShakePlatform4Right
  INC Platform4XCoordinate
  INC SpawnPoint4XCoordinate
  LDA #$04
  JMP ESScheduleNextRoutine
ESStartPlatform4DissolveVisuals
  LDA #$00
  STA Platform4PaletteWidth
  STA Platform4GfxLo
  LDA #$01
  STA PlatformDissolveAnimationFlag0
  STA PlatformDissolveAnimationFlag1
  LDA #$32
  STA PlatformDissolveAnimationXCoord0
  LDA #$3E
  STA PlatformDissolveAnimationXCoord1
  LDA #$41
  STA PlatformDissolveAnimationYCoord0
  STA PlatformDissolveAnimationYCoord1
  LDA #$02
  JMP ESScheduleNextRoutine
ESUpdatePlatform4DissolveParticles
  INC PlatformDissolveAnimationXCoord0
  DEC PlatformDissolveAnimationXCoord1
  INC PlatformDissolveAnimationYCoord0
  INC PlatformDissolveAnimationYCoord1
  LDA #$02
  JMP ESScheduleNextRoutine
AdvancePlatform4DissolveFrame
  INC PlatformDissolveAnimationFlag0
  INC PlatformDissolveAnimationFlag1
  LDA #$02
  JMP ESScheduleNextRoutine

ESShakePlatforms56Left      ; this label is misleading, but named to match the existing pattern
                            ; The two platform halves shake by moving in and out.
  DEC Platform5XCoordinate
  INC Platform6XCoordinate
  LDA #$04
  JMP ESScheduleNextRoutine

ESShakePlatforms56Right
  INC Platform5XCoordinate
  DEC Platform6XCoordinate
  LDA #$04
  JMP ESScheduleNextRoutine

ESStartPlatforms56DissolveVisuals
  LDA #$00
  STA Platform56PaletteWidth
  STA Platform56GfxLo
  LDA #$01
  STA PlatformDissolveAnimationFlag2
  STA PlatformDissolveAnimationFlag3
  LDA #$90
  STA PlatformDissolveAnimationXCoord2
  LDA #$02
  STA PlatformDissolveAnimationXCoord3
  LDA #$39
  STA PlatformDissolveAnimationYCoord2
  STA PlatformDissolveAnimationYCoord3
  LDA #$02
  JMP ESScheduleNextRoutine
ESUpdatePlatforms56DissolveParticles
  INC PlatformDissolveAnimationXCoord2
  DEC PlatformDissolveAnimationXCoord3
  INC PlatformDissolveAnimationYCoord2
  INC PlatformDissolveAnimationYCoord3
  LDA #$02
  JMP ESScheduleNextRoutine
ESAdvancePlatforms56DissolveFrame
  INC PlatformDissolveAnimationFlag2
  INC PlatformDissolveAnimationFlag3
  LDA #$02
  JMP ESScheduleNextRoutine
ESShakePlatform0Left
  DEC Platform0XCoordinate
  LDA #$04
  JMP ESScheduleNextRoutine
ESShakePlatform0Right
  INC Platform0XCoordinate
  LDA #$04
  JMP ESScheduleNextRoutine
ESStartPlatform0DissolveVisuals
  LDA #$00
  STA Platform0PaletteWidth
  STA Platform0GfxLo
  LDA #$01
  STA PlatformDissolveAnimationFlag4
  STA PlatformDissolveAnimationFlag5
  LDA #$39
  STA PlatformDissolveAnimationXCoord4
  LDA #$45
  STA PlatformDissolveAnimationXCoord5
  LDA #$81
  STA PlatformDissolveAnimationYCoord4
  STA PlatformDissolveAnimationYCoord5
  LDA #$02
  JMP ESScheduleNextRoutine
ESUpdatePlatform0DissolveParticles
  INC PlatformDissolveAnimationXCoord4
  DEC PlatformDissolveAnimationXCoord5
  INC PlatformDissolveAnimationYCoord4
  INC PlatformDissolveAnimationYCoord5
  LDA #$02
  JMP ESScheduleNextRoutine

ESAdvancePlatform0DissolveFrame
  INC PlatformDissolveAnimationFlag4
  INC PlatformDissolveAnimationFlag5
  LDA #$02
  JMP ESScheduleNextRoutine

ES_StartBuzzardBaitWarningDelay
  LDA #$1E
  JMP ESScheduleNextRoutine

ESShowBuzzardBaitText
  TXA                    ; save X and Y
  PHA
  TYA
  PHA
  LDA #$02               ; "buzzard bait"
  JSR DisplayGameMessage
  PLA                    ; restore X and Y
  TAY
  PLA
  TAX
  JMP ESCancelRoutine

ES_StartEndOfWaveBonusDisplayTimer
  LDA #$46
  JMP ESScheduleNextRoutine

ESTransitionToNextLevel
  LDA PterryState1
  ORA PterryState2
  ORA PterryState3
  BEQ NoPterrysAlive2
  JSR SetAnyPterrysToLeaving
  DEC ESRoutineIndex,X       ; Change event to the event above this one
  LDA #$05
  JMP ESScheduleNextRoutine

NoPterrysAlive2
  TXA
  PHA
  TYA
  PHA
  JSR DisplayLevelStartMsg
  PLA
  TAY
  PLA
  TAX
  JMP ESCancelRoutine

ESSetupBridgeBurning
  LDA #$1E
  STA BridgeBurningWidth
  LDA #$05                  ; schedule flame sprite frame animation
  STA ESRoutineIndex+8
  LDA #$1E
  STA $6E
  LDA #$14
  LDY #$00
  JMP ESScheduleNextRoutine

ESAnimateBridgeBurning
  INC BridgeBurningWidth
  DEC $6E
  BMI ESAnimateBridgeBurningChangeState
  INC BridgeLeftGfxByte1
  INC BridgeLeftGfxByte3
  INC BridgeLeftGfxByte2
  DEC BridgeRightGfxByte1
  DEC BridgeRightGfxByte3
  DEC BridgeRightGfxByte2
ESBridgeAnimationScheduleNextFrame
  DEC ESRoutineIndex,X
  LDA #$0F
  JMP ESScheduleNextRoutine

ESAnimateBridgeBurningChangeState
  LDA #$10
  STA $6E
  LDA #$0F
  JMP ESScheduleNextRoutine
ESAnimateBridgeReforming
  DEC $6E
  BMI FinishBridgeReforming
  DEC BridgeLeftGfxByte1
  DEC BridgeLeftGfxByte3
  INC BridgeRightGfxByte1
  INC BridgeRightGfxByte3
  JMP ESBridgeAnimationScheduleNextFrame
FinishBridgeReforming
  JMP ESCancelRoutine

TopFlameAnimationGfxLoLUT
 .byte $3A, $38, $36, $2E, $2C, $2A, $28, $26
 .byte $24, $22, $20, $34, $32, $30, $2E, $2C
 .byte $2A, $28, $26, $24, $22, $20 

BottomFlameAnimationGfxLoLUT
 .byte $56, $54, $52, $4A, $48, $46, $44, $42 
 .byte $40, $3E, $3C, $50, $4E, $4C, $4A, $48 
 .byte $46, $44, $42, $40, $3E, $3C 

ESSetupFlameAnimation               ; event index $05
  LDA #$15
  STA LeftFlameAnimationIndex
  LDA #$06
  STA RightFlameAnimationIndex
  LDA #$01
  JMP ESScheduleNextRoutine

ESDoFlameAnimation                  ; event index $06
  LDY LeftFlameAnimationIndex
  LDA TopFlameAnimationGfxLoLUT,Y
  STA $1F50
  LDA BottomFlameAnimationGfxLoLUT,Y
  STA $1FA6
  LDY RightFlameAnimationIndex
  LDA TopFlameAnimationGfxLoLUT,Y
  STA $1F4C
  LDA BottomFlameAnimationGfxLoLUT,Y
  STA $1FA2

  DEC LeftFlameAnimationIndex
  BPL SkipWrapFixLeftFlameIndex
  LDA #$15
  STA LeftFlameAnimationIndex
SkipWrapFixLeftFlameIndex
  DEC RightFlameAnimationIndex
  BPL SkipWrapFixRightFlameIndex
  LDA #$15
  STA RightFlameAnimationIndex
SkipWrapFixRightFlameIndex
  LDA #$01
  DEC ESRoutineIndex,X
  JMP ESScheduleNextRoutine

ESWave1StartRoutines       ; event index $0A
  LDA #$06
SetValueThenESScheduleNextRoutine
  STA ESTemp6ELavaRiseCounter
  LDA #$0F
  JMP ESScheduleNextRoutine

ESWave2StartRoutines
  LDA #$FF
  STA $1FC4
  LDA #$7C
  STA $1FC8
  LDA #$06
  JMP SetValueThenESScheduleNextRoutine

ESWave3StartRoutines
  LDA #$FF
  STA $1FB1
  LDA #$7C
  STA $1FAD
  LDA #$02
  JMP SetValueThenESScheduleNextRoutine

ESRaiseLavaWave1
  DEC LeftLavaLevelWave1
  DEC RightLavaLevelWave1
  DEC ESTemp6ELavaRiseCounter
  BMI DoneRisingLavaWave1
ScheduleThisRoutineAgainAfterDelay
  LDA #$0F                   ; Number of ticks to wait before running.
  DEC ESRoutineIndex,X       ; Decrement this timer so the "next"
  JMP ESScheduleNextRoutine  ; routine is this one again.
DoneRisingLavaWave1
  LDA #$01
  STA ESRoutineIndex+3
  STA ESRoutineIndex+4
  JMP ESCancelRoutine        ; We're done

ESRaiseLavaWave2
  DEC LeftLavaLevelWave2
  DEC RightLavaLevelWave2
  DEC ESTemp6ELavaRiseCounter
  BMI DoneRisingLavaWave2
  JMP ScheduleThisRoutineAgainAfterDelay
DoneRisingLavaWave2
  LDA #$01
  STA ESRoutineIndex+1
  STA ESRoutineIndex+2
  JMP ESCancelRoutine
ESRaiseLavaWave3
  DEC LeftLavaLevelWave3
  DEC RightLavaLevelWave3
  DEC ESTemp6ELavaRiseCounter
  BMI DoneRisingLavaWave3
  JMP ScheduleThisRoutineAgainAfterDelay

DoneRisingLavaWave3
  LDA #$01
  STA ESRoutineIndex
  LDA #$07              ; Schedule the ESSetupBridgeBurning routine
  STA ESRoutineIndex+7  ; in 8th slot. (8th slot significance unknown)
  JMP ESCancelRoutine

ESSetPointedValueFromRandom
  JSR ES_LoadLavaSparkleGfxPtr
  JSR ReturnRandInA
ClampRandomValueLoop
  CMP #$20
  BCC StoreRandomValueAndScheduleNext
  CMP #$79
  BCC ContinueClampingRandomValue
  CMP #$99
  BCC StoreRandomValueAndScheduleNext
ContinueClampingRandomValue
  SBC #$3B
  JMP ClampRandomValueLoop
StoreRandomValueAndScheduleNext
  LDY #$03
  STA (TempPoint5ACurrentESRoutineLo),Y
  LDA #$08
  JMP ESScheduleNextRoutine
ES_AdvanceLavaSparkleFrame
  JSR ES_LoadLavaSparkleGfxPtr
  LDY #$00
  LDA (TempPoint5ACurrentESRoutineLo),Y
  CLC
  ADC #$01
  STA (TempPoint5ACurrentESRoutineLo),Y
  LDA #$06
  JMP ESScheduleNextRoutine
ES_ReverseLavaSparkleFrame
  JSR ES_LoadLavaSparkleGfxPtr
  LDY #$00
  LDA (TempPoint5ACurrentESRoutineLo),Y
  SEC
  SBC #$01
  STA (TempPoint5ACurrentESRoutineLo),Y
  CPX #$00
  BNE ScheduleNextESRoutineShortDelay
  LDA #$B0
  LDY #$03
  STA (TempPoint5ACurrentESRoutineLo),Y
ScheduleNextESRoutineShortDelay
  LDA #$05
  JMP ESScheduleNextRoutine

ES_LoadLavaSparkleGfxPtr
  LDA ES_LavaSparkleAnimationPtrLoLUT,X
  STA TempPoint5ACurrentESRoutineLo
  LDA ES_LavaSparkleAnimationPtrHiLUT,X
  STA TempPoint5ACurrentESRoutineHi
  RTS

ES_SetRandomLavaSparkleFrameAndCancel
  JSR ES_LoadLavaSparkleGfxPtr
  LDA #$B0
  LDY #$03
  STA (TempPoint5ACurrentESRoutineLo),Y
  LDA #$00
  STA ESRoutineIndex,X
  JSR ReturnRandInA
  AND #$1F
  JMP ESScheduleNextRoutine

;------------------------------------------------------------------------------
; ReturnRandInA
; A simple pseudo-random number generator.
; It uses an incrementing seed to read 4 bytes from ROM code space ($B0xx) and
; adds them together.
; Returns: A = random byte. Preserves Y.
;------------------------------------------------------------------------------
ReturnRandInA
  TYA
  PHA
  INC IncrementingRandSeed
  LDA IncrementingRandSeed
  STA Temp68
  LDA #$B0
  STA Temp69
  LDY #$03
RandLoop
  ADC ($68),Y
  DEY
  BPL RandLoop
  STA Temp68
  PLA
  TAY
  LDA Temp68
  RTS

;------------------------------------------------------------------------------
; TrollAiLogic
; Manages the troll that lives in the lava. If conditions are right, it will
; jump out to grab a bird and pull it into the lava.
;------------------------------------------------------------------------------
TrollAiLogic
  LDA Level
  CMP #$03                      ; Troll only appears on level 3 and higher
  BMI TrollTargetSearchFailed
  LDA Difficulty
  BEQ TrollTargetSearchFailed                     ; Troll does not appear on Beginner difficulty
  LDA TrollState
  BEQ SearchForTrollTarget      ; 0=troll hiding in lava
  BMI TrollInAirProcessing                     ; ff=troll jumping at bird
  CMP #$01
  BNE ProcessTrollState                     ; 02=troll holding a bird
  JMP TrollHoldingBirdUpdateState
ProcessTrollState
  JMP TrollIsRetreating
SearchForTrollTarget
  LDX #$09
SearchForTrollTargetLoop
  LDA GameObjectYCoordHi,X
  CMP TrollMinimumTargetYCoord
  BCC TrollTargetSearch_CheckNextObject
  LDA GameObjectState,X
  CMP #$04
  BNE TrollTargetSearch_CheckNextObject
  LDA GameObjectAlive,X
  BEQ TrollTargetSearch_CheckNextObject
  LDA GameObjectXCoordHi,X
  CMP #$33
  BCC TrollTargetFound
  CMP #$A4
  BCS TrollTargetFound
TrollTargetSearch_CheckNextObject
  DEX
  BPL SearchForTrollTargetLoop
TrollTargetSearchFailed
  RTS

TrollTargetFound
  STX TrollTargetIndex
  LDA #$B6
  STA TrollYCoord
Troll_StartJump
  LDA GameObjectXCoordHi,X
  STA TrollXCoord
  LDA #$FF        ; troll is jumping at a bird
  STA TrollState
  LDA #SFX_TrollJumping
  JSR ScheduleSFX
SetTrollGrabGfxFrame
  LDA ObjectGrabYOffsetLUT,X
  CLC
  ADC GameObjectYCoordHi,X
  SEC
  SBC TrollYCoord
  BPL SelectTrollJumpFrameByDistance
  EOR #$FF
  CLC
  ADC #$01
SelectTrollJumpFrameByDistance
  CMP #$03
  BCS TrollGfxSelectMediumDistanceFrame
  LDA #$06
  JMP TrollGfxFrameUpdate
TrollGfxSelectMediumDistanceFrame
  CMP #$06
  BCS TrollGfxSelectFarDistanceFrame
  LDA #$04
  JMP TrollGfxFrameUpdate
TrollGfxSelectFarDistanceFrame
  CMP #$09
  BCS TrollGfxSetDefaultOrRetreat
  LDA #$02
  JMP TrollGfxFrameUpdate
TrollGfxSetDefaultOrRetreat
  LDA #$00
TrollGfxFrameUpdate
  STA TrollGfxOffset
  RTS

TrollInAirProcessing
; Troll is in the air, check if it should grab the bird or retreat
  LDX TrollTargetIndex
  LDA GameObjectState,X
  CMP #$04
  BNE TrollTargetInvalid
  LDA GameObjectAlive,X
  BEQ TrollTargetInvalid
  LDA GameObjectYCoordHi,X
  CMP TrollMinimumTargetYCoord
  BCC TrollTargetInvalid
  LDY GameObjectXCoordHi,X
  CPY #$33
  BCC TrollTargetStillValid
  CPY #$A4
  BCS TrollTargetStillValid
TrollTargetInvalid
; troll retreats
  LDA #$02
  STA TrollState
  BPL SetTrollGrabGfxFrame
TrollTargetStillValid
; Target is still valid, continue moving towards it
  STY TrollXCoord
  CLC
  ADC ObjectGrabYOffsetLUT,X
  CMP TrollYCoord
  BCS TrollGrabbedBirdCheckGrabHeight
  LDA TrollYCoord
  CMP TrollMinimumGrabYCoord
  BCC TrollContinuesJump
  DEC TrollYCoord
TrollContinuesJump
  JMP SetTrollGrabGfxFrame
TrollHoldingBirdUpdateState
  LDX TrollTargetIndex
  LDA GameObjectState,X
  CMP #$04                                ; alive
  BEQ TrollHoldingBirdUpdateGrabPosition
  LDA GameObjectAlive,X
  BEQ TrollTargetInvalid
TrollHoldingBirdUpdateGrabPosition
  LDA GameObjectYCoordHi,X
  CLC
  ADC ObjectGrabYOffsetLUT,X
TrollGrabbedBirdCheckGrabHeight
  CMP TrollMinimumGrabYCoord
  BCS TrollGrabbedTheBird
  JMP Troll_StartJump
TrollGrabbedTheBird
  STA TrollYCoord
  LDA #$00
  STA GameObjectXVelocity,X
  LDA GameObjectYVelocityLo,X
  CLC
  ADC TrollPullingVelocity
  STA GameObjectYVelocityLo,X
  LDA GameObjectYVelocityHi,X
  ADC #$00
  STA GameObjectYVelocityHi,X
  LDA TrollXCoord
  STA GameObjectXCoordHi,X
  JMP SetTrollGrabGfxFrame
TrollIsRetreating
  INC TrollYCoord
  LDA TrollYCoord
  CMP #$B6
  BCC TrollUpdateRetreatingFrame
  LDA #$00
  STA TrollState                          ; troll in lava (default)
  STA TrollYCoord
TrollUpdateRetreatingFrame
  JMP TrollGfxSetDefaultOrRetreat

ObjectGrabYOffsetLUT  ; bird objects only
 .byte $0A, $0C, $0A, $0A, $0A, $0A, $0A, $0A, $0A, $0A

;------------------------------------------------------------------------------
; Platform Collision Detection Routines
; These routines check for collisions between a game object and the platforms.
;------------------------------------------------------------------------------
CheckPlatformCollision
  LDA GameObjectXCoordHi,X
  STA Temp5E
  LDA GameObjectYCoordHi,X
  STA Temp5F
  STY Temp68
  LDY Temp69
  LDA PlatformCollisionCheckPointXLUT,Y
  CLC
  ADC Temp5E
  STA Temp62
  LDA PlatformCollisionCheckPointYLUT,Y
  CLC
  ADC Temp5F
  STA Temp61
  LDA PlatformPresentBitmask
  STA Temp63
  LDY #$06
CheckCollisionAgainstEachPlatformLoop
  LSR Temp63
  BCS CheckCollisionWithPresentPlatform
CollisionCheckNextPlatform
  DEY
  BPL CheckCollisionAgainstEachPlatformLoop
  JMP CheckCollisionWithLavaBridge
CheckCollisionWithPresentPlatform
  LDA Temp61
  CMP PlatformYTop,Y
  BCS PlatformCollisionCheckYBottomAndXBounds
  JMP PlatformCollisionNoHitReturnZero
PlatformCollisionCheckYBottomAndXBounds
  LDA Temp5F
  CMP PlatformYBottom,Y
  BCS CollisionCheckNextPlatform
  LDA Temp62
  CMP PlatformXLeftEdge,Y
  BEQ CollisionCheckNextPlatform
  BCC CollisionCheckNextPlatform
  LDA Temp5E
  CMP PlatformXRightEdge,Y
  BCS CollisionCheckNextPlatform
  LDA Temp61
  CLC
  ADC #$04
  LSR
  LSR
  LSR
  STA Temp60
  LDA Temp62
  SEC
  SBC PlatformXLeftBounce,Y
  BMI InitializeRightSideCollisionCheck
  SEC
  SBC Temp60
  BCS HandleLeftSidePlatformCollision
  JMP CollisionCheckNextPlatform
HandleLeftSidePlatformCollision
  ADC #$00
  STA TempCollisionOverlap
  LDA #$01
  STA Temp64
  LDA Temp5E
  SEC
  SBC TempCollisionOverlap
  STA Temp65
  LDA Temp5F
  STA Temp66
  JMP ContinueRightSideCollisionCheck
InitializeRightSideCollisionCheck
  LDA #$64
  STA TempCollisionOverlap
ContinueRightSideCollisionCheck
  LDA PlatformXRightBounce,Y
  SEC
  SBC Temp5E
  BMI ContinueCollisionCheckWithYAxis
  SEC
  SBC Temp60
  BCS UpdateClosestRightSideCollision
  JMP CollisionCheckNextPlatform
UpdateClosestRightSideCollision
  ADC #$00
  CMP TempCollisionOverlap
  BCS ContinueCollisionCheckWithYAxis
  STA TempCollisionOverlap
  CLC
  ADC Temp5E
  STA Temp65
  LDA Temp5F
  STA Temp66
  LDA #$08
  STA Temp64
ContinueCollisionCheckWithYAxis
  LDA Temp5F
  SEC
  SBC PlatformYBottomForBounceCheckLUT,Y
  BMI CheckPlatformTopCollision
  STA Temp60
  LDA Temp62
  CLC
  ADC #$02
  LSR
  LSR
  SEC
  SBC Temp60
  BCS UpdateClosestBottomSideCollision
  JMP CollisionCheckNextPlatform
UpdateClosestBottomSideCollision
  ADC #$00
  CMP TempCollisionOverlap
  BCS CheckPlatformTopCollision
  STA TempCollisionOverlap
  CLC
  ADC Temp5F
  STA Temp66
  LDA Temp5E
  STA Temp65
  LDA #$02
  STA Temp64
CheckPlatformTopCollision
  LDA Temp5E
  CLC
  ADC #$02
  LSR
  LSR
  STA Temp60
  LDA PlatformYTopForBounceCheckLUT,Y
  SEC
  SBC Temp5F
  BMI SkipUpdatingClosestCollision
  SEC
  SBC Temp60
  BCS UpdateClosestBottomPlatformCollision
  JMP CollisionCheckNextPlatform
UpdateClosestBottomPlatformCollision
  ADC #$00
  CMP TempCollisionOverlap
  BCS SkipUpdatingClosestCollision
  STA TempCollisionOverlap
  CLC
  ADC Temp5F
  STA Temp66
  LDA Temp5E
  STA Temp65
  LDA #$04
  STA Temp64
SkipUpdatingClosestCollision
  LDA Temp61
  SEC
  SBC PlatformYTop,Y
  CLC
  ADC #$02
  CMP TempCollisionOverlap
  BEQ UpdateClosestCollisionPoint
  BCC UpdateClosestCollisionPoint
  JMP FinalizePlatformCollisionCheck
UpdateClosestCollisionPoint
  STA TempCollisionOverlap
  LDA Temp62
  SEC
  SBC #$03
  CMP PlatformXLeftEdge,Y
  BCS FinalizeTopPlatformCollision
  JMP CollisionCheckNextPlatform
FinalizeTopPlatformCollision
  LDA PlatformXRightEdge,Y
  SEC
  SBC #$03
  CMP Temp5E
  BCS HandlePlatformTopCollision
  JMP CollisionCheckNextPlatform
HandlePlatformTopCollision
  LDA #$FF
  STA Temp64
  LDA Temp5E
  STA Temp65
  LDA Temp5F
  SEC
  SBC TempCollisionOverlap
  STA Temp66
  INC Temp66
  JMP FinalizePlatformCollisionCheck
CheckCollisionWithLavaBridge
  LDA Temp61
  SEC
  SBC #$AF
  BMI PlatformCollisionNoHitReturnZero
  STA Temp60
  LDA Temp62
  SEC
  SBC BridgeBurningWidth
  STA TempCollisionOverlap
  INC TempCollisionOverlap
  LDA #$01
  STA Temp64
  LDA Temp5E
  SEC
  SBC TempCollisionOverlap
  STA Temp65
  LDA Temp5F
  STA Temp66
  LDA #$E0
  SEC
  SBC BridgeBurningWidth
  SEC
  SBC Temp5E
  CMP TempCollisionOverlap
  BCS PrioritizeLavaBridgeHorizontalCollision
  STA TempCollisionOverlap
  INC TempCollisionOverlap
  LDA #$08
  STA Temp64
  LDA Temp5E
  CLC
  ADC TempCollisionOverlap
  STA Temp65
  LDA Temp5F
  STA Temp66
PrioritizeLavaBridgeHorizontalCollision
  LDA Temp60
  CMP TempCollisionOverlap
  BCS FinalizePlatformCollisionCheck
  LDA Temp62
  SEC
  SBC #$03
  CMP BridgeBurningWidth
  BCS CheckLavaBridgeXBounds
  JMP PlatformCollisionNoHitReturnZero
CheckLavaBridgeXBounds
  LDA #$E0
  SEC
  SBC BridgeBurningWidth
  SEC
  SBC #$03
  CMP Temp5E
  BCS HandleLavaBridgeTopCollision
  JMP PlatformCollisionNoHitReturnZero
HandleLavaBridgeTopCollision
  LDA #$FF
  STA Temp64
  LDA Temp5E
  STA Temp65
  LDA Temp5F
  SEC
  SBC Temp60
  STA Temp66
FinalizePlatformCollisionCheck
  LDY Temp68
  LDA Temp64
  RTS

PlatformCollisionNoHitReturnZero
  LDY Temp68
  LDA #$00
  RTS

SelectEggCollisionPointAndCheck
  LDA #$04
  STA Temp69
  JMP CheckPlatformCollision
SelectCollisionPointsAndCheckPlatform
  LDA GameObjectSkidDirection,X
  BNE SelectCollisionPointsForSkiddingBird
  LDA #$00
  STA Temp69
  JMP CheckPlatformCollision
SelectCollisionPointsForSkiddingBird
  LDA #$0A
  STA Temp69
  JMP CheckPlatformCollision
CheckForShallowPlatformCollision
  LDA GameObjectXCoordHi,X
  CMP #$B0
  BCS FallbackToStandardCollisionCheck
  CMP #$AA
  BEQ FallbackToStandardCollisionCheck
  BCC FallbackToStandardCollisionCheck
  LDA GameObjectYCoordHi,X
  CMP #$66
  BCS FallbackToStandardCollisionCheck
  CMP #$62
  BCC FallbackToStandardCollisionCheck
  LDA GameObjectXVelocity,X
  BPL FallbackToStandardCollisionCheck
  LDA #$AA
  STA Temp65
  LDA #$73
  STA Temp66
  LDA #$02
  RTS

FallbackToStandardCollisionCheck
  CPX #$01
  BNE SelectCollisionPointForOtherBirds
  LDA #$08
  STA Temp69
  JMP CheckPlatformCollision
SelectCollisionPointForOtherBirds
  LDA #$02
  STA Temp69
  JMP CheckPlatformCollision

CheckPterryPlatformCollision
  LDA GameObjectXCoordHi,X
  CMP #$AD
  BCS SelectCollisionPointsForPterry
  CMP #$AA
  BCC SelectCollisionPointsForPterry
  LDA GameObjectYCoordHi,X
  CMP #$67
  BCS SelectCollisionPointsForPterry
  CMP #$62
  BCC SelectCollisionPointsForPterry
  LDA GameObjectXVelocity,X
  BPL SelectCollisionPointsForPterry
  LDA #$AA
  STA Temp65
  LDA #$73
  STA Temp66
  LDA #$02
  RTS

SelectCollisionPointsForPterry
  LDA #$06
  STA Temp69
  JMP CheckPlatformCollision
CheckIfEggSlippingOffPlatform
  STY Temp64
  LDY #$04
  JMP PerformPlatformEdgeCheck
CheckIfBirdNearPlatformEdge
  STY Temp64
  LDA GameObjectSkidDirection,X
  BNE SelectCollisionPointForSkiddingBird
  LDY #$00
  JMP PerformPlatformEdgeCheck
SelectCollisionPointForSkiddingBird
  LDY #$0A
PerformPlatformEdgeCheck
  LDA GameObjectXCoordHi,X
  STA Temp5E
  LDA GameObjectYCoordHi,X
  STA Temp5F
  LDA #$00
  STA Temp60
  LDA PlatformCollisionCheckPointYLUT,Y
  CLC
  ADC Temp5F
  STA Temp61
  INC Temp61
  LDA PlatformCollisionCheckPointXLUT,Y
  CLC
  ADC Temp5E
  STA Temp62
  LDA PlatformPresentBitmask
  STA Temp63
  LDY #$06
PlatformEdgeCheckLoop
  LSR Temp63
  BCC CheckNextPlatformForEdge
  LDA Temp61
  CMP PlatformYTop,Y
  BEQ ConfirmBirdIsOnPlatform
  BCS CheckNextPlatformForEdge
  JMP FinishPlatformEdgeCheck
ConfirmBirdIsOnPlatform
  LDA Temp62
  SEC
  SBC #$03
  CMP PlatformXLeftEdge,Y
  BCC CheckNextPlatformForEdge
  LDA PlatformXRightEdge,Y
  SEC
  SBC #$03
  CMP Temp5E
  BCC CheckNextPlatformForEdge
  JMP PlatformEdgeCheckOnPlatformReturnTrue
CheckNextPlatformForEdge
  DEY
  BPL PlatformEdgeCheckLoop
  LDA Temp61
  CMP #$B0
  BNE FinishPlatformEdgeCheck
  LDA #$E0
  SEC
  SBC BridgeBurningWidth
  CMP Temp5E
  BEQ FinishPlatformEdgeCheck
  BCC FinishPlatformEdgeCheck
  LDA Temp62
  CMP BridgeBurningWidth
  BEQ FinishPlatformEdgeCheck
  BCC FinishPlatformEdgeCheck
  LDA #$E0
  SEC
  SBC BridgeBurningWidth
  CMP Temp5E
  BEQ FinishPlatformEdgeCheck
  BCC FinishPlatformEdgeCheck
PlatformEdgeCheckOnPlatformReturnTrue
  LDA #$01
  STA Temp60
FinishPlatformEdgeCheck
  LDY Temp64
  RTS

; Pairs of coordinates used for platform collision checks
PlatformCollisionCheckPointXLUT
 .byte $08
PlatformCollisionCheckPointYLUT
 .byte $0F
 .byte $08,$0A
 .byte $04,$05
 .byte $10,$09
 .byte $08,$0C
 .byte $08,$0E

PlatformXLeftEdge
 .byte $57,$AB,$18,$8D,$4E,$18,$A8

PlatformXRightEdge
 .byte $7B,$CD,$44,$AE,$7D,$33,$C4

PlatformYTop
 .byte $80,$70,$70,$68,$40,$38,$38

PlatformYBottom
 .byte $86,$77,$76,$72,$46,$3E,$3E

PlatformXLeftBounce  ; used to start low-approacing bird bouncing
 .byte $47,$9D,$0A,$80,$46,$11,$A1

PlatformYBottomForBounceCheckLUT
 .byte $6D,$4A,$6A,$47,$2D,$34,$11
PlatformYTopForBounceCheckLUT
 .byte $9F,$A4,$82,$9D,$5F,$48,$6B

PlatformXRightBounce  ; used to start low-approacing bird bouncing
 .byte $8B,$DB,$52,$BB,$85,$3A,$CB

; Platform information tables used by riderless bird figuring out which
; direction to start with to get to it's hatched rider.
PlatformTopCoordinate
 .byte $32,$3A,$62,$6A,$7A,$AA,$AA
PlatformLeftsideCoordinate
 .byte $A8,$4E,$8D,$AB,$4D,$3C,$A4
PlatformRightsideCoordinate
 .byte $C0,$65,$9D,$C0,$89,$70,$C0

; Doesn't seem to be referenced during play, even indirectly.
UnusedPterrySpawnYCoordLUT  
 .byte $15,$52,$52,$52,$52,$94,$94

; Data for random egg placement during Egg Waves
NewEggXMaximum
 .byte $10,$0B,$27,$1D,$20,$12,$20,$59
NewEggXOffset
 .byte $AB,$23,$51,$90,$23,$AE,$5A,$45
NewEggYCoordinate
 .byte $32,$32,$3A,$62,$6A,$6A,$7A,$AA

;------------------------------------------------------------------------------
; UpdatePlayerScoreAndBonus
; Adds a score value to the current player's score and checks for extra lives.
; Input: X = player index. Temp60/61/62 = score value to add.
;------------------------------------------------------------------------------
UpdatePlayerScoreAndBonus
  TXA
  PHA
  TYA
  PHA
  LDA P0Lives,X
  BMI FinalizeScoreUpdateAndExit
  LDA DemoMode
  BNE FinalizeScoreUpdateAndExit
  STA Temp60
  TXA
  BNE UpdatePlayer1Score
  LDY #$02
  SED
  CLC
AddScoreToPlayer0Loop
  LDA.w Temp60,Y
  ADC.w P0Score,Y
  STA.w P0Score,Y
  DEY
  BPL AddScoreToPlayer0Loop
  TXA
  JSR CheckForPlayer0BonusLife
  JMP FinalizeScoreUpdateAndExit
UpdatePlayer1Score
  LDY #$02
  SED
  CLC
AddScoreToPlayer1Loop
  LDA.w Temp60,Y
  ADC.w P1Score,Y
  STA.w P1Score,Y
  DEY
  BPL AddScoreToPlayer1Loop
  TXA
  JSR CheckForPlayer1BonusLife
FinalizeScoreUpdateAndExit
  JSR UpdateScoreDisplay
  PLA
  TAY
  PLA
  TAX
  RTS

CheckForPlayer0BonusLife
  LDY #$01
  CLC
AddScoreToBonusLifeCounterLoop: LDA.w Temp61,Y
  ADC.w P0ExtraLifeScore,Y
  STA.w P0ExtraLifeScore,Y
  DEY
  BPL AddScoreToBonusLifeCounterLoop
  CLD
  LDA P0ExtraLifeScore
  BMI NoBonusLifeAwarded
  SEC
  SBC #$80
  STA P0ExtraLifeScore
  JMP AwardBonusLife
NoBonusLifeAwarded
  RTS

CheckForPlayer1BonusLife
  LDY #$01
  CLC
AddScoreToP1BonusLifeCounterLoop
  LDA.w Temp61,Y
  ADC.w P1ExtraLifeScore,Y
  STA.w P1ExtraLifeScore,Y
  DEY
  BPL AddScoreToP1BonusLifeCounterLoop
  CLD
  LDA P1ExtraLifeScore
  BMI NoBonusLifeAwarded
  SEC
  SBC #$80
  STA P1ExtraLifeScore
  JMP AwardBonusLife

;------------------------------------------------------------------------------
; InitPlayerLivesAndBonus
; Initializes lives and bonus score thresholds at the start of a game.
;------------------------------------------------------------------------------
InitPlayerLivesAndBonus
; Initializes the lives and bonus score for one or both players
  LDA #$FF
  STA P1Lives
  LDA #$04
  STA P0Lives
  LDX PlayerCount
  BEQ InitPlayerLivesDone  ; leave P1Lives=FF for one player mode
  STA P1Lives
InitPlayerLivesDone
  LDA #$80
  STA P0ExtraLifeScore
  STA P1ExtraLifeScore
  ASL
  STA P0ExtraLifeScore+1
  STA P1ExtraLifeScore+1
EarlyReturn3
  RTS

;------------------------------------------------------------------------------
; DeadPlayerChecks
; Handles the logic when a player dies: decrements lives, checks for game over.
; Input: X = player index
;------------------------------------------------------------------------------
DeadPlayerChecks
  LDA #$00
  STA LevelWasSurvived       ; No survival points if you die.
  STA P0EggPointLevel,X
  DEC P0Lives,X              ; Lose a life and return
  LDA P0Lives,X              ; only if you have more.
  BPL EarlyReturn3
  LDA P0Lives                ; when both player lives are zero, we skip
  EOR P1Lives                ; over player death notifications, and 
  BEQ SkipPlayerGameOverMsg  ; instead display "thy game is over"
  LDA DemoMode
  BNE EarlyReturn3
  TXA
  CLC
  ADC #$08                   ; game message 8="player one", 9="player two"
  JSR DisplayGameMessage
  LDA #$0A                   ; "game over"
  JMP DisplayGameMessage
SkipPlayerGameOverMsg
  LDA DemoMode
  BNE EarlyReturn3
  LDA #$01
  STA GameActiveFlag
  LDA #$0B                   ; "thy game is over"
  JMP DisplayGameMessage
AwardBonusLife
  LDA P0Lives,X
  CMP #$7F                   ; Maximum lives is $7F
  BEQ ScheduleFreeBirdSound
  INC P0Lives,X
  JSR UpdatePlayerLivesDisplayed
ScheduleFreeBirdSound
  LDA #SFX_FreeBird
  JMP ScheduleSFX
UpdateScoreDisplay
  LDA #$00
  STA Temp5E
  TAX
UpdateScoreDisplayP0HandleLeadingZeros
  LDA P0Score,X
  LSR
  LSR
  LSR
  LSR
  BNE UpdateScoreDisplayP0ProcessDigit
  JSR AddSpaceToScoreHudString
  LDA P0Score,X
  AND #$0F
  BNE UpdateScoreDisplayP0ProcessUnitsDigit
  JSR AddSpaceToScoreHudString
  INX
  CPX #$03
  BMI UpdateScoreDisplayP0HandleLeadingZeros
  JMP FinalizeP0ScoreDisplay
UpdateScoreDisplayP0Loop
  LDA P0Score,X
  LSR
  LSR
  LSR
  LSR
UpdateScoreDisplayP0ProcessDigit
  CLC
  ADC #$01
  JSR AddCharToScoreHudString
  LDA P0Score,X
  AND #$0F
UpdateScoreDisplayP0ProcessUnitsDigit
  CLC
  ADC #$01
  JSR AddCharToScoreHudString
  INX
  CPX #$03
  BMI UpdateScoreDisplayP0Loop
FinalizeP0ScoreDisplay
  LDA #$01
  JSR AddCharToScoreHudString
  LDA PlayerCount
  BNE UpdatePlayer1ScoreDisplay
  RTS

UpdatePlayer1ScoreDisplay
  LDA #$0D
  STA Temp5E
  LDX #$00
UpdateScoreDisplayP1HandleLeadingZeros
  LDA P1Score,X
  LSR
  LSR
  LSR
  LSR
  BNE UpdateScoreDisplayP1ProcessDigit
  JSR AddSpaceToScoreHudString
  LDA P1Score,X
  AND #$0F
  BNE UpdateScoreDisplayP1ProcessUnitsDigit
  JSR AddSpaceToScoreHudString
  INX
  CPX #$03
  BMI UpdateScoreDisplayP1HandleLeadingZeros
  BPL FinalizeP1ScoreDisplay
UpdateScoreDisplayP1Loop
  LDA P1Score,X
  LSR
  LSR
  LSR
  LSR
UpdateScoreDisplayP1ProcessDigit
  CLC
  ADC #$0C
  JSR AddCharToScoreHudString
  LDA P1Score,X
  AND #$0F
UpdateScoreDisplayP1ProcessUnitsDigit
  CLC
  ADC #$0C
  JSR AddCharToScoreHudString
  INX
  CPX #$03
  BMI UpdateScoreDisplayP1Loop
FinalizeP1ScoreDisplay
  LDA #$0C
  BNE AddCharToScoreHudString

UpdatePlayerLivesDisplayed
  TXA
  PHA
  TYA
  PHA
  LDA DemoMode
  BNE UpdatePlayerLivesExit
  LDA HudPlayerLivesStartXOffsetLUT,X
  STA Temp5E
  LDY #$03
DrawPlayerLivesLoop
  TYA
  CMP P0Lives,X
  BPL DrawEmptyLifeSlot
  LDA HudPlayerLifeIconCharLUT,X
  JSR AddCharToScoreHudString
  JMP UpdatePlayerLivesContinueLoop
DrawEmptyLifeSlot
  JSR AddSpaceToScoreHudString
UpdatePlayerLivesContinueLoop
  DEY
  BPL DrawPlayerLivesLoop
UpdatePlayerLivesExit
  PLA
  TAY
  PLA
  TAX
  RTS

HudPlayerLifeIconCharLUT
 .byte $0B,$16  ; Character codes for P0 and P1 life icons
HudPlayerLivesStartXOffsetLUT
 .byte $07,$14  ; Start offsets in HUD buffer for P0 and P1 lives

AddSpaceToScoreHudString
  LDA #$00 ; character index for space
AddCharToScoreHudString
  STY Temp5F
  LDY Temp5E
  STA HudDisplayStringBuffer,Y
  LDY Temp5F
  INC Temp5E
  RTS

;------------------------------------------------------------------------------
; DisplayGameMessage
; Schedules a message to be displayed on screen.
; Input: A = message index
;------------------------------------------------------------------------------
DisplayGameMessage
  STA Temp69
  TXA
  PHA
  TYA
  PHA
  LDA Temp69
  TAX
  LDY MessageLineNumber,X
  LDA Temp69
  STA $2038,Y
  PLA
  TAY
  PLA
  TAX
  RTS

;------------------------------------------------------------------------------
; ManageMessageTimers
; Manages the timers for on-screen messages, clearing them when they expire.
;------------------------------------------------------------------------------
ManageMessageTimers
  LDX #$03
UpdateMessageDisplayTimers
  LDA $2038,X
  BMI ManageMessageTimersCheckNextLine
  LDA MessageLine0Timer,X
  BPL ManageMessageTimersCheckNextLine
  LDA $2038,X
  JSR DisplayMessageAndSetTimer
  LDA #$FF
  STA $2038,X
ManageMessageTimersCheckNextLine
  DEX
  BPL UpdateMessageDisplayTimers
  LDX #$03
ClearExpiredMessageTimersLoop: LDA MessageLine0Timer,X
  CMP #$FF
  BEQ CheckNextMessageTimer
  LDA MessageLine0Timer,X
  BMI CheckNextMessageTimer
  DEC MessageLine0Timer,X
  BNE CheckNextMessageTimer
  LDA MsgBufferLo,X
  STA TempPointLo5A
  LDA MsgBufferHi,X
  STA TempPointHi5B
  LDY #$0F
  LDA #$00
ClearMessageBufferLoop
  STA (TempPointLo5A),Y
  DEY
  BPL ClearMessageBufferLoop
CheckNextMessageTimer
  DEX
  BPL ClearExpiredMessageTimersLoop
  RTS

DisplayMessageAndSetTimer
  STA Temp69
  TXA
  PHA
  TYA
  PHA
  LDX Temp69
  LDA MessageLineNumber,X
  TAX
  LDY Temp69
  LDA MessageDisplayTime,Y
  STA MessageLine0Timer,X
  JSR UpdateDLWithMessageString
  LDA Temp69
  BNE FinishDisplayingMessage
  TAX
  TAY
  JSR UpdateDLWithMessageString
  LDA LevelBCD
  LSR
  LSR
  LSR
  LSR
  BEQ DisplayWaveNumberProcessUnitsDigit
  CLC
  ADC #$0C
  STA HudWaveNumberDigit1
DisplayWaveNumberProcessUnitsDigit
  LDA LevelBCD
  AND #$0F
  CLC
  ADC #$0C
  STA HudWaveNumberDigit2
FinishDisplayingMessage
  PLA
  TAY
  PLA
  TAX
  RTS

UpdateDLWithMessageString
  LDA MsgBufferLo,X
  STA TempPointLo5A
  LDA MsgBufferHi,X
  STA TempPointHi5B
  TYA
  ASL
  ASL
  ASL
  ASL  ; A = Y * 16
  PHP
  CLC
  ADC #<GameMessages
  STA Temp5E
  BCC AddHiByteWithNoCarry
  LDA #$01
  BNE FinalizeMessagePointerHiByte
AddHiByteWithNoCarry
  LDA #$00
FinalizeMessagePointerHiByte
  PLP
  ADC #>GameMessages
  STA Temp5F
  LDY #$0F
CopyMessageStringToBufferLoop
  LDA (Temp5E),Y
  CLC
  ADC #$00
  STA (TempPointLo5A),Y
  DEY
  BPL CopyMessageStringToBufferLoop
  RTS

MessageLineNumber
 .byte 0,2,3,1,1,1,1,1,2,2,3,2,1,1,1,2,1,1,1

MessageDisplayTime
 .byte $3C, $46, $28, $3C, $3C, $3C, $3C, $3C
 .byte $3C, $3C, $3C, $F0, $3C, $3C, $3C, $3C
 .byte $3C, $3C, $3C 

MsgBufferLo
 .byte <$2671 
 .byte <$2685 
 .byte <$2699 
 .byte <$26AD 

MsgBufferHi
 .byte >$2671 
 .byte >$2685 
 .byte >$2699 
 .byte >$26AD 

GameMessages
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
            ; "WAVE"
 .byte $29  ; 'W' 
 .byte $17  ; 'A'
 .byte $28  ; 'V'
 .byte $1B  ; 'E'
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
            ; "PREPARE TO JOUST"
 .byte $23  ; 'P' 
 .byte $24  ; 'R'
 .byte $1B  ; 'E'
 .byte $23  ; 'P'
 .byte $17  ; 'A'
 .byte $24  ; 'R'
 .byte $1B  ; 'E'
 .byte $00
 .byte $26  ; 'T'
 .byte $0C  ; '0'
 .byte $00
 .byte $1F  ; 'J' 
 .byte $0C  ; '0' 
 .byte $27  ; 'U' 
 .byte $25  ; 'S' 
 .byte $26  ; 'T' 
 .byte $00
 .byte $00
            ; "BUZZARD BAIT"
 .byte $18  ; 'B' 
 .byte $27  ; 'U'
 .byte $2C  ; 'Z'
 .byte $2C  ; 'Z'
 .byte $17  ; 'A'
 .byte $24  ; 'R'
 .byte $1A  ; 'D'
 .byte $00
 .byte $18  ; 'B' 
 .byte $17  ; 'A'
 .byte $1E  ; 'I'
 .byte $26  ; 'T'
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
            ; "EGG WAVE"
 .byte $1B  ; 'E' 
 .byte $1C  ; 'G'
 .byte $1C  ; 'G'
 .byte $00
 .byte $29  ; 'W'
 .byte $17  ; 'A'
 .byte $28  ; 'V'
 .byte $1B  ; 'E'
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $1C  ; 'G' 
 .byte $20  ; 'L'
 .byte $17  ; 'A'
 .byte $1A  ; 'D'
 .byte $1E  ; 'I'
 .byte $17  ; 'A'
 .byte $26  ; 'T'
 .byte $0C  ; 'O'
 .byte $24  ; 'R'
 .byte $00
 .byte $29  ; 'W' 
 .byte $17  ; 'A'
 .byte $28  ; 'V'
 .byte $1B  ; 'E'
 .byte $00
 .byte $00
 .byte $00
 .byte $23  ; 'P' 
 .byte $26  ; 'T'
 .byte $1B  ; 'E'
 .byte $24  ; 'R'
 .byte $24  ; 'R'
 .byte $2B  ; 'Y'
 .byte $00
 .byte $00
 .byte $29  ; 'W' 
 .byte $17  ; 'A'
 .byte $28  ; 'V'
 .byte $1B  ; 'E'
 .byte $00
 .byte $00
 .byte $00
 .byte $25  ; 'S' 
 .byte $27  ; 'U'
 .byte $24  ; 'R'
 .byte $28  ; 'V'
 .byte $1E  ; 'I'
 .byte $28  ; 'V'
 .byte $17  ; 'A'
 .byte $20  ; 'L'
 .byte $00
 .byte $00
 .byte $29  ; 'W' 
 .byte $17  ; 'A'
 .byte $28  ; 'V'
 .byte $1B  ; 'E'
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $26  ; 'T'
 .byte $1B  ; 'E'
 .byte $17  ; 'A'
 .byte $21  ; 'M'
 .byte $00
 .byte $00
 .byte $29  ; 'W'
 .byte $17  ; 'A'
 .byte $28  ; 'V'
 .byte $1B  ; 'E'
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $23  ; 'P'
 .byte $20  ; 'L'
 .byte $17  ; 'A'
 .byte $2B  ; 'Y'
 .byte $1B  ; 'E'
 .byte $24  ; 'R'
 .byte $00
 .byte $0D  ; '1'
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $23  ; 'P'
 .byte $20  ; 'L'
 .byte $17  ; 'A'
 .byte $2B  ; 'Y'
 .byte $1B  ; 'E'
 .byte $24  ; 'R'
 .byte $00
 .byte $0E  ; '2'
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $1C  ; 'G'
 .byte $17  ; 'A'
 .byte $21  ; 'M'
 .byte $1B  ; 'E'
 .byte $00
 .byte $00
 .byte $0C  ; '0'
 .byte $28  ; 'V'
 .byte $1B  ; 'E'
 .byte $24  ; 'R'
 .byte $00
 .byte $00
 .byte $26  ; 'T'
 .byte $1D  ; 'H'
 .byte $2B  ; 'Y'
 .byte $00
 .byte $1C  ; 'G'
 .byte $17  ; 'A'
 .byte $21  ; 'M'
 .byte $1B  ; 'E'
 .byte $00
 .byte $1E  ; 'I'
 .byte $25  ; 'S'
 .byte $00
 .byte $0C  ; '0'
 .byte $28  ; 'V'
 .byte $1B  ; 'E'
 .byte $24  ; 'R'
 .byte $00
 .byte $0F  ; '3'
 .byte $0C  ; '0'
 .byte $0C  ; '0'
 .byte $0C  ; '0'
 .byte $00
 .byte $00

 .byte $25  ; 'S'
 .byte $27  ; 'U'
 .byte $24  ; 'R'
 .byte $28  ; 'V'
 .byte $1E  ; 'I'
 .byte $28  ; 'V'
 .byte $17  ; 'A'
 .byte $20  ; 'L'
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $0F  ; '3'
 .byte $0C  ; '0'
 .byte $0C  ; '0'
 .byte $0C  ; '0'
 .byte $00  ;
 .byte $00  ;
 .byte $26  ; 'T'
 .byte $1B  ; 'E'
 .byte $17  ; 'A'
 .byte $21  ; 'M'
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $0F  ; '3'
 .byte $0C  ; '0'
 .byte $0C  ; '0'
 .byte $0C  ; '0'
 .byte $00
 .byte $1C  ; 'G'
 .byte $20  ; 'L'
 .byte $17  ; 'A'
 .byte $1A  ; 'D'
 .byte $1E  ; 'I'
 .byte $17  ; 'A'
 .byte $26  ; 'T'
 .byte $0C  ; '0'
 .byte $24  ; 'R'
 .byte $00
 .byte $00
 .byte $23  ; 'P'
 .byte $0C  ; '0'
 .byte $1E  ; 'I'
 .byte $22  ; 'N'
 .byte $26  ; 'T'
 .byte $25  ; 'S'
 .byte $00
 .byte $17  ; 'A'
 .byte $29  ; 'W'
 .byte $17  ; 'A'
 .byte $24  ; 'R'
 .byte $1A  ; 'D'
 .byte $1B  ; 'E'
 .byte $1A  ; 'D'

 .byte $00
 .byte $00
 .byte $00
 .byte $22  ; 'N'
 .byte $0C  ; '0'
 .byte $00
 .byte $00
 .byte $25  ; 'S'
 .byte $27  ; 'U'
 .byte $24  ; 'R'
 .byte $28  ; 'V'
 .byte $1E  ; 'I'
 .byte $28  ; 'V'
 .byte $17  ; 'A'
 .byte $20  ; 'L'
 .byte $00

 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $22  ; 'N'
 .byte $0C  ; '0'
 .byte $00
 .byte $00
 .byte $26  ; 'T'
 .byte $1B  ; 'E'
 .byte $17  ; 'A'
 .byte $21  ; 'M'
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00

 .byte $22  ; 'N'
 .byte $0C  ; '0'
 .byte $00
 .byte $1C  ; 'G'
 .byte $20  ; 'L'
 .byte $17  ; 'A'
 .byte $1A  ; 'D'
 .byte $1E  ; 'I'
 .byte $17  ; 'A'
 .byte $26  ; 'T'
 .byte $0C  ; 'O'
 .byte $24  ; 'R'
 .byte $00
 .byte $00

StringCopyright
 .byte $2E  ; 1/2 (c) symbol
 .byte $2F  ; 1/2 (c) symbol
 .byte $00  ; ' '
 .byte $17  ; 'A'
 .byte $26  ; 'T'
 .byte $17  ; 'A'
 .byte $24  ; 'R'
 .byte $1E  ; 'I'
 .byte $00  ; ' '
 .byte $0D  ; '1'
 .byte $15  ; '9'
 .byte $14  ; '8'
 .byte $10  ; '4' 
 .byte $2E  ; 1/2 (c) symbol
 .byte $2F  ; 1/2 (c) symbol
 .byte $00  ; ' '
 .byte $29  ; 'W'
 .byte $1E  ; 'I'
 .byte $20  ; 'L'
 .byte $20  ; 'L'
 .byte $1E  ; 'I'
 .byte $17  ; 'A'
 .byte $21  ; 'M'
 .byte $25  ; 'S'
 .byte $00  ; ' '
 .byte $0D  ; '1'
 .byte $15  ; '9'
 .byte $14  ; '8'
 .byte $0E  ; '2'

;------------------------------------------------------------------------------
; DisplayRoutineInit
; Initializes the working Display List pointers in RAM ($242B/$2443)
; from the master pointers in ROM. It then proceeds to build the render list
; for the current frame by processing all active game objects.
;------------------------------------------------------------------------------
DisplayRoutineInit
  LDX #$17  ; 24 DLs
InitializeDisplayListPointers
  LDA DisplayListLoLUT,X
  STA DisplayListLo,X
  LDA DisplayListHiLUT,X
  STA DisplayListHi,X
  DEX
  BPL InitializeDisplayListPointers

; loop through all of the birds...
  LDY #$09
RenderBirdObjectsLoop
  LDA.w GameObjectState,Y
  BEQ RenderNextBird      ; if state is dead then skip
  CMP #$03                ; if state is born1
  BEQ RenderNextBird      ; then skip
  JSR PrepareBirdSpriteForRenderList
  LDA GameObjectInAir,Y
  BNE RenderAirborneBirdRider
  JSR PrepareOnPlatformBirdSprite
RenderAirborneBirdRider
  LDA.w GameObjectAlive,Y
  BEQ RenderNextBird
  JSR PrepareRiderOnBirdSpriteForRenderList
RenderNextBird
  DEY
  BPL RenderBirdObjectsLoop

; loop through all of the pterrys...
  LDY #$02
RenderPterryObjectsLoop
  LDA.w PterryState1,Y
  BEQ RenderNextPterry
  JSR PreparePterrySpriteForRenderList
RenderNextPterry
  DEY
  BPL RenderPterryObjectsLoop

; loop through all of the eggs...
  LDY #$0B
RenderEggObjectsLoop
  LDA.w EggState,Y
  BEQ RenderNextEggObject
  JSR PrepareEggSpriteForRenderList
RenderNextEggObject
  DEY
  BPL RenderEggObjectsLoop

; loop through all of the player birds...
  LDY #$01
RenderPlayerDeathExplosionLoop
  LDA.w P0DeathState,Y
  BEQ RenderNextPlayerDeathExplosion
  JSR PreparePlayerExplosionSpriteForRenderList
RenderNextPlayerDeathExplosion
  DEY
  BPL RenderPlayerDeathExplosionLoop

  LDY #$05
RenderDissolvingPlatformObjectsLoop
  LDA PlatformDissolveAnimationFlag0,Y
  BEQ RenderNextDissolvingParticle
  JSR PrepareDissolvingPlatformParticleForRenderList
RenderNextDissolvingParticle
  DEY
  BPL RenderDissolvingPlatformObjectsLoop
  LDA TrollState
  BEQ RenderTrollIfVisible      ; Troll hiding in lava?
  JSR PrepareTrollSpriteForRenderList
RenderTrollIfVisible
  STX RenderListCount
  LDA #$01
  STA RenderListDirty
  RTS

PrepareTrollSpriteForRenderList
  LDA TrollGfxOffset
  STA Temp5EGfxDataLo
  LDA #$A8
  STA Temp5FGfxDataHi
  LDA #$1E
  STA Temp60PaletteAndWidth
  LDA TrollXCoord
  SEC
  SBC #$20
  STA Temp61SpriteXCoord
  LDA #$9A
  STA Temp62
  LDA TrollYCoord
  SEC
  SBC #$10
  LDY TrollTargetIndex
  CPY #$01
  BNE StoreTrollSpriteYCoord
  SBC #$02
StoreTrollSpriteYCoord
  STA Temp63SpriteYCoord
  JMP AddSpriteToRenderList
PrepareDissolvingPlatformParticleForRenderList
  SEC
  SBC #$01
  ASL
  ASL
  CLC
  ADC #$78
  STA Temp5EGfxDataLo
  LDA #$A8
  STA Temp5FGfxDataHi
  LDA #$1C
  STA Temp60PaletteAndWidth
  LDA PlatformDissolveAnimationXCoord0,Y
  STA Temp61SpriteXCoord
  LDA #$A1
  STA Temp62
  LDA PlatformDissolveAnimationYCoord0,Y
  SEC
  SBC #$10
  STA Temp63SpriteYCoord
  JMP AddSpriteToRenderList
PreparePlayerExplosionSpriteForRenderList
  LDA.w P0DeathState,Y
  SEC
  SBC #$01
  ASL
  CLC
  ADC #$70
  STA Temp5EGfxDataLo
  LDA #$A8
  STA Temp5FGfxDataHi
  LDA #$7E
  STA Temp60PaletteAndWidth
  LDA.w PlayerExplosionXCoord,Y
  SEC
  SBC #$20
  STA Temp61SpriteXCoord
  LDA #$A2
  STA Temp62
  LDA.w PlayerExplosionYCoord,Y
  SEC
  SBC #$10
  STA Temp63SpriteYCoord
  JMP AddSpriteToRenderList

; This appears to be dead menu code. Apparently at one point in development
; changing menu options would update the DL with the relevant character object.
; This was abandoned for the current character-buffer update system.
  STY Temp65
  LDA #$8F
  STA Temp60PaletteAndWidth
  LDA #$27
  STA Temp61SpriteXCoord
  LDA UNUSED_TitleMenuTextAddrLoLUT,Y
  STA Temp5EGfxDataLo
  LDA UNUSED_TitleMenuTextAddrHiLUT,Y
  STA Temp5F
  LDA UNUSED_TitleMenuDLZoneLUT,Y
  TAY
  JSR UpdateRenderList
  LDY Temp65
  RTS

PreparePterrySpriteForRenderList
  LDA $2344,Y ; pterry direction
  BMI SetPterryBaseGfxForLeftFacing
  LDA #$44
  JMP StorePterryBaseGfxAndContinue
SetPterryBaseGfxForLeftFacing
  LDA #$2C
StorePterryBaseGfxAndContinue
  STA Temp5EGfxDataLo
  LDA $236A,Y ; pterry animation frame
  ASL
  ASL
  ADC Temp5EGfxDataLo
  STA Temp5EGfxDataLo
  LDA #$A8
  STA Temp5F
  STX Temp61
  LDA $236A,Y
  TAX
  LDA PterrySpritePaletteAndWidthLUT,X
  STA Temp60PaletteAndWidth
  LDX Temp61
  LDA $015E,Y
  SEC
  SBC #$20
  STA Temp61SpriteXCoord
  LDA #$9A
  STA Temp62
  LDA $0177,Y
  SEC
  SBC #$10
  STA Temp63SpriteYCoord
  JMP AddSpriteToRenderList
PrepareEggSpriteForRenderList
  CMP #$04
  BEQ PrepareRiderSpriteForRenderList
  CMP #$03
  BEQ PrepareRiderSpriteForRenderList
  LDA $221B,Y
  CLC
  ADC #$80
  STA Temp5EGfxDataLo
  LDA EggX,Y
  SEC
  SBC #$20
  STA Temp61SpriteXCoord
  LDA #$9F
  STA Temp60PaletteAndWidth
  LDA #$A8
  STA Temp5FGfxDataHi
  LDA #$A4
  STA Temp62
  LDA EggY,Y
  SEC
  SBC #$0F
  STA Temp63SpriteYCoord
  JMP AddSpriteToRenderList
PrepareRiderSpriteForRenderList
  LDA.w EggState,Y
  CMP #$03
  BNE RenderHatchedRiderSprite
  LDA #$CB
  STA Temp5EGfxDataLo
  LDA #$C0
  STA Temp5FGfxDataHi
  LDA #$9E
  STA Temp60PaletteAndWidth
  LDA EggX,Y
  SEC
  SBC #$21
  STA Temp61SpriteXCoord
  LDA EggY,Y
  SEC
  SBC #$0C
  LSR
  LSR
  LSR
  STY TempCollisionOverlap
  TAY
  JSR UpdateRenderList
  LDY TempCollisionOverlap
RenderHatchedRiderSprite
  LDA EggLevelAndType,Y
  ASL
  CLC
  ADC #$58
  STA Temp5EGfxDataLo
  LDA #$C0
  STA Temp5FGfxDataHi
  LDA #$5E
  STA Temp60PaletteAndWidth
  LDA EggX,Y
  SEC
  SBC #$20
  STA Temp61SpriteXCoord
  LDA EggY,Y
  SEC
  SBC #$0C
  LSR
  LSR
  LSR
  STY TempCollisionOverlap
  TAY
  JSR UpdateRenderList
  LDY TempCollisionOverlap
  RTS

PrepareRiderOnBirdSpriteForRenderList
  LDA RiderObjectPaletteAndWidth,Y
  STA Temp60PaletteAndWidth
  LDA GameObjectDirection,Y
  BMI SetRiderBaseGfxForLeftFacing
  LDA #$5E
  JMP StoreRiderBaseGfxAndContinue
SetRiderBaseGfxForLeftFacing
  LDA #$5C
StoreRiderBaseGfxAndContinue
  STA Temp5E
  CPY #$02
  BCS CalculateEnemyRiderGfxOffset    ; branch if non-player enemy bird
  TYA
  JMP CalculateFinalRiderGfxPointer
CalculateEnemyRiderGfxOffset
  LDA ObjectBirdLevelAndType,Y
  CLC
  ADC #$02
CalculateFinalRiderGfxPointer
  ASL
  ASL
  ADC Temp5EGfxDataLo
  STA Temp5EGfxDataLo
  LDA #$A8
  STA Temp5F
  LDA GameObjectXCoordHi,Y
  CLC
  ADC GameObjectDirection,Y
  SEC
  SBC #$20
  STA Temp61SpriteXCoord
  LDA #$A1
  STA Temp62
  LDA GameObjectYCoordHi,Y
  SEC
  SBC #$0F
  STA Temp63SpriteYCoord
  JMP AddSpriteToRenderList
PrepareOnPlatformBirdSprite
  STX TempCollisionOverlap
  LDA.w GameObjectState,Y
  CMP #$06                            ; invincible
  BNE RenderBirdOnPlatform
  LDA BirdObjectPaletteAndWidth,Y
  STA Temp60PaletteAndWidth
  LDA BirdSpawnGfxOffsetLUT,Y
  CLC
  LDX GameObjectDirection,Y
  BMI SetSpawnGfxForLeftFacingBird
  ADC #$02
SetSpawnGfxForLeftFacingBird
  STA Temp5E
  LDA GameObjectYCoordHi,Y
  SEC
  SBC #$05
  STA Temp66
  AND #$07
  STA Temp64
  LDA #$A8
  CLC
  ADC Temp64
  STA Temp5F
  LDA Temp66
  LSR
  LSR
  LSR
  STA Temp66
  JMP FinalizeOnPlatformSpriteCoordsAndRender

RenderBirdOnPlatform
  LDA #$1E
  STA Temp60PaletteAndWidth
  LDA #$AB
  STA Temp5FGfxDataHi
  LDA GameObjectFootstepAnimIndex,Y
  ASL
  LDX GameObjectDirection,Y
  BMI SelectLeftFacingWalkFrame
  CLC
  ADC #$0A
SelectLeftFacingWalkFrame
  CLC
  ADC #$C4
  ADC BirdWalkCycleGfxOffsetLUT,Y
  STA Temp5EGfxDataLo
  LDA GameObjectYCoordHi,Y
  SEC
  SBC #$07
  LSR
  LSR
  LSR
  STA Temp66
FinalizeOnPlatformSpriteCoordsAndRender
  LDA GameObjectXCoordHi,Y
  LDX GameObjectDirection,Y
  CLC
  BMI SetLeftFacingOnPlatformSpriteXCoord
  ADC #$01
SetLeftFacingOnPlatformSpriteXCoord
  SEC
  SBC #$20
  STA Temp61SpriteXCoord
  LDX TempCollisionOverlap
  STY Temp65
  LDY Temp66
  JSR UpdateRenderList
  LDY Temp65
  RTS

PrepareBirdSpriteForRenderList
  STX TempCollisionOverlap
  LDA BirdObjectPaletteAndWidth,Y
  STA Temp60PaletteAndWidth
  LDA GameObjectDirection,Y
  BMI SetBirdSpriteBaseGfxForLeftFacing
  LDA #$06
  JMP ContinuePrepareBirdSprite
SetBirdSpriteBaseGfxForLeftFacing
  LDA #$00
ContinuePrepareBirdSprite
  STA Temp5E
  LDA GameObjectInAir,Y
  BEQ SetOnPlatformAnimationOffset
  LDA GameObjectAnimationFrame,Y
  ASL
  JMP CalculateFinalBirdGfxPointer
SetOnPlatformAnimationOffset
  LDA #$04
CalculateFinalBirdGfxPointer
  CLC
  ADC Temp5E
  ADC BirdGfxAnimationOffsetLUT,Y
  STA Temp5E
  LDX TempCollisionOverlap
  LDA #$A8
  STA Temp5F
  LDA GameObjectXCoordHi,Y
  SEC
  SBC #$20
  STA Temp61
  LDA #$9C
  STA Temp62
  LDA GameObjectYCoordHi,Y
  SEC
  SBC #$10
  CMP #$F0
  BCC SkipClampYCoord
  LDA #$00
SkipClampYCoord
  STA Temp63SpriteYCoord

;------------------------------------------------------------------------------
; AddSpriteToRenderList
; Takes sprite parameters from ZP temps and adds one or more 8-pixel tall sprite
; slices to the render list. It handles breaking down taller sprites into
; multiple slices and calculating their positions in the Display List zones.
;------------------------------------------------------------------------------
AddSpriteToRenderList
  STY Temp65               ; save Y
  LDA Temp63SpriteYCoord
  AND #$07
  STA Temp64SpriteYOffset  ; Y offset in zone
  LDA Temp5FGfxDataHi
  CLC
  ADC Temp64SpriteYOffset
  STA Temp5FGfxDataHi
  LDA Temp63SpriteYCoord
  LSR
  LSR
  LSR
  TAY                      ; Y is zone index for this sprite
AddAllSpriteSlicesToRenderListLoop
  JSR UpdateRenderList
  LDA Temp5FGfxDataHi
  SEC
  SBC #$08
  CMP Temp62
  BCC FinishMultiSliceSpriteRender
  STA Temp5FGfxDataHi
  INY
  JMP AddAllSpriteSlicesToRenderListLoop
FinishMultiSliceSpriteRender
  LDY Temp65               ; restore Y
  RTS

;------------------------------------------------------------------------------
; UpdateRenderList
; The core function that adds a single sprite slice to the render list arrays.
; It also decrements the pointer for the corresponding DL zone to make space.
;------------------------------------------------------------------------------
UpdateRenderList
  CPY #$16
  BCS UpdateRenderList_Exit
  INX
  LDA Temp5EGfxDataLo
  STA RenderListGfxDataLo,X
  LDA Temp5FGfxDataHi
  STA RenderListGfxDataHi,X
  LDA Temp60PaletteAndWidth
  STA RenderListPaletteAndWidth,X
  LDA Temp61SpriteXCoord
  STA RenderListXCoord,X

  LDA DisplayListLo,Y
  SEC
  SBC #$04
  STA DisplayListLo,Y
  STA RenderListDLLo,X
  LDA DisplayListHi,Y
  SBC #$00
  STA DisplayListHi,Y
  STA RenderListDLHi,X
UpdateRenderList_Exit
  RTS

;------------------------------------------------------------------------------
; UpdateDLWithBackgroundSprites
; Initializes the static background elements of the Display Lists by copying
; data from ROM tables into their respective DL object locations in RAM.
; This sets up platforms, lava, and other non-interactive scenery.
;------------------------------------------------------------------------------
UpdateDLWithBackgroundSprites
  LDX #$20
BackgroundSpriteUpdateLoop
  LDA GameDLStartLutLo,X
  STA TempPointLo5A
  LDA GameDLStartLutHi,X
  STA TempPointHi5B

  LDA BackgroundSpriteGfxLoLUT,X
  LDY #$00
  STA (TempPointLo5A),Y
  LDA BackgroundSpritePaletteWidthLUT,X
  INY
  STA (TempPointLo5A),Y
  LDA BackgroundSpriteGfxHiLUT,X
  INY
  STA (TempPointLo5A),Y
  LDA BackgroundSpriteXCoordLUT,X
  INY
  STA (TempPointLo5A),Y
  DEX
  BPL BackgroundSpriteUpdateLoop

  LDX #$05
UpdateDLWithMsgAndHUDCharObjects
  LDA HUDCharDLAddrLoLUT,X
  STA TempPointLo5A
  LDA HUDCharDLAddrHiLUT,X
  STA TempPointHi5B

; DL byte 0
  LDA MsgAndHUDCharObjectStringLoLUT,X
  LDY #$00
  STA (TempPointLo5A),Y

; DL byte 1
  LDA #$60 ; character mode
  INY
  STA (TempPointLo5A),Y

; DL byte 2
  LDA MsgAndHUDCharObjectStringHiLUT,X
  INY
  STA (TempPointLo5A),Y

; DL byte 3
  LDA MsgAndHUDCharObjectPaletteAndWidthLUT,X
  INY
  STA (TempPointLo5A),Y

; DL byte 4
  LDA MsgAndHUDCharObjectXLUT,X
  INY
  STA (TempPointLo5A),Y

  DEX
  BPL UpdateDLWithMsgAndHUDCharObjects

  LDA DisplayEasterEggFlag
  BEQ SkipEasterEggDisplaySetup

  LDX #$0E
CopyEasterEggDataToRamLoop
  LDA EasterEggDisplayData,X
  STA $21FF,X
  DEX
  BNE CopyEasterEggDataToRamLoop
  LDA #$30
  STA $1FEF
SkipEasterEggDisplaySetup
  LDX #$09                            ; loop through all of the birds
InitializeBirdPalettesLoop
  LDA BirdPaletteAndWidthLUT,X
  STA BirdObjectPaletteAndWidth,X
  LDA RiderPaletteAndWidthLUT,X
  STA RiderObjectPaletteAndWidth,X
  DEX
  BPL InitializeBirdPalettesLoop
; Initialize the working Display List pointers from the master ROM table
  LDX #$17
InitializeWorkDLPointerLoop
  LDA DisplayListLoLUT,X
  STA DisplayListLo,X
  LDA DisplayListHiLUT,X
  STA DisplayListHi,X
  DEX
  BPL InitializeWorkDLPointerLoop
  LDX #$00
  LDY #$00
CopyWorkDLToFinalDLLLoop
  LDA DisplayListHi,X
  STA $275E,Y
  LDA DisplayListLo,X
  STA $275F,Y
  INY
  INY
  INY
  INX
  CPX #$18
  BNE CopyWorkDLToFinalDLLLoop
  LDX #$18
InitializeScorePopupJumpTable
  LDA ReturnFromScorePopupCreation,X
  STA $27B2,X
  DEX
  BNE InitializeScorePopupJumpTable
EasterEggDisplayData
  RTS

; easter egg data
 .byte $18 
 .byte $29 
 .byte $21 
 .byte $26 
 .byte $21 
 .byte $FD 
 .byte $1B 
 .byte $17 
 .byte $21 
 .byte $23 
 .byte $23 
 .byte $1C 
 .byte $23 
 .byte $1C 

;------------------------------------------------------------------------------
; SetupTitleScrDLs
; Initializes all Display List objects specifically for the title screen.
;------------------------------------------------------------------------------
SetupTitleScrDLs
  LDX #$50
SetupTitleScrCharacterDLs
; destination memory for next DL object...
  LDA TitleScrDLAddrLoLUT,X
  STA TempPointLo5A
  LDA TitleScrDLAddrHiLUT,X
  STA TempPointHi5B

  LDA TitleScrCharObject_LoAddress,X
  LDY #$00
  STA (TempPointLo5A),Y                   ; Byte 0: Lo Address

  LDA #$60                                ; Indirect/Character Object
  INY
  STA (TempPointLo5A),Y                   ; Byte 1: Mode

  LDA TitleScrCharObject_HiAddress,X
  INY
  STA (TempPointLo5A),Y                   ; Byte 2: Hi Address

  LDA TitleScrCharObject_PalleteWidth,X
  INY
  STA (TempPointLo5A),Y                   ; Byte 3: Palette+Width

  LDA TitleScrCharObject_XCoordinate,X
  INY
  STA (TempPointLo5A),Y                   ; Byte 4: X Coordinate

  DEX
  BPL SetupTitleScrCharacterDLs

  LDX #$0C
SetupTitleScrSpriteDLs
; destination memory for next DL object...
  LDA TitleScrCharacterObjectLoLUT,X
  STA TempPointLo5A
  LDA TitleScrCharacterObjectHiLUT,X
  STA TempPointHi5B

  LDA TitleScrSpriteObjectLoLUT,X
  LDY #$00
  STA (TempPointLo5A),Y                    ; Byte 0: Lo Address

  LDA TitleScrSpriteObject_PaletteWidth,X
  INY
  STA (TempPointLo5A),Y                    ; Byte 1: Palette+Width

  LDA TitleScrSpriteObjectHiLUT,X
  INY
  STA (TempPointLo5A),Y                    ; Byte 2: Hi Address

  LDA TitleScrSpriteObject_XCoordinate,X
  INY
  STA (TempPointLo5A),Y                    ; Byte 3: X Coordinate

  DEX
  BPL SetupTitleScrSpriteDLs
  RTS

;------------------------------------------------------------------------------
; SetColorRegisters
; Copies the 24-byte color palette from RAM ($0190) to the hardware color
; registers ($20-$3F) every frame.
;------------------------------------------------------------------------------
SetColorRegisters
  LDA #$00
  STA BACKGRND
  LDA PaletteInRam+$00
  STA P0C1
  LDA PaletteInRam+$01
  STA P0C2
  LDA PaletteInRam+$02
  STA P0C3
  LDA PaletteInRam+$03
  STA P1C1
  LDA PaletteInRam+$04
  STA P1C2
  LDA PaletteInRam+$05
  STA P1C3
  LDA PaletteInRam+$06
  STA P2C1
  LDA PaletteInRam+$07
  STA P2C2
  LDA PaletteInRam+$08
  STA P2C3
  LDA PaletteInRam+$09
  STA P3C1
  LDA PaletteInRam+$0A
  STA P3C2
  LDA PaletteInRam+$0B
  STA P3C3
  LDA PaletteInRam+$0C
  STA P4C1
  LDA PaletteInRam+$0D
  STA P4C2
  LDA PaletteInRam+$0E
  STA P4C3
  LDA PaletteInRam+$0F
  STA P5C1
  LDA PaletteInRam+$10
  STA P5C2
  LDA PaletteInRam+$11
  STA P5C3
  LDA PaletteInRam+$12
  STA P6C1
  LDA PaletteInRam+$13
  STA P6C2
  LDA PaletteInRam+$14
  STA P6C3
  LDA PaletteInRam+$15
  STA P7C1
  LDA PaletteInRam+$16
  STA P7C2
  LDA PaletteInRam+$17
  STA P7C3
  RTS

; well, how did I get here?
; (clear the HUD display string buffer)
  LDX #$67
  LDA #$00
ClearHudDisplayStringBuffer
  STA HudDisplayStringBuffer,X
  DEX
  BPL ClearHudDisplayStringBuffer
  RTS

InitializeFinalDisplayListPointers
  LDX #$56
CopyFinalDllPointersLoop
  LDA DllLUT,X
  STA $2700,X
  STA $2757,X
  DEX
  BPL CopyFinalDllPointersLoop
  RTS

;------------------------------------------------------------------------------
; CreateScorePopupAtObjectLocation
; Creates a score popup (e.g., "500") at a specified location. It finds an
; available popup slot and initializes its timer, position, and character value.
; Input: Temp5E/Temp5F = Y/X coordinates. Temp60 = score value index.
;------------------------------------------------------------------------------
CreateScorePopupAtObjectLocation
  TXA
  PHA
  TYA
  PHA
  LDX #$0B
FindAvailableScorePopupSlot
  LDA $26C4,X
  BEQ FoundAvailableScorePopupSlotInitialize
  DEX
  BPL FindAvailableScorePopupSlot
  LDX #$0B
FoundAvailableScorePopupSlotInitialize
  LDA #$1E
  STA $26C4,X
  LDA Temp5E
  SEC
  SBC #$10
  BPL CalculateScorePopupZone
  CMP #$E0
  BCC CalculateScorePopupZone
  LDA Temp5F
  SEC
  SBC #$12
  STA Temp5F
  LDA #$00
CalculateScorePopupZone
  LSR
  LSR
  LSR
  STA $26DC,X
  LDA Temp5F
  SEC
  SBC #$26
  CMP #$91
  BCC StoreClampedScorePopupXCoord
  CMP #$C0
  BCC ClampScorePopupXTooFarRight
  LDA #$00
  BEQ StoreClampedScorePopupXCoord
ClampScorePopupXTooFarRight
  LDA #$90
StoreClampedScorePopupXCoord
  STA $26D0,X
  LDY Temp60
  LDA ScorePopupValues,Y
  STA $26E8,X
  PLA
  TAY
  PLA
  TAX
ReturnFromScorePopupCreation
  RTS

; Data for score popups
 .byte $00 
 .byte $0E 
 .byte $11 
 .byte $0C 
 .byte $00 
 .byte $11 
 .byte $0C 
 .byte $0C 
 .byte $00 
 .byte $13 

 .byte $11 
 .byte $0C 
 .byte $0D 
 .byte $0C 
 .byte $0C 
 .byte $0C 
 .byte $00 
 .byte $06 
 .byte $01 
 .byte $01 
 .byte $0F 
 .byte $0C 
 .byte $0C 
 .byte $0C 

ScorePopupValues  ; scores 250, 500, 750, ...
 .byte $B3 
 .byte $B7 
 .byte $BB 
 .byte $BF 
 .byte $C3 
 .byte $C7 

;------------------------------------------------------------------------------
; NMI_ROUTINE
; Non-Maskable Interrupt handler. This is the heart of the game's frame-by-frame
; processing. It is responsible for sound, input, palette updates, and, most
; importantly, processing the render list to update the hardware display lists.
; It supports three modes via NMIModeFlag:
;     $00: Normal in-game processing.
;     $01: Title screen, 160A mode (left/right borders).
;     $FF: Title screen, 320A mode (central logo area).
;------------------------------------------------------------------------------
NMI_ROUTINE
NMI_SaveRegisters
  PHA                                     ; Save A
  TXA
  PHA                                     ; Save X
  TYA
  PHA                                     ; Save Y

  LDA NMIModeFlag                         ; Check current NMI mode
  BEQ NMI_ProcessNormalGame               ; If $00, handle normal game NMI
  BMI NMI_ProcessSplitScreen_320A_Mode    ; If $FF (negative), handle 320A part of split screen

; This is the 160A portion of the title screen's split-screen rendering.
; It handles the scrolling borders on the left and right.
NMI_ProcessSplitScreen_160A_Mode          ; NMIModeFlag was $01
  LDA #CTRL_DMA_ON_160AB                  ; Set CTRL for 160A mode (normal width pixels)
  STA CTRL
  JSR CycleTitleScreenPaletteAndCounters  ; Update title screen colors and border animation
  LDA #NMIMode_SplitScreen320A            ; Set flag for the *next* NMI to be the 320A part
  STA NMIModeFlag
  JMP NMI_ExitAndRestoreRegisters

; This is the 320A portion of the title screen's split-screen rendering.
; It handles the central logo area with double-width pixels.
NMI_ProcessSplitScreen_320A_Mode          ; NMIModeFlag was $FF
  STA WSYNC                               ; Wait until the end of the line
  STA WSYNC                               ; Wait until the end of the line
  LDA #CTRL_DMA_ON_320AC                  ; Set CTRL for 320A mode (double-width pixels)
  STA CTRL
  LDA #NMIMode_SplitScreen160A            ; Set flag for the *next* NMI to be the 160A part
  STA NMIModeFlag
  JMP NMI_ExitAndRestoreRegisters

; This is the main NMI handler for gameplay and demo mode.
NMI_ProcessNormalGame
  JSR ServiceSFX                          ; Process active sound effects

; Read Player 0 Fire Button with a one-frame debounce.
; P0LastFire holds the state from the previous NMI, P0CurrentFire holds this NMI's state.
; This prevents a single press from being registered on multiple consecutive frames.
  LDA INPT4                               ; Read Player 0 input port
  AND #Input_FireButtonMask               ; Isolate fire button bit
  CMP P0LastFire                          ; Compare with last latched state
  BEQ NMI_P0_Fire_NoChange                ; If same, no change in press state
  STA P0LastFire                          ; New state, update P0LastFire
  BNE NMI_P0_Fire_StoreCurrent            ; If different and non-zero (pressed), store it as current
NMI_P0_Fire_NoChange
  STA P0CurrentFire                       ; Store current state (either newly pressed or still same as last)
NMI_P0_Fire_StoreCurrent

; Read Player 1 Fire Button with a one-frame debounce.
  LDA INPT5                     ; Read Player 1 input port
  AND #Input_FireButtonMask     ; Isolate fire button bit
  CMP P1LastFire                ; Compare with last latched state
  BEQ NMI_P1_Fire_NoChange      ; If same, no change in press state
  STA P1LastFire                ; New state, update P1LastFire
  BNE NMI_P1_Fire_StoreCurrent  ; If different and non-zero (pressed), store it as current
NMI_P1_Fire_NoChange
  STA P1CurrentFire             ; Store current state
NMI_P1_Fire_StoreCurrent

; Increment global 16-bit frame timer
  INC TimerLo
  BNE NMI_Timer_HiByteNoInc        ; If TimerLo didn't wrap, skip TimerHi increment
  INC TimerHi
NMI_Timer_HiByteNoInc

  JSR SetColorRegisters            ; Update hardware color palette registers from RAM

  LDA RenderListDirty              ; Check if the game logic has prepared a new list of sprites to draw
  BNE ProcessDirtyRenderList       ; If dirty, branch to process it
  JMP NMI_ExitAndRestoreRegisters  ; If not dirty, nothing to render, so exit NMI

ProcessDirtyRenderList
  LDX RenderListCount              ; Get number of sprites in the render list
  BMI FinalizeDisplayListPointers  ; If negative (e.g., $FF from init), skip sprite processing

RenderSpritesFromListToDisplayList
; This loop iterates through the render list (built by the main game logic)
; and writes the 5-byte sprite definitions into the appropriate Display List zones.
  LDA RenderListDLLo,X             ; Get Lo byte of target DL entry address for this sprite
  STA TempPoint5CLo                ; Store in ZP pointer
  LDA RenderListDLHi,X             ; Get Hi byte of target DL entry address
  STA TempPoint5DHi                ; Store in ZP pointer
  LDY #$00                         ; Y will be offset into the 5-byte DL object

  LDA RenderListGfxDataLo,X         ; Byte 0: Graphics Data Lo
  STA (TempPoint5CLo),Y
  INY

  LDA RenderListPaletteAndWidth,X   ; Byte 1: Palette and Width
  STA (TempPoint5CLo),Y
  INY

  LDA RenderListGfxDataHi,X         ; Byte 2: Graphics Data Hi
  STA (TempPoint5CLo),Y
  INY

  LDA RenderListXCoord,X            ; Byte 3: X Coordinate (Horizontal Position)
  STA (TempPoint5CLo),Y

  DEX
  BPL RenderSpritesFromListToDisplayList

; After processing sprites, process score popups (if any)
  LDX #$0B                          ; Max index for score popups (12 popups, 0 to 11)
ProcessScorePopupsLoop
  LDA ScorePopup_ActiveTimerBase,X  ; Check if this score popup timer is active
  BEQ SkipThisScorePopup            ; If timer is 0, skip this popup
  DEC ScorePopup_ActiveTimerBase,X  ; Decrement active timer

  LDY ScorePopup_ZoneIndexBase,X    ; Y = Zone index for this score popup
  SEC
  LDA DisplayListLo,Y               ; Get current Lo pointer for this zone's DL
  SBC #$05                          ; Make space for a 5-byte character DL object by moving the pointer up
  STA DisplayListLo,Y               ; Store updated Lo pointer
  STA TempPoint5CLo                 ; Use ZP pointer for indirect write
  LDA DisplayListHi,Y               ; Get current Hi pointer
  SBC #$00                          ; Adjust Hi pointer if Lo underflowed
  STA DisplayListHi,Y               ; Store updated Hi pointer
  STA TempPoint5DHi                 ; Use ZP pointer

  LDY #$00                          ; Y = offset into DL object
  LDA ScorePopup_CharDataLoBase,X   ; Byte 0: Character Data Lo (e.g., '2' for "250")
  STA (TempPoint5CLo),Y
  INY
  LDA #ScorePopup_DLModeByte        ; Byte 1: Mode (Indirect Character)
  STA (TempPoint5CLo),Y
  INY
  LDA #ScorePopup_DLCharHiByte      ; Byte 2: Character Data Hi (High byte of HSC font base)
  STA (TempPoint5CLo),Y
  INY
  LDA #$7C                          ; Byte 3: Palette & Width (seems fixed for score popups)
  STA (TempPoint5CLo),Y
  INY
  LDA ScorePopup_XCoordBase,X       ; Byte 4: X Coordinate
  STA (TempPoint5CLo),Y
SkipThisScorePopup
  DEX                               ; Next score popup slot
  BPL ProcessScorePopupsLoop        ; Loop if X is not negative

FinalizeDisplayListPointers
                                    ; Copy the adjusted DisplayListLo/Hi pointers (from $242B/$2443)
                                    ; into the final RAM area ($275E onwards) that Maria's DLL will use.
                                    ; This is done in reverse order because the final DLL structure is 3 bytes
                                    ; per entry (Interrupt/Height, Hi, Lo) and it's easier to fill from the end.
  LDX #$17                          ; Number of DL zones - 1 (0 to 23)
  LDY #$45                          ; Offset into FinalDLL_PointerStorage (starts at $275E + $45 = $27A3)
CopyDLLPointersToFinalRAMLoop
  LDA DisplayListHi,X               ; Get Hi byte of DL pointer for zone X
  STA FinalDLL_PointerStorage_Hi,Y  ; Store it
  LDA DisplayListLo,X               ; Get Lo byte of DL pointer for zone X
  STA FinalDLL_PointerStorage_Lo,Y  ; Store it
  DEY                               ; Next storage slot (lower address)
  DEY
  DEY                               ; DLL entries are 3 bytes (interrupt+height, Hi, Lo)
  DEX                               ; Next zone
  BPL CopyDLLPointersToFinalRAMLoop

  INX                  ; X = 0
  STX RenderListDirty  ; Clear the dirty flag, signaling the main loop that rendering is complete.

NMI_ExitAndRestoreRegisters
  PLA  ; Restore Y
  TAY
  PLA  ; Restore X
  TAX
  PLA  ; Restore A
       ; Fall through to IRQ RTI

IRQ_ROUTINE
  RTI 

;------------------------------------------------------------------------------
; Title Screen and Menu Logic
;------------------------------------------------------------------------------
AttractModeMain
  JSR SetupTitleScreen
  LDA TimerLo
  STA $6E
  SEC
  SBC #$01
  STA Temp68
  LDA #$01
  STA Temp69
AttractMode_FrameLoop
  LDA TimerLo
  CMP $6E
  BEQ AttractMode_FrameLoop
  STA $6E
  JSR HandleDemoModeJoyInput
  JSR AnimateTitleScreenFrame
  LDA Temp68
  CMP TimerLo
  BNE AttractMode_FrameLoop
  DEC Temp69
  BPL AttractMode_FrameLoop
  LDA #$00
  STA Temp62
AttractMode_HSC_Loop
  JSR DoHscStatusCheck
  BMI HscLoopContinueOrExit
  JSR DoHscAttractMode
  LDA INPT4
  AND INPT5
  JSR CheckForAttractModeExit
HscLoopContinueOrExit
  INC Temp62
  LDA Temp62
  CMP #$04
  BCC AttractMode_HSC_Loop
  RTS

SetupTitleScreen
  SEI
  LDA #$60
  STA CTRL
  JSR ClearRamMuteAudio
  JSR LoadTitlePalette
  JSR SetColorRegisters
  JSR SetupTitleScrText
  JSR SetupTitleScrDLs
WaitVblankOver
  BIT MSTAT
  BMI WaitVblankOver
WaitVblankStart
  BIT MSTAT
  BPL WaitVblankStart

; ** We're now in vblank

  LDA #$00
  STA DPPL
  LDA #$27
  STA DPPH  ; Register $2700 as the DLL location

  LDA #$40  ; DMA on, single-width chars
  STA CTRL
  RTS

DisplayGameOptions
  JSR SetupTitleScreen
  LDX #$0F
CopyPlayerStringLoop
  LDA StringOnePlayer,X
  STA MenuLine2CharBuffer,X
  LDA StringTwoPlayer,X
  STA MenuLine1CharBuffer,X
  DEX
  BPL CopyPlayerStringLoop
  JSR SetupGameOptionsText
  JSR SetMenuJoyDebounceTimerTo20
  JSR SetMenuSelectDebounceTimerTo20
  JSR SetMenuResetTimer  ; MenuResetTimer = Timerhi - 1
GameOptionsMenuLoop
  JSR CheckMenuFireButtonAndStartGame
  JSR SetupGameOptionsText
  JSR AnimateTitleScreenFrame
  LDA SWCHA
  AND #$F0
  CMP #$F0
  BNE MenuJoyInput_ProcessAfterDebounce
  LDA #$00
  STA MenuJoyDebounceTimer
GoMenuHandlingAndUpdate
  JMP MenuHandlingAndUpdate

; MenuJoyInput_ProcessAfterDebounce
; This code is reached after the joystick debounce timer (MenuJoyDebounceTimer)
; has expired, indicating it's time to process joystick input for the menu.
; Part of the DisplayGameOptions routine.

MenuJoyInput_ProcessAfterDebounce
  DEC MenuJoyDebounceTimer         ; Decrement debounce timer
  BPL GoMenuHandlingAndUpdate      ; If timer still active, skip input processing for now
  JSR SetMenuJoyDebounceTimerTo20  ; Reset debounce timer for next input

  LDA SWCHA                        ; Read Player 1 Joystick & Console Switches
                                   ; SWCHA bits (active low for joystick)
                                   ; 7654 3210
                                   ; ---- RLDU (Right, Left, Down, Up for P1 Joystick)

  ASL                              ; Shift P1 Right (Bit 3) into Carry
  BCC HandleP0JoyRight
  ASL                              ; Shift P1 Left (Bit 2) into Carry
  BCC HandleP0JoyLeft
  ASL                              ; Shift P1 Down (Bit 1) into Carry
  BCS MenuJoyInput_CheckP1UpOrNoVerticalInput
  JMP MenuSelectOnePlayer          ; P1 Down IS pressed: Select 1 Player

MenuJoyInput_CheckP1UpOrNoVerticalInput
  ASL                              ; Shift P1 Up (Bit 0) into Carry
  BCS HandleP0JoyRight
  JMP MenuSelectTwoPlayers         ; P1 Up IS pressed: Select 2 Players

;------------------------------------------------------------------------------
; HandleP0JoyRight
; Increases the game difficulty, capping at Difficulty_Expert.
; Resets the counters used for the menu easter egg.
;------------------------------------------------------------------------------
HandleP0JoyRight
  LDX Difficulty
  CPX #Difficulty_Expert      ; Max difficulty?
  BPL SkipDifficultyIncrease  ; If at or above max, skip
  INX                         ; Increase difficulty
  STX Difficulty
  JSR ClearScores             ; Clear scores when difficulty changes
SkipDifficultyIncrease
  LDA #$00                    ; Reset easter egg press counters
  STA EasterEggJoyDownCount
  STA EasterEggJoyLeftCount
  JMP MenuTextUpdate          ; Redraw menu text

;------------------------------------------------------------------------------
; HandleP0JoyLeft
; Decreases the game difficulty, bottoming out at Difficulty_Beginner.
; Jumps to update counters used for the menu easter egg.
;------------------------------------------------------------------------------
HandleP0JoyLeft
  LDX Difficulty
  BEQ SkipDifficultyDecrease  ; If already at Beginner, skip
  DEX                         ; Decrease difficulty
  STX Difficulty
  JSR ClearScores             ; Clear scores when difficulty changes
SkipDifficultyDecrease
  JMP MenuUpdateCounter

; This section seems to be entirely dead. 
; Emulation debugging during regular play shows no access.
; In assembly it would be "BIT $56, ORA $03, KIL, KIL".
; It's not accessed by Maria, and isn't sensible in ASCII nor the game's character set.
 .byte $24  ; $9FFA
 .byte $56  ; $9FFB
 .byte $05  ; $9FFC
 .byte $03  ; $9FFD
 .byte $12  ; $9FFE
 .byte $12  ; $9FFF

; ORG $A000

; This upcoming bit is a bit ugly. It's mostly Maria gfx data, but they stuck some
; 6502 data in the middle of each page.

 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00

; A058

BirdWalkCycleGfxOffsetLUT
 .byte $00, $14, $28, $28, $28, $28, $28, $28, $28, $28

BirdSpawnGfxOffsetLUT
 .byte $82, $86, $8A, $8A, $8A, $8A, $8A, $8A, $8A, $8A 

BirdPaletteAndWidthLUT
 .byte $BE,$DE,$FE,$FE,$FE,$FE,$FE,$FE,$FE,$FE

RiderPaletteAndWidthLUT
 .byte $7E,$7E,$5E,$5E,$5E,$5E,$5E,$5E,$5E,$5E

BirdGfxAnimationOffsetLUT
 .byte $08, $14, $20, $20, $20

; This doesn't seem to be reached by Sally or Maria
 .byte $20, $20, $20, $20, $20

; This is graphics data (A08E)
 .byte $03, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $F0, $3F, $55, $55, $55, $55, $55, $55
 .byte $55, $55, $55, $55, $55, $7F, $FD, $55
 .byte $55, $55, $55, $55, $55, $55, $55, $55
 .byte $55, $55, $7C, $00, $EB, $FF, $FF, $FB
 .byte $FF, $FA, $AF, $FF, $BE, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $EB, $FB, $AF, $AF
 .byte $FE, $FF, $AB, $BF, $FF, $AF, $FF, $FF
 .byte $A8, $0F, $BE, $A0, $00, $AF, $F8, $2F
 .byte $FF, $A8, $BB, $FA, $FC, $0F, $FF, $AF
 .byte $80, $FF, $EB, $E8, $2A, $C2, $BF, $E8
 .byte $F0, $EE, $AF, $00, $00, $00, $00, $00

 ; A100

 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00 

; A180

TitleScrSpriteObject_PaletteWidth
 .byte $FA, $FF, $F7, $FD, $F6, $F6, $FA, $F6
 .byte $F6, $FA, $33, $33, $34

 .byte $00  ; not used

; A18E

 .byte $0E, $FA 
 .byte $AA, $AA, $AA, $AA, $AA, $AA, $AA, $AA
 .byte $AA, $AA, $AB, $FF, $EA, $AA, $AA, $AA
 .byte $AA, $AA, $AA, $AA, $AA, $AA, $AA, $AF
 .byte $FC, $2F, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $00, $AB, $FF, $BF, $FA
 .byte $BF, $FF, $EA, $BE, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $EA, $BE, $BE, $AA, $AE, $FA
 .byte $AF, $FE, $AF, $EE, $AB, $FB, $AB, $FE
 .byte $AA, $0E, $BF, $A8, $02, $BF, $FE, $FF
 .byte $FA, $FE, $BF, $BF, $EA, $FF, $FE, $FB
 .byte $B3, $FF, $BA, $EA, $2F, $A2, $FA, $B0
 .byte $E0, $AA, $BB, $FC, $0A, $C0, $00, $00 

; A200

 .byte $F8, $00, $F8, $00, $3E, $00, $F0, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00

; A280

TitleScrSpriteObject_XCoordinate
 .byte $41, $2F, $0F, $57, $31, $57, $7D, $31
 .byte $57, $7D, $B4, $B4, $B8

 .byte $00  ; not used

; A28E
 .byte $02, $BF 

 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $F8, $0D, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $DF, $F7, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $DF, $00, $3A, $FB, $EB, $FF
 .byte $BF, $FF, $FA, $EF, $FF, $FF, $FE, $AF
 .byte $FE, $BF, $BE, $EE, $FF, $FA, $AB, $FF
 .byte $FF, $FE, $AF, $AA, $FA, $AF, $3F, $EA
 .byte $AA, $83, $FF, $E8, $00, $BF, $FF, $BF
 .byte $EF, $FF, $FE, $FE, $BE, $BF, $FE, $AF
 .byte $FE, $AF, $FE, $3F, $AB, $EF, $B8, $02
 .byte $AF, $FF, $FC, $AF, $BA, $F0, $00, $00

; A300

 .byte $BE, $00, $BE, $00, $3F, $00, $FC, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $FC, $3A, $7C
 .byte $00, $00, $3F, $00, $3D, $AC, $00, $00
 .byte $00, $00, $BB, $C0, $00, $00, $00, $00
 .byte $03, $EE, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00

; A380

PterryFrameIndexAdjustmentLUT
 .byte $01, $01, $FF, $FF 

BirdBornSFXType
 .byte $09, $09, $0C, $0C, $0C, $0C, $0C, $0C, $0C, $0C

; A38E

 .byte $0F, $AF 

 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FA
 .byte $A8, $37, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $F7, $DF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $F7, $C0, $3E, $BF, $FF, $FF
 .byte $FE, $AA, $EE, $AA, $FF, $FF, $EA, $FE
 .byte $FB, $AF, $FF, $EF, $EF, $FF, $BF, $FF
 .byte $FF, $FA, $EA, $BF, $FA, $A8, $03, $AB
 .byte $EA, $A2, $FF, $FA, $00, $FF, $FF, $EF
 .byte $FF, $FF, $EB, $FF, $FF, $EF, $FF, $EE
 .byte $EA, $BF, $FA, $AF, $BA, $AA, $A0, $0A
 .byte $FF, $FF, $F0, $EA, $FA, $EF, $AC, $00

; A400

 .byte $BF, $80, $BF, $80, $3F, $C0, $FF, $40
 .byte $00, $00, $0F, $B0, $00, $00, $00, $00
 .byte $0E, $F0, $00, $00, $03, $AF, $05, $6F
 .byte $02, $BF, $FA, $C0, $F9, $50, $FE, $80
 .byte $00, $00, $EE, $F0, $00, $00, $00, $00 
 .byte $0F, $BB, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00 
 .byte $00, $00, $00, $00, $00, $00, $00, $00 
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00 ,$00, $00
 .byte $00, $00, $00, $00, $00, $00

; A48E

 .byte $0F, $AB

 .byte $FF, $EA, $AE, $AA, $AB, $FA, $AA, $FF
 .byte $FF, $FF, $AB, $FA, $AB, $FF, $EA, $AA
 .byte $AA, $BE, $EA, $AB, $FB, $FA, $AA, $AA
 .byte $FC, $3F, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $80, $0F, $AF, $FF, $FF
 .byte $FA, $AF, $FE, $FB, $BF, $FF, $EF, $FF
 .byte $EF, $FF, $FF, $FB, $FF, $FF, $EB, $EA
 .byte $BF, $FB, $FF, $FF, $FE, $BE, $00, $BF
 .byte $FE, $A8, $FF, $EA, $80, $3F, $FF, $FE
 .byte $FF, $FF, $EF, $FF, $FF, $FF, $EA, $FB
 .byte $BA, $BF, $FF, $EA, $AA, $BE, $88, $AA
 .byte $FF, $AA, $A0, $EF, $FE, $BF, $FF, $00

; A500

 .byte $3F, $E0, $3F, $E0, $3F, $F0, $FD, $80
 .byte $03, $F8, $03, $B8, $03, $B8, $2F, $C0
 .byte $2E, $C0, $2E, $C0, $05, $A8, $06, $A8
 .byte $06, $A8, $2A, $50, $2A, $90, $2A, $90
 .byte $01, $A8, $37, $AC, $45, $B8, $2A, $40
 .byte $3A, $DC, $2E, $51, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00

; A58E

 .byte $05, $55 

 .byte $55, $55, $55, $55, $55, $55, $57, $55
 .byte $55, $55, $D5, $55, $55, $55, $55, $55
 .byte $55, $55, $55, $55, $55, $55, $55, $55
 .byte $54, $3F, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $80, $0F, $EB, $FF, $FF
 .byte $BB, $FF, $FF, $FF, $FF, $FE, $FF, $FF
 .byte $BF, $FF, $FF, $FF, $FF, $FF, $FE, $BF
 .byte $EF, $FF, $FF, $FF, $FF, $AC, $00, $FF
 .byte $FA, $AA, $BE, $AA, $A0, $2F, $FF, $FF
 .byte $AA, $AF, $FF, $FE, $FF, $FF, $AF, $BF
 .byte $FE, $FF, $FF, $AA, $BB, $FF, $AA, $AB
 .byte $FA, $FA, $00, $EB, $BF, $AE, $AF, $C0

; A600

 .byte $3F, $F0, $3F, $FC, $BE, $FC, $39, $D0
 .byte $0F, $FF, $0F, $EB, $0F, $EE, $FF, $F0
 .byte $EB, $F0, $BB, $F0, $36, $A0, $35, $A0
 .byte $35, $A0, $0A, $9C, $0A, $5C, $0A, $5C
 .byte $46, $BA, $06, $E6, $56, $AE, $AE, $91
 .byte $9B, $90, $BA, $95, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00

; A68E
 
 .byte $05, $55 

 .byte $55, $55, $55, $55, $55, $55, $5D, $55
 .byte $55, $55, $75, $55, $55, $55, $55, $55
 .byte $55, $55, $55, $55, $55, $55, $55, $55
 .byte $54, $EB, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FB, $EF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FB, $A0, $0E, $AF, $BF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $EF, $BC, $00, $EB
 .byte $EF, $FA, $BA, $FF, $AA, $2F, $FF, $EE
 .byte $AB, $EA, $FF, $FF, $FF, $FE, $FF, $FF
 .byte $FF, $FF, $FF, $AB, $EB, $FF, $AA, $AF
 .byte $EB, $FF, $00, $FF, $FA, $AF, $AB, $C0

; A700

 .byte $3F, $F8, $3F, $F8, $BE, $FF, $3E, $60
 .byte $3C, $EE, $1C, $BA, $1F, $FA, $BB, $3C
 .byte $AE, $34, $AF, $F4, $16, $9B, $16, $80
 .byte $16, $80, $E6, $94, $02, $94, $02, $94
 .byte $56, $AC, $46, $A9, $1A, $BB, $3A, $95
 .byte $6A, $91, $EE, $A4, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $40, $00, $15, $54
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $15, $54, $00, $01
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00

; A78E

 .byte $05, $55 

 .byte $55, $55, $55, $55, $55, $55, $75, $55
 .byte $55, $55, $5D, $55, $55, $55, $55, $55
 .byte $55, $55, $55, $55, $55, $55, $55, $55
 .byte $54, $FE, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $EF, $FB, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $EF, $80, $0A, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FF, $FF, $FF, $FF, $EF, $FC, $00, $3B
 .byte $EF, $FF, $AB, $FF, $AA, $AB, $EF, $EE
 .byte $BF, $FF, $AE, $FF, $FF, $FF, $FF, $FF
 .byte $FB, $FF, $FE, $AF, $FF, $FF, $EB, $FF
 .byte $FF, $FF, $00, $55, $55, $55, $55, $50

; A800

 .byte $3F, $F4, $3E, $F4, $FE, $F0, $3E, $F0
 .byte $13, $F8, $13, $FE, $13, $FC, $2F, $C4
 .byte $BF, $C4, $3F, $C4, $16, $57, $16, $00
 .byte $16, $00, $D5, $94, $00, $94, $00, $94
 .byte $16, $BA, $56, $A8, $0A, $A0, $AE, $94
 .byte $2A, $95, $0A, $A0, $00, $3F, $C0, $00
 .byte $50, $3F, $C0, $00, $14, $F1, $55, $50
 .byte $00, $00, $C3, $30, $00, $00, $C3, $00
 .byte $00, $00, $C0, $00, $00, $03, $FC, $00
 .byte $00, $03, $FC, $05, $05, $55, $4F, $14
 .byte $33, $0C, $00, $00, $03, $0C, $00, $00
 .byte $00, $0C, $00, $00, $00, $14, $14, $00
 .byte $00, $28, $28, $00, $00, $14, $14, $00
 .byte $00, $28, $28, $00, $00, $3C, $3C, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00

; A88E

 .byte $FF, $FF 

 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $00
 .byte $00, $00, $0F, $EF, $EA, $AA, $80, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00

; A8C4
BirdSpawnInvulnAnimStateSequence_TypeA
 .byte $00, $00, $00, $00 
 .byte $00, $00, $00, $01, $02, $03, $04, $02
 .byte $03, $04, $02, $03, $04, $02, $03, $04
 .byte $02, $03, $04, $02, $03, $04, $02, $03
 .byte $04, $02, $03, $04, $02, $03, $04, $05
 .byte $06, $05, $06, $05, $06, $05, $06, $05
 .byte $06, $05, $06, $05, $06, $05, $06, $05
 .byte $06, $05, $06, $05, $06, $05, $06, $07

; A900

 .byte $3F, $FC, $3E, $FC, $FA, $FC, $3E, $A0
 .byte $10, $FC, $10, $00, $10, $00, $3F, $04
 .byte $00, $04, $00, $04, $38, $9A, $17, $00
 .byte $17, $00, $A6, $2C, $00, $D4, $00, $D4
 .byte $00, $AB, $14, $00, $00, $00, $EA, $00
 .byte $00, $14, $00, $00, $00, $FF, $FF, $C0
 .byte $35, $F1, $55, $C0, $05, $FD, $55, $00
 .byte $F3, $CF, $33, $C0, $03, $03, $00, $C0
 .byte $00, $00, $00, $00, $03, $FF, $FF, $00
 .byte $03, $55, $4F, $5C, $00, $55, $7F, $50
 .byte $0F, $33, $CF, $3C, $0C, $03, $03, $00
 .byte $00, $00, $00, $00, $00, $15, $54, $00
 .byte $00, $2A, $A8, $00, $00, $15, $54, $00
 .byte $00, $2A, $A8, $00, $00, $3F, $FC, $00
 .byte $00, $00, $00, $00, $00, $00, $82, $08
 .byte $02, $A8, $8A, $00, $02, $82, $00, $20
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00

; A98E

 .byte $FF, $FF 

 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $00
 .byte $00, $00, $AE, $BE, $BE, $FF, $A0, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $2A
 .byte $A0, $00, $00, $00, $3C, $EB, $EB, $3F
 .byte $A0, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $10

; A9C4

BirdSpawnInvulnAnimStateSequence_TypeB
 .byte $02, $02, $02, $02
 .byte $02, $02, $02, $00, $04, $04, $04, $04
 .byte $04, $04, $03, $03, $03, $03, $03, $03
 .byte $02, $02, $02, $02, $02, $02, $02, $02
 .byte $02, $01, $01, $01, $00, $00, $00, $04
 .byte $04, $04, $04, $03, $03, $03, $03, $02
 .byte $02, $02, $02, $02, $02, $01, $01, $01
 .byte $01, $01, $01, $00, $00, $00, $00, $00

; AA00

 .byte $3F, $CC, $3E, $CF, $FB, $CF, $3E, $F0
 .byte $10, $3E, $1C, $00, $30, $00, $BC, $04
 .byte $00, $34, $00, $0C, $38, $2B, $1C, $00
 .byte $1C, $00, $E8, $2C, $00, $34, $00, $34
 .byte $00, $2A, $00, $00, $00, $00, $A8, $00
 .byte $00, $00, $00, $00, $03, $C7, $FF, $FF
 .byte $3D, $45, $55, $5F, $FD, $45, $55, $CF
 .byte $33, $33, $3C, $00, $33, $30, $0C, $00
 .byte $33, $30, $00, $00, $FF, $FF, $D3, $C0
 .byte $F5, $55, $51, $7C, $F3, $55, $51, $7F
 .byte $00, $F3, $33, $30, $00, $C0, $33, $30
 .byte $00, $00, $33, $30, $00, $15, $54, $00
 .byte $00, $2A, $A8, $00, $00, $15, $54, $00
 .byte $00, $2A, $A8, $00, $00, $3F, $FC, $00
 .byte $00, $00, $00, $00, $20, $20, $00, $00
 .byte $23, $E2, $8A, $80, $08, $82, $03, $28
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00

; AA8E

 .byte $FF, $FF 

 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $00
 .byte $00, $FF, $FB, $BF, $AB, $FE, $A0, $00
 .byte $00, $00, $00, $00, $00, $02, $80, $AA
 .byte $BC, $00, $00, $00, $AA, $BF, $FF, $FF
 .byte $FE, $EE, $00, $2A, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $10, $00
 .byte $00, $00, $10, $44

; AAC4

MaxEnemyBirdCountLUT
 .byte $06, $06, $06, $06, $06, $06, $08, $08, $08, $08

EggWaveEnemyTypeLUT
 .byte $00, $00, $01, $01, $01, $01, $01, $01, $01, $01

PlatformsInLevelLUT
 .byte $1F, $1F, $0F, $0F, $0D, $0C, $0C, $04, $0F, $07
 .byte $04, $04, $0F, $0F, $0F, $0F, $0E, $0E, $06, $06
 .byte $04, $04, $04, $04, $0F, $0F, $0D, $0D, $07, $0D
 .byte $05, $05, $0E, $06, $06, $04, $07, $0D, $05, $05

; AB00

 .byte $3C, $C0, $3C, $C0, $FC, $F0, $3E, $A0
 .byte $10, $3E, $1C, $00, $10, $00, $BC, $04
 .byte $00, $34, $00, $04, $34, $3F, $10, $00
 .byte $10, $00, $FC, $1C, $00, $04, $00, $04
 .byte $00, $2B, $00, $00, $00, $00, $E8, $00
 .byte $00, $00, $00, $00, $55, $07, $FC, $00
 .byte $05, $03, $FF, $C0, $01, $0D, $55, $5C
 .byte $CF, $30, $CF, $3C, $CC, $30, $00, $F0
 .byte $0C, $00, $00, $30, $00, $3F, $D0, $55
 .byte $03, $FF, $C0, $50, $35, $55, $70, $40
 .byte $F3, $CC, $33, $CC, $3C, $00, $30, $CC
 .byte $30, $00, $00, $C0, $55, $51, $45, $55
 .byte $AA, $A2, $8A, $AA, $55, $51, $45, $55
 .byte $AA, $A2, $8A, $AA, $FF, $F3, $CF, $FF
 .byte $00, $00, $01, $00, $01, $00, $08, $80
 .byte $23, $E0, $8E, $0C, $22, $A2, $00, $80
 .byte $28, $A0, $04, $14, $14, $10, $20, $38
 .byte $2C, $08, $70, $50, $05, $0D

; AB8E

 .byte $FF, $FF 

 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $00
 .byte $3A, $FF, $FF, $FF, $FF, $AA, $FF, $00
 .byte $00, $00, $00, $00, $00, $AA, $AB, $AB
 .byte $AA, $BF, $00, $2A, $BF, $AA, $BF, $FF
 .byte $FF, $EA, $FE, $AA, $B0, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $10
 .byte $00, $00, $00, $10, $04, $14, $01, $40
 .byte $00, $50, $00, $14, $04, $10, $14, $10
 .byte $01, $40, $05, $00, $14, $00, $04, $10
 .byte $14, $05, $01, $80, $00, $10, $00, $04
 .byte $1C, $60, $50, $14, $02, $40, $04, $00
 .byte $10, $00, $09, $34, $70, $50, $06, $00
 .byte $01, $80, $00, $60, $14, $50, $05, $0D
 .byte $00, $90, $02, $40, $09, $00, $05, $14

; AC00

 .byte $0C, $C0, $3C, $F0, $FB, $3C, $3E, $F0
 .byte $14, $3F, $14, $00, $14, $00, $FC, $14
 .byte $00, $14, $00, $14, $04, $0F, $10, $00
 .byte $10, $00, $F0, $10, $00, $04, $00, $04
 .byte $00, $0E, $00, $00, $00, $00, $B0, $00
 .byte $00, $00, $00, $00, $05, $41, $FC, $00
 .byte $10, $00, $00, $00, $04, $00, $00, $00
 .byte $0C, $33, $33, $30, $0C, $33, $03, $30
 .byte $00, $33, $03, $00, $00, $3F, $41, $50
 .byte $00, $00, $00, $04, $00, $00, $00, $10
 .byte $33, $33, $30, $C0, $33, $03, $30, $C0
 .byte $03, $03, $30, $00, $00, $15, $54, $00
 .byte $00, $2A, $A8, $00, $00, $15, $54, $00
 .byte $00, $2A, $A8, $00, $00, $3F, $FC, $00
 .byte $01, $40, $04, $40, $01, $10, $80, $08
 .byte $AB, $22, $BE, $2C, $23, $E0, $0A, $28
 .byte $AA, $A8, $01, $04, $10, $40, $08, $08
 .byte $20, $20, $30, $10, $04, $0C

; AC8E

 .byte $FF, $FF 

 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $00
 .byte $FF, $FA, $FF, $FF, $FF, $FF, $EF, $00
 .byte $00, $00, $00, $00, $00, $FF, $AA, $EA
 .byte $FF, $FF, $00, $AE, $FF, $FF, $FF, $FF
 .byte $FF, $AB, $EB, $AB, $F0, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $DC
 .byte $00, $10, $00, $00, $01, $04, $00, $48
 .byte $00, $10, $00, $04, $04, $10, $10, $40
 .byte $21, $00, $04, $00, $10, $00, $04, $10
 .byte $05, $04, $02, $40, $00, $10, $00, $04
 .byte $04, $70, $10, $50, $01, $80, $04, $00
 .byte $10, $00, $0D, $10, $30, $10, $01, $00
 .byte $00, $40, $00, $10, $05, $24, $04, $0C
 .byte $00, $40, $01, $00, $04, $00, $18, $50

; AD00

 .byte $00, $00, $30, $30, $F3, $CC, $3E, $A0
 .byte $54, $0E, $54, $00, $54, $00, $B0, $15
 .byte $00, $15, $00, $15, $14, $0F, $50, $00
 .byte $50, $00, $F0, $14, $00, $05, $00, $05
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $5F, $C0
 .byte $40, $00, $00, $00, $10, $00, $00, $00
 .byte $30, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $03, $F5, $00, $00
 .byte $00, $00, $00, $01, $00, $00, $00, $04
 .byte $00, $00, $00, $30, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $24, $18, $00
 .byte $00, $18, $24, $00, $00, $34, $1C, $00
 .byte $00, $18, $24, $00, $00, $2C, $38, $00
 .byte $00, $00, $01, $00, $01, $00, $08, $80
 .byte $2B, $2A, $0F, $2C, $A8, $0A, $AE, $AB
 .byte $AA, $AA, $01, $44, $11, $40, $02, $20
 .byte $08, $80, $24, $10, $04, $18

; AD8E

 .byte $FF,$FF 

 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $0E
 .byte $FF, $FF, $FD, $55, $55, $57, $FE, $00
 .byte $00, $0B, $AE, $C0, $0E, $FF, $FF, $FA
 .byte $FA, $AF, $03, $FF, $EF, $55, $55, $55
 .byte $FA, $BF, $FE, $BF, $FE, $00, $05, $55
 .byte $55, $50, $0A, $AA, $AA, $A0, $00, $00
 .byte $10, $44, $00, $00, $01, $44, $00, $44
 .byte $00, $14, $00, $04, $01, $04, $11, $40
 .byte $11, $00, $14, $00, $10, $00, $10, $40
 .byte $01, $14, $00, $54, $00, $90, $14, $14
 .byte $01, $10, $14, $40, $15, $00, $06, $00
 .byte $14, $14, $04, $40, $24, $10, $01, $00
 .byte $00, $40, $30, $10, $01, $14, $04, $18
 .byte $00, $40, $01, $00, $04, $0C, $14, $40

; AE00

 .byte $00, $00, $00, $00, $F0, $F0, $2F, $B0
 .byte $10, $0B, $10, $00, $10, $00, $E0, $04
 .byte $00, $04, $00, $04, $38, $03, $D0, $00
 .byte $D0, $00, $C0, $2C, $00, $07, $00, $07
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $05, $5C
 .byte $00, $00, $00, $00, $40, $00, $00, $00
 .byte $C0, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $3F, $50, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $01
 .byte $00, $00, $00, $0C, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $14, $14, $00
 .byte $00, $28, $28, $00, $00, $14, $14, $00
 .byte $00, $28, $28, $00, $00, $3C, $3C, $00
 .byte $00, $00, $00, $00, $20, $20, $00, $00
 .byte $33, $AA, $0F, $AB, $AE, $2A, $A2, $8B
 .byte $AA, $2A, $00, $54, $15, $00, $00, $B0
 .byte $0E, $00, $04, $50, $05, $10

; AE8E

 .byte $FF, $FF 

 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $35
 .byte $55, $55, $75, $55, $55, $55, $D5, $40
 .byte $02, $BB, $FF, $A0, $0A, $AA, $BF, $BF
 .byte $EA, $BB, $0F, $FF, $5D, $55, $55, $55
 .byte $7F, $FF, $FF, $FF, $FF, $C0, $15, $55
 .byte $55, $54, $2A, $AA, $AA, $A8, $00, $00
 .byte $00, $10, $00, $00, $00, $54, $00, $54
 .byte $00, $14, $01, $4C, $01, $44, $15, $00
 .byte $15, $00, $14, $00, $31, $40, $11, $40
 .byte $03, $D4, $03, $DC, $01, $DC, $07, $DC
 .byte $01, $55, $17, $C0, $37, $C0, $37, $40
 .byte $37, $D0, $55, $40, $04, $50, $01, $04
 .byte $00, $40, $0E, $D0, $00, $54, $05, $10
 .byte $10, $40, $01, $00, $07, $B0, $15, $00

; AF00

 .byte $00, $00, $00, $00, $C0, $00, $0B, $C0
 .byte $00, $03, $00, $00, $00, $00, $C0, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $54
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $15, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $14, $14, $00
 .byte $00, $28, $28, $00, $00, $14, $14, $00
 .byte $00, $28, $28, $00, $00, $3C, $3C, $00
 .byte $00, $00, $00, $00, $00, $00, $82, $08
 .byte $C3, $A2, $BC, $3E, $A8, $22, $80, $00
 .byte $28, $0A, $00, $50, $05, $00, $00, $00
 .byte $00, $00, $0A, $90, $06, $A0

; AF8E

 .byte $FF, $FF 

 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $15
 .byte $55, $55, $D5, $55, $55, $55, $75, $40
 .byte $0A, $AA, $BE, $A8, $15, $55, $55, $55
 .byte $55, $57, $15, $55, $75, $55, $55, $55
 .byte $5D, $55, $55, $55, $55, $50, $55, $55
 .byte $55, $55, $AA, $AA, $AA, $AA, $00, $00
 .byte $00, $00, $00, $00, $00, $50, $00, $54
 .byte $00, $50, $00, $50, $00, $00, $05, $00
 .byte $15, $00, $05, $00, $05, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $0A, $90, $01, $90
 .byte $02, $60, $03, $E0, $00, $00, $06, $A0
 .byte $06, $40, $09, $80, $0B, $C0, $00, $00

GameColdInit
ESGameColdInit
  SEI
  LDA #$60
  STA CTRL
  LDA #$17
  STA INPTCTRL
  LDA #$00
  STA OFFSET
  CLD
  LDX #$FF
  TXS
  JSR SystemHardwareInit
StartGameInDemoMode
  JSR AttractModeMain
  LDA #$01
  STA DemoMode
  JSR InitializeNewGame
  LDA TimerHi
  CLC
  ADC #$06
  STA DemoTimer    ; run demo for ~24-30 seconds
  JMP MainGameLoop
StartNewGameFromMenu
  LDA #$00
  STA DemoMode
  JSR InitializeNewGame

MainGameLoop
  LDA TimerLo
  STA GamePacingTimer  ; Initialize the pacer with the current frame time

MainGameLoop_CheckState
  LDA GameActiveFlag      ; Check if the game is active or in a game-over transition
  BEQ GameIsActiveOrDemo  ; If 0, game is running normally (or in demo)

; Game is in the "ending" state (GameActiveFlag = 1)
  LDA GameEndDelayTimer
  BPL DecrementGameOverTimer
  LDA #$40                ; If timer underflowed, reset it
  STA GameEndDelayTimer
DecrementGameOverTimer
  JSR CheckResetSwitch    ; Allow player to reset during the game over sequence
  DEC GameEndDelayTimer   ; Countdown before transitioning to high score screen
  BNE GameIsActiveOrDemo  ; If timer still running, continue normal processing

                          ; Game over timer has expired, transition to High Score screen
  JSR MuteAudio
  LDA #$00
  STA CurrentPlayer        ; Set to Player 1 for first score entry
  JSR DoHscScoreEntry      ; Call High Score Cartridge for P1
  LDA PlayerCount
  BEQ GameOver_ScoresEntered
  LDA #$01                 ; If 2 players, set to Player 2
  STA CurrentPlayer
  JSR DoHscScoreEntry      ; Call High Score Cartridge for P2
GameOver_ScoresEntered
  LDA Difficulty
  STA Temp62               ; Preserve difficulty setting
  JSR DoHscAttractMode     ; Show high score table
  JMP StartGameInDemoMode  ; After high scores, return to demo mode

GameIsActiveOrDemo
  LDA DemoMode
  BEQ PacingLoop           ; If not in demo mode, skip to game pacing logic
  LDA TimerHi
  CMP DemoTimer            ; Check if the demo timer has expired
  BEQ StartGameInDemoMode  ; If so, restart the demo/attract sequence

PacingLoop
; This loop ensures the main game logic runs at a consistent rate (every 2 frames).
                             ; It prevents the game speed from fluctuating based on processing load.
  LDA TimerLo
  SEC
  SBC GamePacingTimer
  CMP #$02                   ; Has the frame timer advanced by at least 2 ticks?
  BCC PacingLoop             ; If not, wait.
  LDA TimerLo
  STA GamePacingTimer        ; Latch the new time for the next pace check

  JSR HandleConsoleSwitches  ; Check for Pause, Reset, Select
  JSR UpdateAllGameObjects   ; Run all game object logic (players, enemies, eggs, etc.)
  JSR ESRoutineScheduler     ; Run any scheduled events (lava rising, platform dissolving, etc.)
  JSR ManageMessageTimers    ; Update the HUD string buffer with current scores/lives

WaitForRender
; Wait here until the NMI has finished processing the previous frame's render list.
; This synchronizes the main logic thread with the VBlank/NMI thread.
  LDA RenderListDirty
  BNE WaitForRender

  JSR DisplayRoutineInit       ; Prepare the render list for the upcoming frame
  JMP MainGameLoop_CheckState  ; Loop back to the start of the main logic

RestartGameFromMenu
  LDX #$FF                  ; Reset the stack pointer
  TXS
  JMP StartNewGameFromMenu  ; Jump to start a new game

ResetStackAndDisplayGameOptions
  LDX #$FF
  TXS
  JMP DisplayGameOptions
ResetStackAndStartGameInDemoMode
  LDX #$FF
  TXS
  JMP StartGameInDemoMode
InitializeNewGame
  SEI
  LDA #$60
  STA CTRL
  JSR ClearRamMuteAudio
  JSR LoadGamePalette
  JSR SetColorRegisters
  JSR ResetMessageDisplayFlags
  JSR UpdateDLWithBackgroundSprites
WaitForVblankEnd
  BIT MSTAT
  BMI WaitForVblankEnd
WaitForVblankStart
  BIT MSTAT
  BPL WaitForVblankStart

  LDA #$57
  STA DPPL
  LDA #$27
  STA DPPH

  LDA #$40
  STA CTRL
  JSR InitPlayerLivesAndBonus
  JSR SpawnInitialPlayerBirds
  JSR InitializeSpawnPlatformStates
  LDA #$FF
  STA GameEndDelayTimer
  LDA #$10
  STA BridgeBurningWidth
  LDA #$0A
  STA ESRoutineIndex+9
  LDA #$FF
  STA OldPlatformPresentBitmask
  LDA #$01
  STA LevelBCD
  STA IsEggWave
  STA PterryCountdownHi
  JSR DisplayLevelStartMsg
  LDA DemoMode
  BNE DisplayGameStartMsg
  LDA #$01                        ; "prepare to joust"
  JSR DisplayGameMessage
  LDA #$12
  STA ESRoutineIndex+$0D
  LDA #SFX_GameStart
  JSR ScheduleSFX
  JSR ClearScores
DisplayGameStartMsg
  JSR UpdateScoreDisplay
  LDX #$01                        ; "prepare to joust"
  JSR UpdatePlayerLivesDisplayed
  DEX                             ; "Wave #"
  JMP UpdatePlayerLivesDisplayed
DisplayLevelStartMsg
  JSR UpdateAIParametersForLevel
  JSR DisplayGameStartMsg
  JSR SetAnyPterrysToLeaving
  LDA #$00                        ; "Wave #"
  JMP DisplayGameMessage
AwardEndOfWaveBonuses
  LDA GladiatorWaveFlag
  BEQ CheckForTeamWaveBonus
  LDA PlayerWasGladiator
  BNE DisplayNoGladiatorBonusMessage
  LDA #$0E                        ; "3000 gladiator"
  JSR DisplayGameMessage
  JMP FinalizeGladiatorBonus
DisplayNoGladiatorBonusMessage
  LDA #$12                        ; "no gladiator"
  JSR DisplayGameMessage
FinalizeGladiatorBonus
  LDA #$00
  STA PlayerWasGladiator
  STA GladiatorWaveFlag
  JMP DisplayPointsAwardedMessage
CheckForTeamWaveBonus
  LDA TeamWaveFlag
  BEQ CheckForSurvivalWaveBonus
  LDA PlayersWereATeam
  BEQ DisplayNoTeamBonusMessage
  LDA #$10
  STA ESRoutineIndex+$0C
  LDA #$03
  STA Temp61ScoreAdd
  LDA #$00
  STA Temp62ScoreAdd
  LDX #$00
  JSR UpdatePlayerScoreAndBonus
  LDX #$01
  JSR UpdatePlayerScoreAndBonus
  LDA #$0D                        ; "3000 team"
  JSR DisplayGameMessage
  JMP DisplayPointsAwardedMessage
DisplayNoTeamBonusMessage
  LDA #$11                        ; "no team"
  JSR DisplayGameMessage
  JMP DisplayPointsAwardedMessage
CheckForSurvivalWaveBonus
  LDA SurvivalWaveFlag
  BEQ ExitWaveBonusChecks
  LDA LevelWasSurvived
  BEQ DisplayNoSurvivalBonusMessage
  LDA #$10
  STA ESRoutineIndex+$0C
  LDA #$03
  STA Temp61ScoreAdd
  LDA #$00
  STA Temp62ScoreAdd
  LDX #$00
  JSR UpdatePlayerScoreAndBonus
  LDA #$0C                        ; "3000 survival"
  JSR DisplayGameMessage
  JMP DisplayPointsAwardedMessage
DisplayNoSurvivalBonusMessage
  LDA #$10                        ; "no survival"
  JSR DisplayGameMessage
DisplayPointsAwardedMessage
  LDA #$0F                        ; "points awarded"
  JMP DisplayGameMessage
ExitWaveBonusChecks
  RTS

SetupNextLevel
  LDA GameActiveFlag
  BNE ExitWaveBonusChecks
  TXA
  PHA
  TYA
  PHA
  JSR SetAnyPterrysToLeaving
  JSR AwardEndOfWaveBonuses
  INC Level
  LDA Level
  CMP #$28
  BMI SkipLevelRepeat
  LDA #$20      ; Levels greater than $28 repeat levels $20-$28
  STA Level
SkipLevelRepeat
  LDA LevelBCD  ; adjust the BCD level
  BEQ IncrementLevelBCD
  SED
  CLC
  ADC #$01
  CLD
  STA LevelBCD
IncrementLevelBCD
  LDA Level
  CMP #$01
  BNE SetupNextLevelCheckWave2
  LDA #$0C
  STA ESRoutineIndex+$0A
  JMP FinalizeNextLevelSetup
SetupNextLevelCheckWave2
  CMP #$02
  BNE FinalizeNextLevelSetup
  LDA #$0E
  STA ESRoutineIndex+$0B
FinalizeNextLevelSetup
  LDA ESRoutineIndex+$0C
  BNE SkipResettingWaveEndTimer
  LDA #$10
  STA ESRoutineIndex+$0C
SkipResettingWaveEndTimer
  PLA
  TAY
  PLA
  TAX
  RTS

UpdateAIParametersForLevel
  LDA #$AE
  SEC
  SBC Level
  STA TrollMinimumGrabYCoord
  SBC #$18
  STA TrollMinimumTargetYCoord

  LDA Level
  CLC
  ADC Difficulty
  ADC Difficulty
  ASL
  ADC #$0A                          ; A = (2 * LEVEL) + (4 * Difficulty) + 10
  STA TrollPullingVelocity

  ASL
  BCS AnimateTitleScreen
  ASL
AnimateTitleScreen
  EOR #$FF
  STA EnemyAITargetDelayBase
  LDA Difficulty
  BNE SetAIParamsForNormalOrHarder
  LDA #$03
  STA Temp5E
  LDA #$02
  STA Temp5F
  LDA #$07
  STA Temp60
  LDA #$08
  STA Temp61
  LDA #$02
  STA Temp62
  LDA #$01
  STA Temp63
  LDA #$08
  STA Temp64
  LDA #$09
  STA Temp65
  JMP StoreCalculatedAIPhysicsParameters
SetAIParamsForNormalOrHarder
  LDA #$02
  STA Temp5E
  LDA #$01
  STA Temp5F
  LDA #$08
  STA Temp60
  LDA #$09
  STA Temp61
  LDA #$01
  STA Temp62
  LDA #$00
  STA Temp63
  LDA #$09
  STA Temp64
  LDA #$0A
  STA Temp65
StoreCalculatedAIPhysicsParameters
  LDA Level
  CMP #$02
  BCS SetAIPhysicsForHigherLevels
  LDA Temp5E
  STA BirdPhysicsIndexMaxByType
  STA BirdPhysicsIndexMaxByType+1
  STA BirdPhysicsIndexMaxByType+2
  LDA Temp5F
  STA BirdPhysicsIndexMaxByType+3
  STA BirdPhysicsIndexMaxByType+4

  LDA Temp60
  STA BirdPhysicsIndexMinByType
  STA BirdPhysicsIndexMinByType+1
  STA BirdPhysicsIndexMinByType+2
  LDA Temp61
  STA BirdPhysicsIndexMinByType+3
  STA BirdPhysicsIndexMinByType+4
  JMP ResetWaveBonusFlags
SetAIPhysicsForHigherLevels
  LDA Temp62
  STA BirdPhysicsIndexMaxByType
  STA BirdPhysicsIndexMaxByType+1
  STA BirdPhysicsIndexMaxByType+2
  STA BirdPhysicsIndexMaxByType+3
  LDA Temp63
  STA BirdPhysicsIndexMaxByType+4
  LDA Temp64
  STA BirdPhysicsIndexMinByType
  STA BirdPhysicsIndexMinByType+1
  STA BirdPhysicsIndexMinByType+2
  STA BirdPhysicsIndexMinByType+3
  LDA Temp65
  STA BirdPhysicsIndexMinByType+4
ResetWaveBonusFlags
  LDA #$00
  STA LevelWasSurvived
  STA PlayersWereATeam
  STA PlayerWasGladiator
  STA SurvivalWaveFlag
  STA TeamWaveFlag
  STA GladiatorWaveFlag
  STA PterryWave
  LDA Level
  AND #$03
  BNE ProcessNonEggWaveSetup
  LDA IsEggWave
  BNE ProcessNonEggWaveSetup
  LDA #$01
  STA IsEggWave
  LDA #$03                         ; "Egg Wave"
  JSR DisplayGameMessage
  LDA #$0F
  STA PlatformsInLevel
  LDA Level                        ; Level (range from 0 to 40)
  LSR                              ; 
  LSR                              ; 
  TAX                              ; X=Level/4 (range from 0 to 10)
  LDA MaxEnemyBirdCountLUT,X
  STA MaxEnemyBirdCount
  LDA EggWaveEnemyTypeLUT,X
  STA EggWaveEnemyType
  DEC Level
  JMP FinalizeWaveSetupAndInitPlatforms
ProcessNonEggWaveSetup
  LDA #$00
  STA IsEggWave
  LDX #$01
  LDA Level
  CMP #$04
  BMI CheckForTeamWave
  AND #$03
  CMP #$02
  BNE CheckForTeamWave
  LDY Difficulty
  BEQ CheckForTeamWave
  STX PterryWave
  LDA #$05                     ; "Pterry Wave"
  JSR DisplayGameMessage
  JMP MessageDisplayDone
CheckForTeamWave
  LDY P0Lives
  BMI CheckForSurvivalWave
  LDY P1Lives
  BMI CheckForSurvivalWave
  CMP #$01
  BNE CheckForGladiatorWave
  STX PlayersWereATeam
  STX TeamWaveFlag
  LDA #$07                     ; "Team Wave"
  JSR DisplayGameMessage
  JMP MessageDisplayDone
CheckForGladiatorWave
  CMP #$03
  BNE MessageDisplayDone
  STX PlayerWasGladiator
  STX GladiatorWaveFlag
  LDA #$04                     ; "Gladiator Wave"
  JSR DisplayGameMessage
  JMP MessageDisplayDone
CheckForSurvivalWave
  CMP #$01
  BNE MessageDisplayDone
  STX LevelWasSurvived
  STX SurvivalWaveFlag
  LDA #$06                     ; "Survival Wave"
  JSR DisplayGameMessage

MessageDisplayDone
  LDA #$08
  STA MaxEnemyBirdCount
  LDX Level
  LDA PlatformsInLevelLUT,X
  STA PlatformsInLevel
  LDY #$00
  LDA Difficulty
  ASL
  ASL
  CLC
  ADC Level
  TAX
  STA Temp5E
  LDA EnemyWaveDataLUT_A,X
  JSR PopulateEnemyTypesForWave
  LDX Temp5E
  LDA EnemyWaveDataLUT_B,X
  JSR PopulateEnemyTypesForWave
FindNextValidEnemyTypeInWaveData
  DEY
  LDA EnemyBirdTypesForLevel,Y
  CMP #$03
  BEQ FindNextValidEnemyTypeInWaveData
  STY MaxActiveEnemyIndex
FinalizeWaveSetupAndInitPlatforms
  LDA #$14
  STA ESRoutineIndex+$11
  LDA #$00
  STA PlatformPresentBitmask
  LDA PlatformsInLevel
  CLC
  AND #$08
  BEQ SetupPlatformsProcessTopWrapBit
  SEC
SetupPlatformsProcessTopWrapBit
  ROL PlatformPresentBitmask
  SEC
  ROL PlatformPresentBitmask
  SEC
  ROL PlatformPresentBitmask
  SEC
  ROL PlatformPresentBitmask
  CLC
  LDA PlatformsInLevel
  AND #$02
  STA SpawnPlatformDissolved3
  BEQ SetupPlatformsProcessTopCenterPlatform
  SEC
SetupPlatformsProcessTopCenterPlatform
  ROL PlatformPresentBitmask
  CLC
  LDA PlatformsInLevel
  AND #$01
  BEQ SetupPlatformsProcessBottomLeftWrapBit
  SEC
SetupPlatformsProcessBottomLeftWrapBit
  ROL PlatformPresentBitmask
  CLC
  LDA PlatformsInLevel
  AND #$01
  BEQ SetupPlatformsProcessBottomRightWrapBit
  SEC
SetupPlatformsProcessBottomRightWrapBit
  ROL PlatformPresentBitmask
  LDA PlatformPresentBitmask
  AND #$40
  BEQ HandlePlatform0Removal
  LDA OldPlatformPresentBitmask
  AND #$40
  BNE ContinuePlatformSetupCheckPlatform4
  LDA #$C0
  STA $1D45
  LDA #$37
  STA Platform0XCoordinate
  LDA #$17
  STA Platform0PaletteWidth
  LDA #$17
  STA Platform0GfxLo
  JMP ContinuePlatformSetupCheckPlatform4
HandlePlatform0Removal
  LDA OldPlatformPresentBitmask
  AND #$40
  BEQ ContinuePlatformSetupCheckPlatform4
  LDA #SFX_PlatformDissolve
  JSR ScheduleSFX
  LDA #$3E
  STA ESRoutineIndex+$10
ContinuePlatformSetupCheckPlatform4
  LDA PlatformPresentBitmask
  AND #$04
  BEQ HandlePlatform4RemovalOrNoChange
  LDA OldPlatformPresentBitmask
  AND #$04
  BNE SetupPlatformsContinueAfterPlatform4
  LDA #$A8
  STA $1A6F
  LDA #$2E
  STA Platform4XCoordinate
  LDA #$14
  STA Platform4PaletteWidth
  LDA #$AA
  STA Platform4GfxLo
  JMP SetupPlatformsContinueAfterPlatform4
HandlePlatform4RemovalOrNoChange
  LDA OldPlatformPresentBitmask
  AND #$04
  BEQ SetupPlatformsContinueAfterPlatform4
  LDA #SFX_PlatformDissolve
  JSR ScheduleSFX
  LDA #$1C
  STA ESRoutineIndex+$0E
SetupPlatformsContinueAfterPlatform4
  LDA PlatformPresentBitmask
  AND #$02
  BEQ SetupPlatforms_HandleTopWrappedPlatformRemoval
  LDA OldPlatformPresentBitmask
  AND #$02
  BNE FinalizeWavePlatformSetup
  LDA #$A0
  STA $1A15
  LDA #$00
  STA Platform5XCoordinate
  LDA #$1B
  STA Platform56PaletteWidth
  LDA #$FB
  STA Platform56GfxLo
  JMP FinalizeWavePlatformSetup
SetupPlatforms_HandleTopWrappedPlatformRemoval
  LDA OldPlatformPresentBitmask
  AND #$02
  BEQ FinalizeWavePlatformSetup
  LDA #SFX_PlatformDissolve
  JSR ScheduleSFX
  LDA #$2D
  STA ESRoutineIndex+$0F
FinalizeWavePlatformSetup
  LDA PlatformPresentBitmask
  STA OldPlatformPresentBitmask
  RTS

PopulateEnemyTypesForWave
  STA Temp5F
  LDX #$03
UnpackEnemyTypeDataLoop
  LDA #$00
  ASL Temp5F
  ROL
  ASL Temp5F
  ROL
  STA EnemyBirdTypesForLevel,Y
  INY
  DEX
  BPL UnpackEnemyTypeDataLoop
  RTS

ClearRamMuteAudio
; zero ZP $60 to $FF
  LDA #$00
  LDX #$A0
ClearMoreZPRamLoop
  STA $5F,X
  DEX
  BNE ClearMoreZPRamLoop

; zero $140 to $1F7
  LDA #$00
  LDX #$B8
ClearRamMuteAudioLoop1
  STA $013F,X
  DEX
  BNE ClearRamMuteAudioLoop1

  LDA #$18
  STA Temp5F
  LDA #$00
  STA Temp5E  ; ($5E) = $1800
  LDA #$00    ; value to store in
  LDX #$08    ; 8 pages of ram.
  JSR MemSet  ; ie. zero ram from $1800 to $1FFF

; zero $2000 to $203F
  LDA #$00
  LDX #$40
ClearRamMuteAudioLoop2
  STA $1FFF,X
  DEX
  BNE ClearRamMuteAudioLoop2

; zero $2100 to $213F
  LDA #$00
  LDX #$40
ClearRamMuteAudioLoop3
  STA $20FF,X
  DEX
  BNE ClearRamMuteAudioLoop3

  LDA #$22
  STA Temp5F
  LDA #$00
  STA Temp5E  ; ($5E) = $2200
  LDA #$00    ; value to store in
  LDX #$05    ; 5 pages of ram.
  JSR MemSet  ; i.e. zero ram from $2200 to $26FF

  LDA #$FF
  STA P0CurrentFire
  JSR MuteAudio
  RTS

SystemHardwareInit
  LDA #$00
  LDX #$20  ; zero $40-$5F...
ClearSomeZPLoop
  STA $3F,X
  DEX
  BNE ClearSomeZPLoop

  LDA #$27
  STA Temp5F
  LDA #$00
  STA Temp5E  ; ($5E) = $2700
  LDA #$00    ; value to store in
  LDX #$01    ; 1 page of ram.
  JSR MemSet  ; ie. zero ram from $1800 to $18FF

  JSR ClearRamMuteAudio
  JSR InitializeFinalDisplayListPointers
  LDA #$00
  STA SWACNT
  STA SWBCNT
  STA $0284
  LDA #$C8
  STA CHARBASE
  LDA #$01
  STA Difficulty
  STA INPTCTRL
  RTS

ResetMessageDisplayFlags
  LDA #$FF
  LDX #$07
ResetMessageFlagsLoop
  STA $2038,X
  DEX
  BPL ResetMessageFlagsLoop
  RTS

InitializeSpawnPlatformStates
  LDA #$01
  STA SpawnPlatformDissolved0
  STA SpawnPlatformDissolved1
  STA SpawnPlatformDissolved2
  STA SpawnPlatformDissolved3
  RTS

LoadGamePalette
  LDX #$17
LoadGamePaletteLoop
  LDA GamePalette,X
  STA PaletteInRam,X
  DEX
  BPL LoadGamePaletteLoop
  RTS

LoadTitlePalette
  LDX #$17
LoadTitlePaletteLoop
  LDA TitlePalette,X
  STA PaletteInRam,X
  DEX
  BPL LoadTitlePaletteLoop
  RTS

ClearScores
  LDX #$02
  LDA #$00
ClearScoresLoop
  STA P0Score,X
  STA P1Score,X
  DEX
  BPL ClearScoresLoop
  RTS

MemSet
  LDY #$00
MemSetLoop
  STA (Temp5E),Y
  DEY
  BNE MemSetLoop
  INC Temp5F
  DEX
  BNE MemSetLoop
  RTS

InitializeWave
  LDA #$00
  STA P0EggPointLevel
  STA P1EggPointLevel
  JSR UpdatePterryRespawnTimer
  LDX #$03
ClearPlayerWaveStartVariablesLoop
  STA UnusedPlayerStateArray,X
  STA TempSpawnUnsafeCounter,X
  DEX
  BPL ClearPlayerWaveStartVariablesLoop
  LDA #$01
  LDX #$02
ResetPterryWaveAggressionFlagsLoop
  STA PterryAgressionFlag,X
  DEX
  BPL ResetPterryWaveAggressionFlagsLoop
  LDA IsEggWave
  BEQ SetupStandardEnemyWave
  RTS

SetupStandardEnemyWave
  LDX MaxActiveEnemyIndex
  STX EnemyBirdCount
  INC EnemyBirdCount
SpawnNextEnemyBirdLoop
  LDA EnemyBirdTypesForLevel,X
  INX
  INX
  STA ObjectBirdLevelAndType,X
  JSR SpawnNewBird
  DEX
  DEX
  DEX
  BPL SpawnNextEnemyBirdLoop
  LDA PterryWave
  BEQ FinalizeStandardWaveSetup
  LDA #$05
  STA PterryCountdownLo
  LDA #$00
  STA PterryCountdownHi
  LDA #$02
  STA PterryAgressionFlag
  LDA Level
  CMP #$0F
  BCC FinalizeStandardWaveSetup
  LDA #$02
  STA PterryAgressionFlag+1
FinalizeStandardWaveSetup
  RTS

SpawnInitialPlayerBirds
  LDX PlayerCount
SpawnNextPlayerBirdLoop
  LDA DemoMode
  BNE SetInitialPlayerBirdTypeForDemo
  LDA #$03
  JMP StoreInitialPlayerBirdType
SetInitialPlayerBirdTypeForDemo
  LDA #$01
StoreInitialPlayerBirdType
  STA ObjectBirdLevelAndType,X
  JSR SpawnNewBird
  LDA DemoMode
  BEQ SetPlayerSpawnPositionAndState
  LDA #$FF
  STA GameObjectAITimerHi,X
SetPlayerSpawnPositionAndState
  LDA PlayerStartXLUT,X
  STA GameObjectXCoordHi,X
  LDA #$A0                  ; Players start on the
  STA GameObjectYCoordHi,X  ; the bottom platform.
  LDA #$04                  ; Set alive state
  STA GameObjectState,X
  LDA #$00
  STA P0FireDebounce,X
  DEX
  BPL SpawnNextPlayerBirdLoop
  RTS

PlayerStartXLUT
 .byte $46, $78
UpdatePterryRespawnTimer
  LDA DemoMode
  BEQ SetPterryRespawnTimerNormalPlay
  LDA #$B0
  STA PterryCountdownLo
  LDA #$01
  STA PterryCountdownHi
  RTS

SetPterryRespawnTimerNormalPlay
  JSR CalculateLevelDifficultyRandomTimer
  ROL Temp5E
  ROL Temp5F
  LDA Temp5E
  ADC #$80
  STA PterryCountdownLo
  LDA Temp5F
  ADC #$00
  STA PterryCountdownHi
  RTS

UpdateAllGameObjects
  LDA P0SkidDirection
  ORA P1SkidDirection
  BNE ProcessGameObjectsMain
  LDA #$0F                ; skid sound effect
  JSR SilenceSFXIfPlaying
ProcessGameObjectsMain
  JSR RunPterryCountdown

  LDX #$18  ; last egg object
EggProcessingLoop
  JSR RunEggObjectProcessing
  DEX
  CPX #$0D  ; first egg object
  BCS EggProcessingLoop

  ; X is holding $0C, the last ptery object index

PterryProcessingLoop
  JSR RunPterryProcessing
  DEX
  CPX #$0A  ; first ptero object
  BCS PterryProcessingLoop

  LDX #$09
AllBirdsProcessingLoop        ; (player and enemy birds)
  JSR RunAllBirdsProcessing
  DEX
  BPL AllBirdsProcessingLoop
  JSR TrollAiLogic
  RTS

SpawnNewBird
  LDA InitialBirdDirection,X
  STA GameObjectDirection,X
  LDA #$00
  STA GameObjectChargingTimer,X
  STA GameObjectFootstepAnimTimer,X
  STA GameObjectFootstepAnimIndex,X
  STA GameObjectYVelocityLo,X
  STA GameObjectYVelocityHi,X
  STA GameObjectYCoordLo,X
  STA GameObjectXCoordLo,X
  STA GameObjectXVelocity,X
  STA GameObjectInAir,X
  STA GameObjectPlatformSkipState,X
  STA GameObjectSkidDirection,X
  STA GameObjectXCoordHi,X
  STA GameObjectYCoordHi,X
  STA GameObjectAITargetDelayTimer,X
  LDA #$02
  STA GameObjectAnimationFrame,X
  LDA ObjectBirdLevelAndType,X           ; A = 0 to 2 (excluding pterry)
  ASL                                    ; A = 0 to 4
  ASL                                    ; A = 0 to 8
  CLC
  ADC Difficulty                         ; A = 0 to 11
  TAY
  LDA AIModeByBirdTypeAndDifficultyLUT,Y
  CMP Level
  BCS StoreCalculatedAIMode
  LDA #$00
StoreCalculatedAIMode
  STA GameObjectAIMode,X
  LDA #$01
  STA GameObjectAlive,X
  LDA #$03
  STA GameObjectTimerResetValue,X
  STA GameObjectEventTimer1,X
  LDA #$03                               ; state=born1
  STA GameObjectState,X
  LDA #$1E
  STA GameObjectCountdownLo,X
  LDA Level
  CMP #$40
  BCC ClampLevelTo64
  LDA #$40
ClampLevelTo64
  LSR
  CLC
  ADC Difficulty
  STA Temp66
  CMP #$04
  BCC ClampTurnTimerIndex
  LDA #$03
ClampTurnTimerIndex
  STA TempCollisionOverlap
  LDA ObjectBirdLevelAndType,X
  ASL
  ASL
  CLC
  ADC TempCollisionOverlap
  STA GameObjectTurnTimerIndex,X
  TAY
  JSR ReturnRandInA
  AND #$7F
  CLC
  ADC TurnCheckTimerValuesLoLUT,Y
  STA GameObjectTurnCheckTimerLo,X
  LDA TurnCheckTimerValuesHiLUT,Y
  ADC #$00
  STA GameObjectTurnCheckTimerHi,X
  LDA Temp66
  CMP #$08
  BCC StoreCalculatedAITimers
  LDA #$07
StoreCalculatedAITimers
  TAY
  LDA AITimerByBirdTypeAndDifficultyLoLUT,Y
  STA GameObjectAITimerLo,X
  LDA AITimerByBirdTypeAndDifficultyHiLUT,Y
  STA GameObjectAITimerHi,X
  RTS

RunAllBirdsProcessing
  LDA GameObjectState,X
  BNE BirdIsAlive
  RTS

BirdIsAlive
  CMP #$03                     ; ** 3 is the bird init first state "born1"
  BNE BirdIsPastInit
  DEC GameObjectCountdownLo,X  ; ** delay before next init state
  BPL ExitAllBirdsProcessing1
  LDA #$07                     ; ** 7 is the bird init second state "born2"
  STA GameObjectState,X
ExitAllBirdsProcessing1
  RTS

BirdIsPastInit
  LDA GameObjectInAir,X
  BNE NOPBranch          ; pointless
NOPBranch
  LDA GameObjectState,X
  CMP #$06               ; Is bird invincible?
  BNE BirdIsntInvincible
  JMP BirdBorn1Processing
BirdIsntInvincible
  CMP #$07               ; Is bird on second init stage?
  BNE BirdIsntInit2
  JMP BirdBorn2Processing
BirdIsntInit2
                         ; if we're here, the bird state must be regular(4) or riderless($14)
  CPX #$02               ; players are the first 2 objects
  BCC PlayerBirdLogic    ; If it's an Enemy Bird then fall-through
  JSR AutoPilotBirdLogic
  JMP ProcessCommonBirdPhysicsAndCollisions
PlayerBirdLogic
  JSR PlayerHitLogic
  LDA GameObjectAlive,X
  BEQ ProcessBirdPhysicsCommon
  JSR HandlePlayerVsPterry
ProcessCommonBirdPhysicsAndCollisions
  LDA GameObjectAlive,X
  BEQ ProcessBirdPhysicsCommon
  JSR ProcessBirdToBirdCollisionLoop
ProcessBirdPhysicsCommon
  JSR ProcessBirdPlatformInteraction
  JSR UpdateAirborneObjectPhysics
  LDA GameObjectInAir,X
  BEQ ProcessOnPlatformBirdLogic
  RTS

ProcessOnPlatformBirdLogic
  LDA GameObjectSkidDirection,X
  BEQ ProcessOnPlatformMovement
  JMP ProcessSkidDeceleration
ProcessOnPlatformMovement
  LDA GameObjectXVelocity,X
  BNE ProcessFootstepAnimation
  STA GameObjectFootstepAnimIndex,X
  RTS

ProcessFootstepAnimation
  LDA GameObjectFootstepAnimTimer,X
  BEQ AdvanceFootstepAnimation
  DEC GameObjectFootstepAnimTimer,X
  RTS

AdvanceFootstepAnimation
  LDA GameObjectXVelocity,X
  CLC
  BMI CalculateFootstepTimerFromAbsXVel
  EOR #$FF
  CLC
  ADC #$01
CalculateFootstepTimerFromAbsXVel
  ADC #$02
  BMI AdvanceFootstepAnimSlowestSpeed
  STA GameObjectFootstepAnimTimer,X
  INC GameObjectFootstepAnimIndex,X
  LDA GameObjectFootstepAnimIndex,X
  CMP #$04
  BCC FinishFootstepAnimationCycle
  LDA #SFX_PlayerFootstep
  JSR SilenceSFXIfPlaying
  LDA #SFX_PlayerFootstep
  JSR ScheduleSFX
  LDA #$00
  STA GameObjectFootstepAnimIndex,X
FinishFootstepAnimationCycle
  RTS

AdvanceFootstepAnimSlowestSpeed
  CMP #$FF
  BNE FootstepAnim_Slow_DoubleFrameIncrement
  LDA #$00
  STA GameObjectFootstepAnimTimer,X
  INC GameObjectFootstepAnimIndex,X
  LDA GameObjectFootstepAnimIndex,X
  CMP #$02
  BNE FootstepAnim_Slowest_CheckCycleEnd
  INC GameObjectFootstepAnimIndex,X
  LDA GameObjectFootstepAnimIndex,X
FootstepAnim_Slowest_CheckCycleEnd
  CMP #$04
  BCC FootstepAnim_Slowest_FinishFrame
  LDA #SFX_PlayerFootstep
  JSR SilenceSFXIfPlaying
  LDA #SFX_PlayerFootstep
  JSR ScheduleSFX
  LDA #$00
  STA GameObjectFootstepAnimIndex,X
FootstepAnim_Slowest_FinishFrame
  RTS

FootstepAnim_Slow_DoubleFrameIncrement
  LDA #$00
  STA GameObjectFootstepAnimTimer,X
  INC GameObjectFootstepAnimIndex,X
  INC GameObjectFootstepAnimIndex,X
  LDA GameObjectFootstepAnimIndex,X
  CMP #$04
  BCC FootstepAnim_Slow_FinishFrame
  LDA #SFX_PlayerFootstep
  JSR SilenceSFXIfPlaying
  LDA #SFX_PlayerFootstep
  JSR ScheduleSFX
  LDA #$00
  STA GameObjectFootstepAnimIndex,X
FootstepAnim_Slow_FinishFrame
  RTS

ProcessSkidDeceleration
  LDA GameObjectChargingTimer,X
  BEQ ApplySkidDeceleration
  DEC GameObjectChargingTimer,X
  RTS

ApplySkidDeceleration
  LDA GameObjectXVelocity,X
  BMI ApplyNegativeVelocitySkidDeceleration
  DEC GameObjectXVelocity,X
  JMP ContinueSkidDeceleration
ApplyNegativeVelocitySkidDeceleration
  INC GameObjectXVelocity,X
ContinueSkidDeceleration
  LDA GameObjectXVelocity,X
  BEQ FinalizeSkidBirdStopped
  LDA #$04
  STA GameObjectChargingTimer,X
  RTS

FinalizeSkidBirdStopped
  JSR ClearSkidStateAndSound
  RTS

BirdBorn1Processing
  DEC BirdInvulnerable,X
  BMI StartSpawnAnimationSequence
  RTS

StartSpawnAnimationSequence
  LDY GameObjectHunterIndex,X
  LDA BirdSpawnInvulnAnimStateSequence_TypeA,Y
  CMP #$00
  BNE DispatchSpawnAnimationState
  DEC GameObjectYCoordHi,X
  JMP AdvanceSpawnAnimationState
DispatchSpawnAnimationState
  CMP #$01
  BNE PlayerAirSteering
  CPX #$02
  BCS HandleEnemyBirdSpawnAnimationState       ; branch if non-player enemy bird
  LDA #SFX_Invulnerable
  JSR ScheduleSFX
  JMP AdvanceSpawnAnimationState
HandleEnemyBirdSpawnAnimationState
  LDA #$00
  STA EnemyIsSpawning
  JMP SpawnAnim_FinalizeAndSetStateNormal
ProcessSpawnAnimationOrPlayerSteering
  JSR SpawnAnim_FinalizeAndSetStateNormal
  JMP PlayerBirdLogic
PlayerAirSteering
  LDA P0CurrentFire,X
  BPL ProcessSpawnAnimationOrPlayerSteering    ; Only when the player bird flaps 
  LDA SWCHA
  CPX #$00
  BEQ SkipJoystickShift
  ASL
  ASL                                          ; Shift the lower j1 nibble into
  ASL                                          ; the higher j0 nibble postition
  ASL
SkipJoystickShift
  ASL
  BCC ProcessSpawnAnimationOrPlayerSteering    ; Right
  ASL
  BCC ProcessSpawnAnimationOrPlayerSteering    ; Left
  ASL
  BCC ProcessSpawnAnimationOrPlayerSteering    ; Down
  ASL
  BCC ProcessSpawnAnimationOrPlayerSteering    ; Up
  LDA BirdSpawnInvulnAnimStateSequence_TypeA,Y
  CMP #$02
  BNE CheckSpawnAnimState3
  JSR SetPlayerSpawnInvulnerablePalette
  LDA #$9C
  STA Temp60PaletteAndWidth
  LDA #$BA
  STA Temp61
  LDA #$7E
  STA Temp62
  LDA BirdPaletteAndWidthLUT,X
  STA Temp63
  JSR UpdatePlatformDLWithSpawnedBirdGfx
  JMP AdvanceSpawnAnimationState
CheckSpawnAnimState3
  CMP #$03
  BNE SpawnAnimCheckState4
  JSR SetPlayerPaletteInRam
  LDA #$9C
  STA Temp60PaletteAndWidth
  LDA #$BA
  STA Temp61
  LDA RiderPaletteAndWidthLUT,X
  STA Temp62
  LDA BirdPaletteAndWidthLUT,X
  STA Temp63
  JSR UpdatePlatformDLWithSpawnedBirdGfx
  JMP AdvanceSpawnAnimationState
SpawnAnimCheckState4
  CMP #$04
  BNE SpawnAnim_CheckState5
  JSR SetPlayerSpawnInvulnerablePalette
  LDA #$7C
  STA Temp60PaletteAndWidth
  LDA SpawnRiderPaletteAndWidthLUT,X
  STA Temp61
  LDA BirdPaletteAndWidthLUT,X
  STA Temp62
  STA Temp63
  JSR UpdatePlatformDLWithSpawnedBirdGfx
  JMP AdvanceSpawnAnimationState
SpawnAnim_CheckState5
  CMP #$05
  BNE SpawnAnim_CheckState6
  JSR SetPlayersSpawnPalette
  LDA #$9C
  STA Temp60PaletteAndWidth
  LDA #$BA
  STA Temp61
  LDA RiderPaletteAndWidthLUT,X
  STA Temp62
  LDA BirdPaletteAndWidthLUT,X
  STA Temp63
  JSR UpdatePlatformDLWithSpawnedBirdGfx
  JMP AdvanceSpawnAnimationState
SpawnAnim_CheckState6
  CMP #$06
  BNE SpawnAnim_FinalizeAndSetStateNormal
  JSR SetPlayerSpawnInvulnerablePalette
  LDA #$5C
  STA Temp60PaletteAndWidth
  LDA #$BA
  STA Temp61
  LDA BirdPaletteAndWidthLUT,X
  STA Temp62
  STA Temp63
  JSR UpdatePlatformDLWithSpawnedBirdGfx
  JMP AdvanceSpawnAnimationState
SpawnAnim_FinalizeAndSetStateNormal
  CPX #$02
  BCS FinalizeBirdSpawnAndSetState        ; branch if non-player enemy bird
  LDA #$0A                                ; player invulnerable sound effect
  JSR SilenceSFXIfPlaying
FinalizeBirdSpawnAndSetState
  LDA #$04
  STA GameObjectState,X
  LDA P0CurrentFire,X
  EOR #$FF
  STA P0FireDebounce,X
  LDA #$00
  STA GameObjectYVelocityLo,X
  STA GameObjectYVelocityHi,X
  JSR SetPlayersSpawnPalette
  LDA #$5C
  STA Temp60PaletteAndWidth
  LDA #$BA
  STA Temp61
  LDA RiderPaletteAndWidthLUT,X
  STA Temp62
  LDA BirdPaletteAndWidthLUT,X
  STA Temp63
  JMP UpdatePlatformDLWithSpawnedBirdGfx
AdvanceSpawnAnimationState
  LDA BirdSpawnInvulnAnimStateSequence_TypeB,Y
  STA BirdInvulnerable,X
  INC GameObjectHunterIndex,X
  RTS

BirdBorn2Processing
  CPX #$02
  BCC PlayerBorn2     ; branch if it's a player bird
  LDA EnemyIsSpawning
  BEQ CheckForPterryBeforeSpawning
DelaySpawnIfAnotherEnemySpawning
  RTS

PlayerBorn2
  LDA P0Lives,X
  BPL ProceedWithSpawnSafetyCheck
  RTS

CheckForPterryBeforeSpawning
  LDA PterryState1
  ORA PterryState2
  ORA PterryState3
  BEQ ProceedWithSpawnSafetyCheck
  RTS

ProceedWithSpawnSafetyCheck
  JSR SpawnPointSafetyCheck
  LDA Temp60
  BMI DelaySpawnIfAnotherEnemySpawning
  CPX #$02
  BCC SpawnPlayerBird                 ; branch if it's a player bird
  LDA #$01
  STA EnemyIsSpawning
SpawnPlayerBird
  LDA #$01
  STA GameObjectAlive,X
  LDA $60
  STA GameObjectSpawnPlatformIndex,X
  TAY
  LDA PlayerRespawnX,Y
  STA GameObjectXCoordHi,X
  LDA PlayerRespawnY,Y
  STA GameObjectYCoordHi,X
  LDA #$06
  STA GameObjectState,X
  LDA #$00
  STA GameObjectInAir,X
  STA GameObjectHunterIndex,X
  STA GameObjectXVelocity,X
  STA BirdInvulnerable,X
  LDA BirdBornSFXType,X     ; different sounds for birds being born,
  JSR ScheduleSFX           ; depending on it being a player or enemy.
  JSR SetPlayerPaletteInRam
  LDA SpawnPlatformPaletteAndWidthLUT,X
  STA Temp60PaletteAndWidth
  LDA SpawnRiderPaletteAndWidthLUT,X
  STA Temp61
  LDA SpawnBirdPaletteAndWidthLUT,X
  STA Temp62
  STA Temp63
UpdatePlatformDLWithSpawnedBirdGfx
  LDY GameObjectSpawnPlatformIndex,X
  LDA SpawnPlatformDLObjectLoLUT,Y
  STA Temp5EGfxDataLo
  LDA SpawnPlatformDLObjectHiLUT,Y
  STA Temp5FGfxDataHi
  LDY #$01
  LDA Temp60PaletteAndWidth
  STA (Temp5E),Y
  DEY
  LDA Temp61
  STA (Temp5E),Y
  LDA Temp62
  STA RiderObjectPaletteAndWidth,X
  LDA Temp63
  STA BirdObjectPaletteAndWidth,X
  RTS

SetPlayerPaletteInRam
  CPX #$00
  BNE SetPlayer1Palette
  LDA #$1E
  STA P5C1_Ram
  STA P5C2_Ram
  STA P5C3_Ram
  JMP UpdatePlayerLivesDisplayed
SetPlayer1Palette
  CPX #$01
  BNE SetPaletteHandleEnemyBird
  LDA #$BC
  STA P6C1_Ram
  STA P6C2_Ram
  STA P6C3_Ram
  JMP UpdatePlayerLivesDisplayed
SetPaletteHandleEnemyBird
  LDA #$0D
  STA P4C1_Ram
  STA P4C3_Ram
  RTS

SetPlayerSpawnInvulnerablePalette
  LDA #$0D
  CPX #$00
  BNE SetSpawnInvulnPalette_HandlePlayer1
  STA P5C1_Ram
  STA P5C2_Ram
  STA P5C3_Ram
  RTS

SetSpawnInvulnPalette_HandlePlayer1
  STA P6C1_Ram
  STA P6C2_Ram
  STA P6C3_Ram
  RTS

SetPlayersSpawnPalette
  CPX #$00
  BNE SetPlayer1SpawnPalette
SetPlayer0SpawnPalette
  LDA #$15
  STA P5C1_Ram
  LDA #$A9
  STA P5C2_Ram
  LDA #$85
  STA P5C3_Ram
  RTS

SetPlayer1SpawnPalette
  CPX #$01
  BNE SetEnemySpawnPalette
  LDA #$0D
  STA P6C1_Ram
  LDA #$1C
  STA P6C2_Ram
  LDA #$44
  STA P6C3_Ram
  RTS

SetEnemySpawnPalette
  LDA #$39
  STA P4C1_Ram
  LDA #$33
  STA P4C3_Ram
  RTS

PlayerHitLogic
  LDA P0Lives,X
  BPL ProcessActivePlayerLogic
  LDA GameObjectState,X
  CMP #$05
  BEQ ProcessActivePlayerLogic
  LDA #$00
  STA GameObjectState,X
  RTS

ProcessActivePlayerLogic
  LDA GameObjectAlive,X
  BNE ProcessActivePlayerCollisionsAndInput
  JMP AutoPilotBirdLogic
ProcessActivePlayerCollisionsAndInput
  JSR CheckPlayerEggCollision
  LDA DemoMode
  BEQ ProcessPlayerInput
  JMP AutoPilotBirdLogic
ProcessPlayerInput
  LDA #$00
  STA Temp5E
  LDA P0CurrentFire,X
  STA Temp5F
  CMP P0FireDebounce,X
  BEQ ProcessPlayerJoystickInput
  STA P0FireDebounce,X
  LDA Temp5F
  BMI PlayerNoFlapResetAnimation
  LDA #$01
  STA Temp5E
  LDY ObjectBirdLevelAndType,X
  JSR ApplyFlapPhysics
  LDA GameObjectSkidDirection,X
  BEQ ContinuePlayerInputAfterFlap
  JSR ClearSkidStateAndSound
ContinuePlayerInputAfterFlap
  JMP ProcessPlayerJoystickInput
PlayerNoFlapResetAnimation
  LDA #$00
  STA GameObjectAnimationFrame,X
ProcessPlayerJoystickInput
  LDA GameObjectInAir,X
  BNE ProcessAirbornePlayerInput
  LDA P0JoyDelay,X
  BEQ ResetOnPlatformJoyDelay
  DEC P0JoyDelay,X
  RTS

ResetOnPlatformJoyDelay
  LDA #$04
  STA P0JoyDelay,X
ProcessAirbornePlayerInput
  LDA SWCHA
  CPX #$00
  BEQ ProcessNormalizedJoystickInput
  ASL
  ASL
  ASL
  ASL
ProcessNormalizedJoystickInput
  ASL
  BCC HandlePlayerJoyRight_PlatformOrAir
  ASL
  BCC HandlePlayerJoyLeft_PlatformOrAir
  RTS

;------------------------------------------------------------------------------
; Player On-Platform Horizontal Movement & Airborne Steering Logic
;
; This section handles player joystick input (Left/Right). Its behavior differs
; based on whether the bird is on a platform or airborne.
;
; - Airborne: The joystick changes the bird's facing direction. If the fire
;   button is also pressed (flapping), it applies horizontal acceleration.
;
; - On-Platform: The joystick controls walking. If the player reverses direction
;   while moving above a certain speed, a "skid" is initiated. Pressing the
;   opposite direction again will cancel the skid.
;
; Input:
;   X = CurrentPlayer index (0 or 1)
;   Temp5E (from outer context): Holds 1 if fire button was pressed this frame
;                                (flapping), 0 otherwise.
;------------------------------------------------------------------------------
HandlePlayerJoyLeft_PlatformOrAir
    LDA GameObjectInAir,X
    BEQ PlayerOnPlatform_JoyLeft_CheckVelocity  ; If on platform, jump to on-platform logic.
                                                ; Bird is AIRBORNE with joystick LEFT
    LDA #Direction_Left                         ; Face left.
    STA GameObjectDirection,X
    LDY Temp5E                                  ; Temp5E holds 1 if flapping, 0 if not.
    BEQ PlatformMovementOrAirborne_RTS1         ; If not flapping, just steer and exit.
                                                ; Fall through to accelerate if flapping in air.

AccelerateBirdLeft_PlatformOrAir  ; Shared target for airborne left flap & on-platform left acceleration.
    DEC GameObjectXVelocity,X     ; Decrease X-velocity (move more left or less right).
PlatformMovementOrAirborne_RTS1
    RTS

PlayerOnPlatform_JoyLeft_CheckVelocity             ; On Platform, Joy Left: Check current X Velocity
    LDA GameObjectXVelocity,X
    BEQ BirdStopped_Platform_JoyLeft               ; If X-velocity is 0, bird is stopped.
    BPL BirdMovingRight_Platform_JoyLeft           ; If >0, bird moving/skidding right.
                                                   ; Bird is already moving/skidding left (X-velocity is negative).
    BNE BirdMovingOrSkiddingLeft_Platform_JoyLeft  ; Always taken if XVel < 0.

BirdStopped_Platform_JoyLeft
    LDA GameObjectDirection,X             ; Bird was stationary. Get current facing direction.
    BMI AccelerateBirdLeft_PlatformOrAir  ; If already facing left ($FF), try to accelerate left.
                                          ; Else, was facing right ($01).
    LDA #Direction_Left                   ; Change direction to Left ($FF).
    STA GameObjectDirection,X
PlatformMovementOrAirborne_RTS2           ; Also used as common RTS for some skid conditions.
    RTS

BirdMovingRight_Platform_JoyLeft                   ; Bird was moving/skidding right, joystick now left.
    CMP #SKID_VELOCITY_THRESHOLD_RIGHT             ; Compare current X-velocity with skid threshold.
    BCC BirdMovingOrSkiddingLeft_Platform_JoyLeft  ; If VelX < Threshold (moving slowly right), don't skid.
                                                   ; Attempt to move left (effectively braking then accelerating).
                                                   ; Bird is moving right quickly enough to skid if direction changes.
    LDA GameObjectSkidDirection,X                  ; Is bird already skidding?
    BNE PlatformMovementOrAirborne_RTS2            ; Yes, already skidding (likely right): let skid resolve. RTS.
                                                   ; Not skidding, but moving right quickly and joystick is left: Initiate SKID to the RIGHT.
    INC GameObjectYCoordHi,X                       ; Visual effect: bird hops up slightly when skidding starts.
    LDA #Direction_Right                           ; Set skid direction to RIGHT ($01) because we were moving right.
    STA GameObjectSkidDirection,X
    LDA #SKID_ANIMATION_FRAME                      ; Set animation to skid frame.
    STA GameObjectFootstepAnimIndex,X
    LDA #SFX_Skid                                  ; Schedule skid sound effect.
    JMP ScheduleSFX

BirdMovingOrSkiddingLeft_Platform_JoyLeft   ; Arrived here if: (XVel < 0) OR (XVel > 0 but < SKID_THRESHOLD_RIGHT)
    LDA GameObjectSkidDirection,X           ; Check if already skidding.
    BEQ AccelerateBirdLeft_PlatformOrAir    ; Not skidding: try to accelerate left.
                                            ; Already skidding.
    BPL PlatformMovementOrAirborne_RTS2     ; Skidding right ($01), joy left (anti-skid): RTS.
                                            ; Skidding left ($FF), joy left (reinforce skid).
    JSR ClearSkidStateAndSound              ; Clear the skid state (was skidding left, now actively moving left).
    JMP AccelerateBirdLeft_PlatformOrAir    ; And try to accelerate left.

HandlePlayerJoyRight_PlatformOrAir
    LDA GameObjectInAir,X
    BEQ PlayerOnPlatform_JoyRight_CheckVelocity  ; If on platform, jump to on-platform logic.
                                                 ; Bird is AIRBORNE with joystick RIGHT
    LDA #Direction_Right                         ; Face right.
    STA GameObjectDirection,X
    LDY Temp5E                                   ; Temp5E has flap status (1 if flapping).
    BEQ PlatformMovementOrAirborne_RTS1          ; If not flapping, just steer and exit.
                                                 ; Fall through to accelerate if flapping in air.

AccelerateBirdRight_PlatformOrAir   ; Shared target for airborne right flap & on-platform right acceleration.
    INC GameObjectXVelocity,X       ; Increase X-velocity (move more right or less left).
    RTS

PlayerOnPlatform_JoyRight_CheckVelocity              ; On Platform, Joy Right: Check current X Velocity
    LDA GameObjectXVelocity,X
    BMI BirdMovingLeft_Platform_JoyRight             ; If <0, bird moving/skidding left.
    BNE BirdMovingOrSkiddingRight_Platform_JoyRight  ; If >0 (and non-zero), bird moving/skidding right.
                                                     ; Bird is stopped (X-velocity is 0).
BirdStopped_Platform_JoyRight
    LDA GameObjectDirection,X                        ; Get current facing direction.
    BPL AccelerateBirdRight_PlatformOrAir            ; If already facing right ($01), try to accelerate right.
                                                     ; Else, was facing left ($FF).
    LDA #Direction_Right                             ; Change direction to Right ($01).
    STA GameObjectDirection,X
PlatformMovementOrAirborne_RTS4                      ; Also used as common RTS for some skid conditions.
    RTS

BirdMovingLeft_Platform_JoyRight                     ; Bird was moving/skidding left, joystick now right.
    CMP #SKID_VELOCITY_THRESHOLD_LEFT                ; Compare current X-velocity with skid threshold (-2).
    BCS BirdMovingOrSkiddingRight_Platform_JoyRight  ; If VelX >= Threshold (moving slowly left), don't skid.
                                                     ; Attempt to move right (effectively braking then accelerating).
                                                     ; Bird is moving left quickly enough to skid.
    LDA GameObjectSkidDirection,X                    ; Is bird already skidding?
    BNE PlatformMovementOrAirborne_RTS4              ; Yes, already skidding (likely left): let skid resolve. RTS.
                                                     ; Not skidding, but moving left quickly and joystick is right: Initiate SKID to the LEFT.
    INC GameObjectYCoordHi,X                         ; Visual hop.
    LDA #Direction_Left                              ; Set skid direction to LEFT ($FF) because we were moving left.
    STA GameObjectSkidDirection,X
    LDA #SKID_ANIMATION_FRAME
    STA GameObjectFootstepAnimIndex,X
    LDA #SFX_Skid
    JMP ScheduleSFX

BirdMovingOrSkiddingRight_Platform_JoyRight   ; Arrived here if: (XVel > 0) OR (XVel < 0 but > SKID_THRESHOLD_LEFT)
    LDA GameObjectSkidDirection,X             ; Check if already skidding.
    BEQ AccelerateBirdRight_PlatformOrAir     ; Not skidding: try to accelerate right.
                                              ; Already skidding.
    BMI PlatformMovementOrAirborne_RTS4       ; Skidding left ($FF), joy right (anti-skid): RTS.
                                              ; Skidding right ($01), joy right (reinforce skid).
    JSR ClearSkidStateAndSound                ; Clear skid state, now actively moving right.
    JMP AccelerateBirdRight_PlatformOrAir     ; And try to accelerate right.

EnemyBirdIsSeekingRider
  LDY GameObjectPreyIndex,X
  CMP #$14
  BNE HunterBirdAICheckPrepareToSlideState
  LDA GameObjectDirection,X
  CMP GameObjectXVelocity,X
  BEQ HunterBirdAIContinueAfterVelocityUpdate
  ASL
  STA GameObjectXVelocity,X
HunterBirdAIContinueAfterVelocityUpdate
  JSR DetermineAIActionBasedOnProximity
  LDY GameObjectPreyIndex,X
  LDA GameObjectYVelocityHi,X
  BEQ UpdateHunterBirdYPosition
  BMI HunterBirdMoveUpward
  LDA #$01
  STA GameObjectYVelocityHi,X
  JMP UpdateHunterBirdYPosition
HunterBirdMoveUpward
  LDA #$FF
  STA GameObjectYVelocityHi,X
  LDA GameObjectYVelocityLo,X
  BMI UpdateHunterBirdYPosition
  LDA #$80
  STA GameObjectYVelocityLo,X
UpdateHunterBirdYPosition
  LDA GameObjectYCoordHi,X
  CLC
  ADC #$06
  CMP GameObjectYCoordHi,Y
  BCC HunterBirdDescendTowardRider
  DEC GameObjectYCoordHi,X
HunterBirdDescendTowardRider
  LDY GameObjectPreyIndex,X
  LDA GameObjectXCoordHi,Y
  SEC
  SBC GameObjectXCoordHi,X
  BPL HunterBirdCheckXProximityToRider
  EOR #$FF
  CLC
  ADC #$01
HunterBirdCheckXProximityToRider
  CMP #$03
  BCS HunterAIContinueSeekingRider
  LDA #$15
  STA GameObjectState,X
  LDA #$0A
  STA GameObjectEventTimer1,X
HunterAIContinueSeekingRider
  RTS

HunterBirdAICheckPrepareToSlideState
  CMP #$15
  BNE HunterBirdHandleSlidingState
  LDA GameObjectYCoordHi,Y
  SEC
  SBC #$06
  CMP GameObjectYCoordHi,X
  BCS HunterBirdInitiateSlide
HunterBirdRepositionAfterSlideFail
  LDA GameObjectDirection,X
  EOR #$FF
  CLC
  ADC #$01
  STA GameObjectDirection,X
  LDA #$00
  STA GameObjectTimerResetValue,X
  STA GameObjectEventTimer1,X
  LDA #$14
  STA GameObjectState,X
  RTS

HunterBirdInitiateSlide
  LDA #$0A
  STA GameObjectEventTimer1,X
  LDA GameObjectInAir,X
  BNE SkipHunterBirdSlideInitiation
  LDA #$16
  STA GameObjectState,X
  LDA GameObjectDirection,X
  STA GameObjectSkidDirection,X
  INC GameObjectYCoordHi,X
  LDA #$04                      ; riderless bird time to slide toward rider
  STA GameObjectChargingTimer,X
SkipHunterBirdSlideInitiation
  RTS

HunterBirdHandleSlidingState
  CMP #$16
  BNE HunterBirdFinalApproach
  LDA GameObjectYCoordHi,Y
  SEC
  SBC #$06
  CMP GameObjectYCoordHi,X
  BCS HunterSlideVerticalAlignOK
  JMP HunterBirdRepositionAfterSlideFail
HunterSlideVerticalAlignOK
  LDA #$0A
  STA GameObjectEventTimer1,X
  LDA GameObjectXCoordHi,Y
  CMP GameObjectXCoordHi,X
  BNE HunterSlideContinueApproach
  JMP HunterBirdReachedRider
HunterSlideContinueApproach
  LDA GameObjectSkidDirection,X
  BNE HunterSlideMaintainSkidDirection
  LDA GameObjectDirection,X
  EOR #$FF
  CLC
  ADC #$01
  STA GameObjectDirection,X
  STA GameObjectXVelocity,X
  LDA #$17
  STA GameObjectState,X
HunterSlideMaintainSkidDirection
  RTS

HunterBirdFinalApproach
  LDA GameObjectYCoordHi,Y
  SEC
  SBC #$06
  CMP GameObjectYCoordHi,X
  BCS HunterBirdTargetStillValidContinueApproach
  JMP HunterBirdRepositionAfterSlideFail
HunterBirdTargetStillValidContinueApproach
  LDA #$0A
  STA GameObjectEventTimer1,X
  LDA GameObjectDirection,X
  STA GameObjectXVelocity,X
  LDA GameObjectXCoordHi,Y
  CMP GameObjectXCoordHi,X
  BNE HunterBirdFinishRiderPickup
HunterBirdReachedRider
  LDA #$FF
  STA GameObjectPreyIndex,X
  LDA #$01
  STA GameObjectAlive,X
  LDA #$04
  STA GameObjectState,X
  LDA #$0A
  STA GameObjectEventTimer1,X
  LDA #$04
  STA GameObjectTimerResetValue,X
  LDA #$00
  STA GameObjectXVelocity,X
  STA GameObjectAITimerLo,X
  STA GameObjectAITimerHi,X
  STA.w GameObjectState,Y
  DEC EggsPresentCount
HunterBirdFinishRiderPickup
  RTS

;------------------------------------------------------------------------------
; AutoPilotBirdLogic
; Handles AI for non-player-controlled birds (enemy birds, demo mode player birds).
; This is the main entry point for their AI processing each game cycle relevant
; to this logic.
; Input: X = GameObject index (bird)
;------------------------------------------------------------------------------

AutoPilotBirdLogic
  LDA GameObjectState,X
  CMP #State_BirdSeekingRider          ; Is bird in State_BirdSeekingRider ($14)?
                                       ; This is a special state where an unridden enemy bird
                                       ; is actively seeking a hatched rider.
  BCC EnemyBirdHasRider_Or_IsNormalAI  ; If state < $14, proceed to general AI or rider-attached logic.
  JSR EnemyBirdIsSeekingRider          ; If state is $14 (or potentially higher, though unlikely),
                                       ; jump to its specialized AI handler.
EnemyBirdHasRider_Or_IsNormalAI
                                       ; This label implies the bird either has a rider (and thus uses general AI)
                                       ; or is an AI bird not in the special "seeking rider" state.

                                       ; Check if the bird is on a platform and has stopped skidding or moving.
  LDA GameObjectInAir,X                ; Is the bird in the air?
  BNE BirdAI_FlightPathAltitudeCheck   ; - Yes, skip platform-specific idle movement.

                                       ; Bird is ON a PLATFORM.
  LDA GameObjectXVelocity,X            ; Get current X velocity.
  BEQ BirdAI_OnPlatform_Stopped        ; - If X velocity is zero, bird is stopped.
  LDA GameObjectSkidDirection,X        ; Check if bird is skidding.
  BEQ BirdAI_FlightPathAltitudeCheck   ; - If not skidding (but moving), proceed to altitude checks.
                                       ; This implies moving non-skidding birds on platforms still use some airborne AI logic.
  RTS                                  ; - Bird is skidding on a platform, AI takes no further action this frame.

BirdAI_OnPlatform_Stopped           ; Bird is on a platform and X-velocity is zero.
                                    ; Make it pace back and forth slightly if it's an AI bird.
  LDA GameObjectXVelocity,X         ; Re-load X-velocity (will be $00 here).
  CLC
  ADC GameObjectDirection,X         ; Add current facing direction to X-velocity.
                                    ; (e.g., if facing right ($01), XVel becomes $01; if left ($FF), XVel becomes $FF)
  STA GameObjectXVelocity,X         ; Store new X-velocity to start slight movement.

BirdAI_FlightPathAltitudeCheck
  LDA GameObjectYCoordHi,X          ; Get current Y position.
  CMP #$A2                          ; Is bird very low (near bottom of typical play area)?
  BCC BirdAI_CheckMidAltitudeRange  ; - If higher than $A2, check next range.
                                    ; Bird is very low (Y >= $A2).
  LDA #$00                          ; Set a fast timer value for GameObjectTimerResetValue.
  STA GameObjectEventTimer1,X       ; Force next event (flap) quickly to gain altitude.
  JMP BirdAI_CheckEventTimer        ; Proceed to main AI timer logic.

BirdAI_CheckMidAltitudeRange
  CMP #$92                               ; Is bird in the lower-mid section of screen?
  BCC BirdAI_CheckUpperAltitudeRange     ; - If higher than $92, check upper range.
                                         ; Bird is in lower-mid range ($92 <= Y < $A2).
  LDA GameObjectYVelocityHi,X            ; Check Y velocity.
  BMI BirdAI_CheckUpperAltitudeRange     ; - If moving up, it might be correcting, check upper range logic.
  CMP #$01                               ; Is bird moving down (YVel >= $01)?
  BCC BirdAI_CheckUpperAltitudeRange     ; - If not moving down (YVel == $00), proceed to upper range logic.
                                         ; This means if stationary or moving slowly down in this zone, it's fine.
                                         ; Bird is in lower-mid range AND moving down significantly (YVel >= $01).
  LDY ObjectBirdLevelAndType,X           ; Get bird type.
  LDA AITimerDistantAggressive_ByType,Y  ; Load a type-specific timer reset value (from "slow reaction" table).
                                         ; This likely makes the bird react/flap sooner to correct downward movement.
  STA GameObjectTimerResetValue,X        ; Store it to influence next flap time.
  JMP BirdAI_CheckEventTimer

BirdAI_CheckUpperAltitudeRange
  CMP #$88                               ; Is bird in the mid-upper section of screen?
  BCC BirdAI_CheckEventTimer             ; - If higher than $88 (i.e. Y < $88, near top), go to standard event timer.
                                         ; Bird is in mid-upper range ($88 <= Y < $92).
  LDA GameObjectYVelocityHi,X            ; Check Y velocity.
  BMI BirdAI_CheckEventTimer             ; - If moving up, behavior is fine, go to standard event timer.
  CMP #$02                               ; Is bird moving down significantly (YVel >= $02)?
  BCC BirdAI_CheckEventTimer             ; - If not (YVel < $02), behavior is fine.
                                         ; Bird is in mid-upper range AND moving down significantly (YVel >= $02).
  LDY ObjectBirdLevelAndType,X           ; Get bird type.
  LDA AITimerDistantAggressive_ByType,Y  ; Load a type-specific timer reset value (from "slow reaction" table).
  STA GameObjectTimerResetValue,X        ; Store it to influence next flap time.
                                         ; Fall through to BirdAI_CheckEventTimer

; Bird Animation Frames:
; $00: Gliding / Standing on platform
; $01: Wings up (during flap)
; $02: Wings mid-stroke (specific flapping/landing animation phase)
; $03: Wings down (during flap)

BirdAI_CheckEventTimer
  LDA GameObjectEventTimer1,X          ; Load the bird's short-term event timer.
  BEQ BirdAI_EventTimerExpired         ; If timer is zero, process the event.
  DEC GameObjectEventTimer1,X          ; Otherwise, decrement the timer.
  JMP BirdAI_CheckMainAIDecisionTimer  ; Proceed to check the longer-term AI decision timer.

BirdAI_EventTimerExpired               ; Short-term event timer has expired.
  LDA GameObjectAnimationFrame,X       ; Get current animation frame.
                                       ; Frames $00 (glide/stand) and $02 (mid-flap/land)
                                       ; seem to be key decision points for initiating a flap.
  BEQ BirdAI_FlapOrTakeAction          ; If frame $00 (gliding/standing), time to flap/act.
  CMP #$02                             ; Is animation frame $02?
  BEQ BirdAI_FlapOrTakeAction          ; If frame $02, also time to flap/act.

BirdAI_SetAnimationToGlideOrStand      ; Current animation is likely $01 (wings up) or $03 (wings down).
                                       ; Reset animation to the default non-flapping state.
  LDA #$00                             ; Set animation frame to $00 (gliding/standing).
  STA GameObjectAnimationFrame,X
  LDA GameObjectTimerResetValue,X      ; Reload short-term event timer from its dynamic reset value.
                                       ; This value is often set by altitude adjustment logic.
  STA GameObjectEventTimer1,X
  JMP BirdAI_CheckMainAIDecisionTimer  ; Proceed to check the longer-term AI decision timer.

BirdAI_FlapOrTakeAction                ; Time to perform a primary action, usually flapping.
  LDA GameObjectTimerResetValue,X      ; Reload short-term event timer.
  STA GameObjectEventTimer1,X
  JSR BirdFlapOrAction                 ; Execute the flap: changes Y-velocity, updates animation frame.

BirdAI_CheckMainAIDecisionTimer   ; Check the 16-bit main AI decision timer.
  LDA GameObjectState,X           ; Get current bird state.
  CMP #State_BirdSeekingRider     ; Is bird in "State_BirdSeekingRider" ($14)?
  BCS BirdAI_ExitThisAutopilot    ; If state is >= $14 (SeekingRider or unknown higher),
                                  ; this general autopilot logic does not apply. Exit.
                                  ; State_BirdSeekingRider has its own specialized AI routines.

; Bird is in a state managed by this general autopilot (e.g., normal flight, attacking player).
; Decrement the 16-bit AI decision timer (GameObjectAITimerHi/Lo).

  SEC
  LDA GameObjectAITimerLo,X
  SBC #$01
  STA GameObjectAITimerLo,X
  LDA GameObjectAITimerHi,X
  BMI BirdAI_DecisionTimerExpired_HandleTargeting  ; If Hi byte was already negative, timer had expired.
  SBC #$00                                         ; Subtract carry (if Lo underflowed).
  STA GameObjectAITimerHi,X
  BMI BirdAI_DecisionTimerExpired_HandleTargeting  ; If Hi byte is now negative, timer just expired.

BirdAI_DecisionTimerStillActive                    ; Main AI decision timer has not expired yet.
                                                   ; Bird continues with its current general behavior.
  JSR AdjustBirdYBasedOnAltitude                   ; Make minor adjustments to Y velocity based on altitude zones.
                                                   ; The return value in A is used to set the next flap timer.
  STA GameObjectTimerResetValue,X                  ; Store the altitude-influenced value. This determines
                                                   ; how soon GameObjectEventTimer1 will expire for the next flap.
  RTS                                              ; Done with autopilot for this frame.

BirdAI_DecisionTimerExpired_HandleTargeting        ; Main AI decision timer has expired.
                                                   ; Time for a more significant AI decision.
  LDA GameObjectAlive,X                            ; Is the bird currently considered "alive"?
  BNE BirdAI_Alive_MakeDecision                    ; If alive, proceed to make a decision (re-target, turn, etc.).
                                                   ; Bird is NOT alive (GameObjectAlive,X is $00), but its AI is still being processed.
                                                   ; This can happen for unridden enemy birds that are flying off-screen after their
                                                   ; rider has been defeated (they are in state $05 "hit" but Alive is $00).
  JMP AdjustBirdYBasedOnAltitudeAndResetEventTimer ; Adjust Y for its off-screen path and set event timer.

BirdAI_Alive_MakeDecision                          ; Bird is alive, and its main AI timer has expired.
  JMP BirdAI_ReTargetOrYAdjust                     ; Jump to the core AI decision logic:
                                                   ; find a target, adjust Y-velocity towards it, or turn.

BirdAI_ExitThisAutopilot   ; Bird is in a state (e.g. SeekingRider) not handled by this general autopilot.
  RTS

BirdFlapOrAction
  LDA GameObjectState,X
  CMP #$08
  BEQ ExitFlapPhysics
  CLC
  LDA GameObjectXVelocity,X
  ADC GameObjectDirection,X
  STA GameObjectXVelocity,X
ApplyFlapPhysics
  LDA GameObjectState,X
  CMP #$08
  BEQ ExitFlapPhysics
  LDY ObjectBirdLevelAndType,X
  SEC
  LDA GameObjectYVelocityLo,X
  SBC BirdFlapLiftFactorLoLUT,Y
  STA GameObjectYVelocityLo,X
  LDA GameObjectYVelocityHi,X
  SBC BirdFlapLiftFactorHiLUT,Y
  STA GameObjectYVelocityHi,X
  LDA GameObjectInAir,X
  BNE FinalizeFlapState
  DEC GameObjectYCoordHi,X
FinalizeFlapState
  LDA #$01
  STA GameObjectAnimationFrame,X
  STA GameObjectInAir,X
  LDA #$00
  STA GameObjectPlatformSkipState,X
  CPX #$02
  BCS ExitFlapPhysics               ; branch if non-player enemy bird
  LDA #SFX_PlayerBirdFlap
  JMP ScheduleSFX
ExitFlapPhysics
  RTS

ClearSkidStateAndSound
  LDA GameObjectSkidDirection,X
  BEQ BirdNotSkidding2
  DEC GameObjectYCoordHi,X
  LDA #$00
  STA GameObjectSkidDirection,X
  STA GameObjectFootstepAnimIndex,X
  LDA #$0F                          ; skid sound effect
  JMP SilenceSFXIfPlaying
BirdNotSkidding2
  RTS

AdjustBirdYBasedOnAltitudeAndResetEventTimer
  JSR AdjustBirdYBasedOnAltitude
  STA GameObjectTimerResetValue,X
  RTS

; BirdAI_ReTargetOrYAdjust
; This routine is called when a bird's main AI decision timer has expired.
; It handles re-targeting, turn timers, and then determines the bird's next
; short-term event timer (GameObjectTimerResetValue,X) based on proximity to its
; target and its current AI mode. This timer controls flap/action frequency.
; Input: X = bird's GameObject index

BirdAI_ReTargetOrYAdjust
  LDA GameObjectPreyIndex,X                  ; Check if bird already has a prey
  BPL TargetAcquired_Or_AITargetDelayActive  ; If prey index is valid (0 or positive), proceed

  JSR FindBestTargetForGameObject       ; No valid prey, try to find one
  LDA GameObjectPreyIndex,X             ; Check again if a prey was found
  BPL PreyFound_ProceedToProximityCalc  ; If prey now valid, proceed

                                   ; Still no prey after trying to find one
  JSR AdjustBirdYBasedOnAltitude   ; Adjust Y velocity based on current altitude
  STA GameObjectTimerResetValue,X  ; Set next event timer based on altitude adjustment
  RTS                              ; No target, no further complex AI decision

TargetAcquired_Or_AITargetDelayActive
  LDA GameObjectAITargetDelayTimer,X      ; Check if AI target re-evaluation is on cooldown
  BEQ AITargetDelayExpired_ForceRetarget  ; If timer is zero, cooldown expired, force re-target
  DEC GameObjectAITargetDelayTimer,X      ; Otherwise, decrement cooldown timer
  JMP PreyFound_ProceedToProximityCalc    ; Proceed with current prey

AITargetDelayExpired_ForceRetarget
  LDA EnemyAITargetDelayBase            ; Cooldown expired, load base delay value
  STA GameObjectAITargetDelayTimer,X    ; Reset the cooldown timer
  JSR FindBestTargetForGameObject       ; Force re-evaluation of the best target

PreyFound_ProceedToProximityCalc        ; Bird has a prey (either existing or newly acquired)
  JSR DecrementAndProcessBirdTurnTimer  ; Handle turn timer (may cause bird to turn around)

DetermineAIActionBasedOnProximity
  JSR CalculateProximityCategoryIndexForAI  ; A -> index based on Y dist/vel to prey. Y reg gets this index.
  TAY                                       ; Y = Proximity Category Index (0-2, 4-6, 8-A)
  LDA BirdAIActionCategoryByProximityLUT,Y  ; Get AI Action Category ($00-$04, $FF)
  LDY ObjectBirdLevelAndType,X              ; Y = Bird Type (0:Bounder, 1:Hunter, 2:ShadowLord, 3:Player)

  CMP #AIActionCat_CloseDefensive          ; Is category $00 (Close/Defensive)?
  BEQ SelectTimer_Cat00_Path               ; If so, branch
  CMP #AIActionCat_OptimalAttack           ; Is category $01 (Optimal Attack)?
  BEQ SelectTimer_Cat01_Path               ; If so, branch
  CMP #AIActionCat_ModerateEngage          ; Is category $02 (Moderate Engage)?
  BEQ SelectTimer_Cat02_Path               ; If so, branch
  CMP #AIActionCat_FarApproach             ; Is category $03 (Far Approach)?
  BEQ SelectTimer_Cat03_Path               ; If so, branch

; Fall-through: Category is $04 (VeryFar) or $FF (NoTarget/Maintain current behavior)

  LDA GameObjectAIMode,X                 ; Check AI Mode (0=Passive, NonZero=Aggressive)
  BEQ Branch_CatFF04_AIMode0             ; If Mode 0 (Passive)
                                         ; AIMode is 1 (Aggressive) for Cat $04/$FF
  LDA AITimerDistantAggressive_ByType,Y  ; Mode 1
                                         ; Fall-through to StoreCalculatedTimerAndExitAIAction

StoreCalculatedTimerAndExitAIAction      ; Common exit point for storing timer and returning.
  STA GameObjectTimerResetValue,X
  RTS

Branch_CatFF04_AIMode0                                      ; Category $04/$FF, AI Mode 0 (Passive)
  LDA AITimerDistantPassive_Or_ApproachAggressive_ByType,Y  ; Use shared table
  JMP StoreCalculatedTimerAndExitAIAction                   ; Jump to common exit

SelectTimer_Cat03_Path                                      ; Category $03 (Far Approach)
  LDA GameObjectAIMode,X
  BEQ Branch_Cat03_AIMode0                                  ; If Mode 0 (Passive)
                                                            ; AI Mode is 1 (Aggressive) for Cat $03
  LDA AITimerDistantPassive_Or_ApproachAggressive_ByType,Y  ; Mode 1: Use shared table
  JMP StoreCalculatedTimerAndExitAIAction                   ; Jump to common exit

Branch_Cat03_AIMode0                                        ; Category $03 (Far Approach), AI Mode 0 (Passive)
  LDA AITimerApproachPassive_ByType,Y                       ; Use Cat03_AIMode0 table
  JMP StoreCalculatedTimerAndExitAIAction                   ; Jump to common exit

SelectTimer_Cat00_Path                                      ; Category $00 (Close/Defensive)
  LDA AITimerCloseDefensive_ByType,Y                        ; This category uses one table regardless of AI mode
  JMP StoreCalculatedTimerAndExitAIAction                   ; Jump to common exit

SelectTimer_Cat02_Path                                      ; Category $02 (Moderate Engage)
  LDA GameObjectAIMode,X
  BEQ Branch_Cat02_AIMode0                                  ; If Mode 0 (Passive)
                                                            ; AI Mode is 1 (Aggressive) for Cat $02
  LDA AITimerMixedEngageAttack_ByType,Y                     ; Mode 1: Use shared table
  JMP StoreCalculatedTimerAndExitAIAction                   ; Jump to common exit

Branch_Cat02_AIMode0                       ; Category $02 (Moderate Engage), AI Mode 0 (Passive)
  LDA AITimerEngagePassive_ByType,Y        ; Use Cat02_AIMode0 table
  JMP StoreCalculatedTimerAndExitAIAction  ; Jump to common exit

SelectTimer_Cat01_Path                     ; Category $01 (Optimal Attack)
  LDA GameObjectAIMode,X
  BEQ Branch_Cat01_AIMode0                 ; If Mode 0 (Passive)
                                           ; AI Mode is 1 (Aggressive) for Cat $01
  LDA AITimerAttackAggressive_ByType,Y     ; Mode 1: Use Cat01_AIMode1 table
  JMP StoreCalculatedTimerAndExitAIAction  ; Jump to common exit

Branch_Cat01_AIMode0                       ; Category $01 (Optimal Attack), AI Mode 0 (Passive)
  LDA AITimerMixedEngageAttack_ByType,Y    ; Mode 0: Use shared table
  JMP StoreCalculatedTimerAndExitAIAction  ; Jump to common exit

AdjustBirdYBasedOnAltitude
  LDA GameObjectYCoordHi,X
  SEC
  SBC #$94
  BPL AltitudeCheckGotAbsYDistFromLowBoundary
  EOR #$FF
  CLC
  ADC #$01
AltitudeCheckGotAbsYDistFromLowBoundary
  CMP #$02
  BCS BirdAI_CheckDistanceFromMidAltitudeBoundary
SetFlapTimerForLowAltitude
  LDY ObjectBirdLevelAndType,X
  LDA AITimerCloseDefensive_ByType,Y
  RTS

BirdAI_CheckDistanceFromMidAltitudeBoundary
  STA Temp65
  LDA GameObjectYCoordHi,X
  SEC
  SBC #$52
  BPL CheckDistanceFromMidAltitude
  EOR #$FF
  CLC
  ADC #$01
CheckDistanceFromMidAltitude
  CMP #$02
  BCC SetFlapTimerForLowAltitude
  STA Temp66
  LDA GameObjectYCoordHi,X
  SEC
  SBC #$15
  BPL CompareAltitudeDistancesAndSelectTimer
  EOR #$FF
  CLC
  ADC #$01
CompareAltitudeDistancesAndSelectTimer
  CMP #$02
  BCC SetFlapTimerForLowAltitude
  CMP Temp66
  BCS BirdAI_CheckMidAltitudePosition
  CMP Temp65
  BCS BirdAI_CompareAltitudeDistances
  LDA GameObjectYCoordHi,X
  CMP #$15
  BCS SetFlapTimerForHighAltitude
SetFlapTimerForMidAltitudePassive
  LDY ObjectBirdLevelAndType,X
  LDA AITimerEngagePassive_ByType,Y
  RTS

BirdAI_CheckMidAltitudePosition
  LDA Temp66
  CMP Temp65
  BCS BirdAI_CompareAltitudeDistances
  LDA GameObjectYCoordHi,X
  CMP #$52
  BCS SetFlapTimerForHighAltitude
  JMP SetFlapTimerForMidAltitudePassive
BirdAI_CompareAltitudeDistances
  LDA GameObjectYCoordHi,X
  CMP #$94
  BCC SetFlapTimerForMidAltitudePassive
SetFlapTimerForHighAltitude
  LDY ObjectBirdLevelAndType,X
  LDA AITimerApproachPassive_ByType,Y
  RTS

CalculateProximityMetric
  LDA #$00
  STA Temp60
  LDA GameObjectXCoordHi,X
  SEC
  SBC GameObjectXCoordHi,Y
  BPL CalculateProximityMetricXDone
  EOR #$FF
  CLC
  ADC #$01
CalculateProximityMetricXDone
  LSR
  LSR
  STA Temp5F
  LDA GameObjectYCoordHi,X
  SEC
  SBC GameObjectYCoordHi,Y
  BPL AddXDistanceToProximityMetric
  EOR #$FF
  CLC
  ADC #$01
AddXDistanceToProximityMetric
  CLC
  ADC Temp5F
  STA Temp5E
  RTS

FindBestTargetForGameObject
  CPX #$02
  BCS FindTargetForEnemyBird  ; branch if non-player enemy bird
  LDA #$02
  STA Temp62
  LDY #$18
  JMP FindTargetInitializeSearch
FindTargetForEnemyBird
  LDA #$00
  STA Temp62
  LDY #$01
FindTargetInitializeSearch
  LDA #$FF
  STA Temp61
  STA GameObjectPreyIndex,X
FindTargetCheckNextObjectLoop
  LDA.w GameObjectState,Y
  BEQ FindTargetCheckNextObject
  CMP #$04
  BEQ TargetStateIsValidCalculateDistance
  BCC TargetStateIsValidCalculateDistance
  CMP #$14
  BCS FindTargetCheckNextObject
  CMP #$0A
  BCC FindTargetCheckNextObject
TargetStateIsValidCalculateDistance
  JSR CalculateProximityMetric
  LDA Temp5E
  CMP Temp61
  BCS FindTargetCheckNextObject
  STA Temp61
  TYA
  STA GameObjectPreyIndex,X
FindTargetCheckNextObject
  DEY
  CPY Temp62
  BPL FindTargetCheckNextObjectLoop
  RTS

CalculateProximityCategoryIndexForAI
  LDY GameObjectPreyIndex,X
  LDA GameObjectYCoordHi,X
  CPY #$0D
  BCS CalculateProximityForEggOrPterry
  SEC
  SBC #$02
  CMP GameObjectYCoordHi,Y
  BCC SetProximityBaseIndexTargetIsBelow
  SEC
  SBC #$06
  CMP GameObjectYCoordHi,Y
  BCS SetProximityCategoryToOptimalAttack
  JMP SetProximityCategoryFarApproach
CalculateProximityForEggOrPterry
  SEC
  SBC #$F2
  CMP GameObjectYCoordHi,Y
  BCC SetProximityBaseIndexTargetIsBelow
  SEC
  SBC #$02
  CMP GameObjectYCoordHi,Y
  BCS SetProximityCategoryToOptimalAttack
SetProximityCategoryFarApproach
  LDA #$04
  JMP StoreProximityBaseIndexAndAddVelocityBias
SetProximityBaseIndexTargetIsBelow
  LDA #$08
  JMP StoreProximityBaseIndexAndAddVelocityBias
SetProximityCategoryToOptimalAttack
  LDA #$00
StoreProximityBaseIndexAndAddVelocityBias
  STA Temp5E
  LDA GameObjectYVelocityHi,X
  BEQ CalculateProximityIndex_BirdStationary
  BPL AddDownwardVelocityBiasToProximityIndex
  CMP #$FF
  BEQ CalculateProximityIndex_BirdStationary
AddDownwardVelocityBiasToProximityIndex
  LDA Temp5E
  CLC
  ADC #$02
  RTS

CalculateProximityIndex_BirdStationary
  LDA Temp5E
  CLC
  ADC #$01
  RTS

  LDA Temp5E
  RTS

ConvertKilledBirdToEgg
  LDA GameObjectSkidDirection,X
  BEQ PlayBirdDeathExplosionSFXTurnIntoEgg
  LDA #SFX_Skid
  JSR SilenceSFXIfPlaying
PlayBirdDeathExplosionSFXTurnIntoEgg
  LDA #SFX_BirdExplosion
  JSR ScheduleSFX
  LDA #$00
  STA GameObjectAlive,X
  LDA #$05
  STA GameObjectState,X
  LDY ObjectBirdLevelAndType,X
  LDA AITimerCloseDefensive_ByType,Y
  STA GameObjectTimerResetValue,X
  INC EggsPresentCount
  LDY #$0D
FindAvailableEggSlot
  LDA.w GameObjectState,Y
  BEQ FoundAvailableEggSlotInitialize
  INY
  CPY #$19
  BNE FindAvailableEggSlot
FoundAvailableEggSlotInitialize
  LDA #$01
  STA.w GameObjectState,Y
  LDA #$01
  STA GameObjectInAir,Y
  STA GameObjectDirection,Y
  LDA GameObjectXVelocity,X
  STA GameObjectXVelocity,Y
  LDA GameObjectYVelocityLo,X
  STA GameObjectYVelocityLo,Y
  LDA GameObjectYVelocityHi,X
  STA GameObjectYVelocityHi,Y
  LDA GameObjectYCoordLo,X
  STA GameObjectYCoordLo,Y
  LDA GameObjectYCoordHi,X
  CLC
  ADC #$0B
  STA GameObjectYCoordHi,Y
  LDA GameObjectXCoordLo,X
  STA GameObjectXCoordLo,Y
  LDA GameObjectXCoordHi,X
  STA GameObjectXCoordHi,Y
  LDA ObjectBirdLevelAndType,X
  STX Temp5E
  TAX
  LDA NextEnemyBirdLevel,X      ; check what the next upgraded enemy is
  LDX Temp5E
  STA ObjectBirdLevelAndType,Y
  LDA #$00
  STA ObjectWiggleAnimation,Y
  RTS

StartPlayerDeath
  LDA PterryCountdownHi
  BNE SkipPterryCountdown
  LDA #$01
  STA PterryCountdownHi
SkipPterryCountdown
  LDA #$00
  STA LevelWasSurvived
  LDA #$00
  STA Temp61ScoreAdd
  LDA #$05
  STA Temp62ScoreAdd
  JSR UpdatePlayerScoreAndBonus
  LDA GameObjectSkidDirection,X
  BEQ PlayPlayerDeathExplosionSFX
  LDA #SFX_Skid
  JSR SilenceSFXIfPlaying
PlayPlayerDeathExplosionSFX
  LDA #SFX_BirdExplosion
  JSR ScheduleSFX
  JSR DeadPlayerChecks

  LDA GameObjectState,X
  BEQ SetupPlayerRespawn  ; state = 0:dead

  LDA #$05  ; state = 5:player was hit 
  STA GameObjectState,X

  LDY ObjectBirdLevelAndType,X
  LDA AITimerCloseDefensive_ByType,Y
  STA GameObjectTimerResetValue,X
  LDA #$00
  STA GameObjectAlive,X
  LDA GameObjectYCoordHi,X
  JMP StorePlayerExplosionCoordinates
SetupPlayerRespawn
  LDA #$03                    ; state = 3:born1
  STA GameObjectState,X
  LDA #$1E
  STA GameObjectCountdownLo,X
  LDA GameObjectYCoordHi,X
  SEC
  SBC #$08                    ; Explosion should be a bit higher than the bird
StorePlayerExplosionCoordinates
  STA PlayerExplosionYCoord,X
  LDA GameObjectXCoordHi,X
  STA PlayerExplosionXCoord,X
  LDA #$16
  STA ESRoutineIndex+5,X
  LDA #$00                    ; Clear the death state
  STA P0DeathState,X
  TYA
  PHA
  LDY #$0A
SetPterrysToLeavingOnDeathLoop
  LDA.w GameObjectState,Y
  BEQ CheckNextPterryOnPlayerDeath
  CMP #$0D
  BEQ CheckNextPterryOnPlayerDeath
  LDA PterryDefeatedFlag,Y
  CMP #$01
  BNE CheckNextPterryOnPlayerDeath
  LDA #$0C
  STA.w GameObjectState,Y
  LDA GameObjectDirection,Y
  BPL SetPterryLeavingVelocityRight
  LDA #$FB
  JMP StorePterryLeavingVelocity
SetPterryLeavingVelocityRight
  LDA #$05
StorePterryLeavingVelocity
  STA GameObjectXVelocity,Y
CheckNextPterryOnPlayerDeath
  INY
  CPY #$0D
  BCC SetPterrysToLeavingOnDeathLoop
  PLA
  TAY
  RTS

DecrementAndProcessBirdTurnTimer
  SEC
  LDA GameObjectTurnCheckTimerLo,X
  SBC #$01
  STA GameObjectTurnCheckTimerLo,X
  LDA GameObjectTurnCheckTimerHi,X
  BMI BirdTurnTimerExpired
  SBC #$00
  STA GameObjectTurnCheckTimerHi,X
  BMI BirdTurnTimerExpired
  RTS

BirdTurnTimerExpired
  LDA GameObjectDirection,X
  EOR #$FF
  CLC
  JMP SetBirdRandomTurnTimers

; This rom is a bit weird in that the last 2 8k blocks are identical,
; including the cart vectors and encryption key.
;
; block 1: the graphics and code are live/used, except for the 
; brief "jmp $B000" located at DF7A. The encryption key, BIOS bytes,
; and 6502 vectors are dead/unused.
;
; block 2: the graphics and code are dead/unused, except for the
; brief "jmp $B000" located at FF7A. The encryption key, BIOS bytes,
; and 6502 vectors are used.
;
; So... Joust has just under 8K unused in block 2, ready and waiting 
; for rom hacks.
;
; This is block 1 of 2...


SpriteGFX2  ; C000-C7FF
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $2A, $A0, $2A, $80, $2A, $20, $AA, $00
 .byte $A8, $00, $AA, $00, $AA, $00, $AA, $80
 .byte $AA, $A0, $AA, $A0, $AA, $A0, $AA, $A0
 .byte $2A, $A0, $2A, $A0, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $11, $48, $22, $8C, $33, $C8, $AA, $AA
 .byte $EA, $54, $00, $10, $6A, $AA, $A8, $A0
 .byte $AA, $AA, $95, $56, $AB, $7A, $A9, $57
 .byte $00, $D0, $6A, $AA, $A5, $5A, $AA, $AA
 .byte $AA, $A0, $00, $00, $00, $15, $55, $56
 .byte $AA, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $03, $00, $33
 .byte $00, $C0, $33, $00, $C0, $0C, $C0, $CC
 .byte $30, $00, $00, $C0, $00, $00, $00, $00
 .byte $00, $0C, $00, $00, $00, $00, $0C, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $0A, $A0, $0A, $AA, $56
 .byte $9A, $90, $36, $A9, $6A, $AA, $80, $00
 .byte $55, $AA, $00, $09, $7A, $A6, $A5, $FE
 .byte $A5, $A9, $54, $D6, $A5, $AA, $BD, $66
 .byte $56, $AB, $70, $00, $00, $00, $A0, $00
 .byte $02, $A0, $00, $00, $A8, $02, $AA, $02
 .byte $A0, $00, $00, $AA, $00, $44, $F0, $81
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $FF, $EB, $CF, $FF, $00, $00, $00
 .byte $00, $00, $00, $0F, $CF, $00, $00, $00
 .byte $00, $FF, $A0, $00, $00, $00, $00, $00
 .byte $2A, $80, $2A, $80, $2A, $00, $AA, $00
 .byte $AA, $00, $AA, $00, $AA, $00, $AA, $80
 .byte $AA, $80, $AA, $A0, $AA, $A0, $AA, $80
 .byte $AA, $A0, $2A, $A0, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $04, $48, $08, $8C, $0C, $C8, $2A, $AA
 .byte $AA, $94, $00, $10, $AA, $AA, $A8, $A0
 .byte $AA, $AA, $95, $56, $AB, $FE, $A9, $57
 .byte $00, $D0, $6A, $AA, $A5, $56, $AA, $AA
 .byte $AA, $A0, $00, $00, $00, $00, $55, $55
 .byte $AA, $80, $00, $00, $2A, $AB, $F4, $00
 .byte $00, $00, $00, $AA, $80, $00, $00, $00
 .byte $00, $2A, $AA, $A8, $00, $5A, $AA, $A0
 .byte $00, $00, $00, $00, $00, $00, $00, $0A
 .byte $AA, $A0, $00, $00, $00, $03, $30, $0C
 .byte $00, $C0, $0C, $00, $C0, $0F, $00, $CC
 .byte $30, $00, $00, $30, $00, $00, $00, $00
 .byte $03, $FF, $FF, $FF, $FF, $FF, $F0, $0F
 .byte $FC, $03, $F3, $C3, $CF, $00, $00, $FC
 .byte $03, $00, $F0, $28, $28, $02, $A9, $5A
 .byte $9E, $90, $36, $A9, $5A, $AA, $40, $00
 .byte $01, $5A, $A0, $0A, $FA, $A6, $A5, $FA
 .byte $A5, $6A, $54, $D6, $A6, $AA, $D7, $A6
 .byte $A5, $AB, $FA, $80, $00, $0A, $AB, $C0
 .byte $0A, $A9, $00, $02, $AA, $86, $AA, $0A
 .byte $A8, $00, $7A, $AA, $B0, $0C, $F0, $63
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $FA, $BF, $FF, $EA, $BE, $F3, $FF, $C0
 .byte $00, $00, $00, $EA, $FB, $EA, $FF, $00
 .byte $03, $FA, $BF, $FA, $80, $00, $00, $00
 .byte $0A, $80, $0A, $80, $2A, $80, $2A, $80
 .byte $2A, $80, $AA, $80, $AA, $80, $AA, $80
 .byte $2A, $80, $AA, $80, $2A, $80, $AA, $80
 .byte $AA, $80, $AA, $80, $AA, $A8, $AF, $A8
 .byte $AE, $A8, $AA, $A8, $2A, $A8, $2A, $A8
 .byte $2A, $A8, $2A, $A8, $AA, $A8, $AA, $A8
 .byte $AA, $A8, $2A, $A8, $2A, $A8, $2A, $A8
 .byte $04, $48, $08, $8C, $0C, $C8, $0A, $AA
 .byte $AA, $94, $00, $10, $AA, $AA, $A8, $20
 .byte $AA, $AA, $55, $4A, $AA, $7E, $A9, $57
 .byte $00, $D0, $6A, $AA, $A5, $56, $AA, $AA
 .byte $AA, $80, $00, $00, $00, $00, $05, $55
 .byte $6A, $A0, $00, $00, $AA, $AA, $BF, $D0
 .byte $00, $00, $A0, $AA, $AB, $40, $00, $00
 .byte $00, $AA, $AA, $AA, $30, $5A, $AA, $A0
 .byte $01, $56, $A8, $00, $00, $00, $70, $AA
 .byte $AA, $AA, $B0, $00, $00, $03, $0C, $00
 .byte $00, $C0, $00, $03, $00, $0C, $C0, $CF
 .byte $F0, $00, $00, $0F, $FC, $00, $00, $00
 .byte $00, $0C, $00, $00, $00, $00, $00, $30
 .byte $03, $0C, $0C, $3C, $30, $C0, $F3, $03
 .byte $03, $03, $0C, $80, $08, $00, $55, $4A
 .byte $AE, $90, $06, $AA, $16, $A9, $00, $00
 .byte $00, $00, $00, $0A, $BA, $A5, $A9, $DA
 .byte $A5, $6A, $54, $D6, $A6, $AA, $FD, $A6
 .byte $AA, $AA, $AA, $00, $00, $2A, $AA, $7F
 .byte $2A, $AA, $F0, $02, $AA, $A6, $AA, $6A
 .byte $AA, $3F, $6A, $AA, $A0, $00, $12, $91
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $FF, $AA, $AA, $BA, $AB, $FA, $EE, $A0
 .byte $00, $00, $02, $AA, $AB, $FF, $FF, $00
 .byte $3F, $EB, $FF, $AB, $FA, $C0, $00, $00
 .byte $0A, $80, $0A, $80, $0A, $80, $0A, $80
 .byte $2A, $80, $2A, $80, $2A, $80, $2A, $80
 .byte $2A, $80, $2A, $80, $2A, $80, $2A, $80
 .byte $2A, $80, $AA, $80, $BB, $A8, $BA, $A8
 .byte $AE, $E8, $BA, $AB, $BE, $A8, $BA, $A0
 .byte $3B, $A8, $3A, $A8, $BE, $A8, $3A, $A8
 .byte $BA, $A8, $3B, $E8, $3A, $E8, $3A, $A8
 .byte $45, $48, $8A, $8C, $CF, $C8, $02, $AA
 .byte $AA, $94, $00, $10, $AA, $A8, $00, $20
 .byte $AA, $A9, $54, $0A, $AA, $BE, $A9, $57
 .byte $00, $D0, $6A, $AA, $A8, $55, $AA, $AA
 .byte $AA, $00, $00, $00, $00, $00, $00, $05
 .byte $52, $A8, $00, $0A, $AA, $AA, $AF, $FD
 .byte $40, $00, $A0, $AA, $AA, $F4, $00, $00
 .byte $02, $AA, $AA, $AA, $90, $5A, $AA, $A0
 .byte $15, $AA, $AA, $A0, $00, $07, $E0, $AA
 .byte $AA, $AA, $A8, $00, $00, $00, $C3, $00
 .byte $00, $C0, $00, $0C, $00, $0C, $30, $00
 .byte $30, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $C0
 .byte $03, $0C, $0C, $0C, $C0, $33, $0F, $03
 .byte $03, $03, $03, $00, $00, $00, $00, $0A
 .byte $A6, $90, $06, $AA, $00, $00, $00, $00
 .byte $00, $00, $00, $0A, $AA, $A5, $A9, $5A
 .byte $95, $6A, $50, $D6, $A6, $AA, $76, $A6
 .byte $AA, $AA, $A8, $00, $00, $AA, $AA, $5D
 .byte $EA, $AA, $FF, $0A, $AA, $AA, $A9, $AA
 .byte $AA, $BF, $AA, $AA, $A8, $00, $12, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $FE, $FF, $FF, $FF, $FF, $EA, $BA, $A8
 .byte $00, $00, $0E, $EE, $AF, $FF, $FF, $00
 .byte $AF, $EF, $EE, $BF, $FA, $AF, $C0, $00
 .byte $0A, $80, $0A, $80, $0A, $80, $0A, $80
 .byte $0A, $80, $0A, $80, $2A, $00, $2A, $80
 .byte $2A, $80, $2A, $80, $2A, $80, $2A, $00
 .byte $2A, $20, $2A, $80, $AE, $A8, $AE, $A0
 .byte $AA, $A0, $AB, $A0, $AF, $A0, $AF, $A0
 .byte $AF, $A0, $2E, $A8, $2F, $A8, $AB, $B8
 .byte $2B, $A8, $2F, $E8, $AE, $A8, $2B, $A8
 .byte $15, $48, $2A, $8C, $3F, $C8, $00, $2A
 .byte $AA, $A4, $00, $00, $AA, $00, $00, $00
 .byte $2A, $95, $00, $0A, $AA, $BF, $A9, $57
 .byte $00, $10, $6A, $AA, $A8, $05, $AA, $AA
 .byte $A8, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $2A, $AA, $AA, $A9, $5F
 .byte $FC, $02, $A0, $AA, $AA, $7F, $C0, $00
 .byte $02, $AA, $AA, $AA, $A0, $5A, $AA, $81
 .byte $5A, $AA, $AA, $AA, $0F, $FF, $60, $AA
 .byte $AA, $AA, $AA, $80, $00, $30, $03, $00
 .byte $3F, $FF, $FF, $F0, $00, $0C, $00, $00
 .byte $30, $00, $00, $0F, $C0, $FC, $03, $C0
 .byte $FC, $00, $15, $55, $55, $54, $00, $C0
 .byte $FF, $0C, $0C, $0F, $00, $0C, $03, $03
 .byte $03, $03, $03, $00, $00, $00, $00, $02
 .byte $AB, $94, $06, $AA, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $AA, $A9, $AA, $AA
 .byte $95, $6A, $50, $D6, $AA, $AA, $BA, $A6
 .byte $AA, $AA, $A0, $00, $02, $AA, $AA, $97
 .byte $AA, $AA, $9D, $FA, $AA, $AA, $A9, $AA
 .byte $AA, $B5, $AA, $AA, $AA, $0B, $B0, $38
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $FE, $FD, $55, $55, $57, $AA, $EA, $AB
 .byte $C0, $00, $FF, $EF, $AB, $BF, $FF, $03
 .byte $EB, $FA, $BF, $FF, $EB, $FF, $FF, $A8
 .byte $2A, $00, $8A, $00, $0A, $00, $02, $80
 .byte $02, $A0, $0A, $A0, $0A, $80, $0A, $80
 .byte $2A, $00, $2A, $00, $2A, $00, $2A, $20
 .byte $2A, $00, $2A, $00, $BB, $A0, $AB, $A0
 .byte $AB, $A0, $AE, $80, $AE, $80, $AE, $80
 .byte $AB, $A0, $AE, $A0, $AF, $EB, $AF, $A8
 .byte $2F, $E8, $AB, $A8, $AB, $E8, $AB, $E8
 .byte $05, $50, $0A, $A0, $0F, $F0, $00, $0A
 .byte $AA, $A8, $00, $00, $80, $00, $00, $00
 .byte $00, $00, $00, $2A, $AA, $AF, $AA, $57
 .byte $00, $10, $6A, $AA, $A8, $00, $02, $AA
 .byte $80, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $AA, $AA, $AA, $AA, $55
 .byte $FF, $CA, $A0, $AA, $AA, $97, $FD, $00
 .byte $0A, $AA, $AA, $AA, $A0, $5A, $AA, $85
 .byte $6A, $AA, $AA, $AA, $BF, $FD, $60, $AA
 .byte $AA, $AA, $AA, $A0, $00, $30, $03, $00
 .byte $00, $C0, $00, $00, $00, $0C, $00, $C0
 .byte $30, $00, $00, $30, $33, $03, $0C, $33
 .byte $03, $00, $AA, $AA, $AA, $AA, $00, $C0
 .byte $00, $03, $F0, $0C, $00, $0C, $00, $FC
 .byte $FC, $FC, $03, $00, $00, $00, $00, $00
 .byte $2A, $A4, $0A, $AA, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $0A, $A9, $6A, $AA
 .byte $95, $DA, $50, $36, $A9, $AA, $AA, $A6
 .byte $AA, $AA, $80, $00, $02, $AA, $AA, $9F
 .byte $AA, $AA, $B7, $EA, $AA, $AA, $AD, $AA
 .byte $AA, $A6, $AA, $AA, $AA, $3D, $F2, $FE
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $55, $75, $55, $55, $55, $D5, $AF, $FF
 .byte $FC, $03, $FF, $FF, $EA, $BF, $FF, $FF
 .byte $FA, $FF, $FF, $FF, $BF, $FF, $FF, $AF
 .byte $28, $00, $28, $00, $08, $00, $02, $80
 .byte $02, $A0, $02, $A0, $02, $A0, $0A, $A0
 .byte $28, $A0, $28, $20, $AA, $00, $28, $20
 .byte $28, $28, $A8, $80, $AA, $A0, $AE, $A0
 .byte $AE, $80, $AA, $80, $AA, $00, $AA, $00
 .byte $AA, $80, $AB, $A0, $AF, $A0, $AA, $E0
 .byte $AB, $A0, $2B, $A0, $AB, $A0, $AE, $A0
 .byte $01, $08, $02, $0C, $03, $08, $00, $00
 .byte $AA, $A8, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $0A, $AA, $A7, $AA, $57
 .byte $00, $10, $6A, $AA, $A8, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $AA, $AA, $AA, $AA, $57
 .byte $F7, $EA, $A0, $AA, $AA, $AD, $FF, $F0
 .byte $0A, $AA, $AA, $AA, $A0, $9A, $AA, $B5
 .byte $AA, $AA, $AA, $AA, $AF, $FD, $A0, $AA
 .byte $AA, $AA, $AA, $A8, $00, $0C, $0C, $00
 .byte $00, $00, $00, $00, $00, $CC, $00, $00
 .byte $33, $00, $00, $C0, $0C, $00, $F0, $0C
 .byte $00, $C0, $00, $00, $00, $00, $00, $C0
 .byte $00, $00, $00, $00, $00, $0C, $00, $00
 .byte $00, $00, $03, $00, $00, $00, $00, $00
 .byte $0A, $A4, $0A, $80, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $02, $A9, $6A, $AA
 .byte $96, $DA, $50, $36, $A9, $AA, $AA, $A1
 .byte $5A, $AA, $A0, $00, $0A, $AA, $AA, $95
 .byte $AA, $AA, $95, $EA, $AA, $AA, $AD, $AA
 .byte $AA, $AE, $AA, $AA, $AA, $2A, $A0, $89
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
 .byte $55, $D5, $55, $55, $55, $75, $55, $55
 .byte $55, $15, $55, $55, $55, $55, $55, $55
 .byte $55, $55, $55, $55, $55, $55, $55, $55
 .byte $88, $00, $08, $00, $08, $00, $00, $00
 .byte $00, $20, $00, $20, $00, $20, $00, $88
 .byte $02, $00, $28, $80, $A0, $00, $00, $00
 .byte $22, $08, $A0, $20, $AA, $A0, $AA, $A0
 .byte $AA, $80, $AE, $00, $AE, $00, $BA, $00
 .byte $AA, $00, $AA, $80, $AA, $A0, $AA, $A0
 .byte $AA, $80, $2A, $A0, $2A, $A0, $AA, $80
 .byte $05, $08, $0A, $0C, $0F, $08, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $02, $AA, $AB, $EA, $54
 .byte $00, $10, $6A, $AA, $A8, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $02, $AA, $AA, $AA, $AA, $95
 .byte $77, $EA, $A0, $AA, $AA, $A5, $DF, $FF
 .byte $2A, $AA, $AA, $AA, $A0, $AA, $AA, $95
 .byte $AA, $AA, $AA, $AA, $AF, $75, $A0, $AA
 .byte $AA, $AA, $AA, $AA, $00, $03, $F0, $00
 .byte $00, $00, $00, $00, $03, $00, $00, $00
 .byte $00, $C0, $03, $00, $33, $00, $C0, $33
 .byte $00, $C0, $0C, $30, $CF, $F0, $00, $C0
 .byte $00, $00, $00, $00, $00, $0C, $00, $00
 .byte $00, $00, $03, $00, $00, $00, $00, $00
 .byte $00, $A8, $08, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $2A, $2A, $AA
 .byte $56, $DA, $50, $36, $A9, $6A, $AA, $A0
 .byte $05, $56, $A8, $00, $2A, $AA, $AA, $A6
 .byte $AA, $6A, $AF, $EA, $AA, $AA, $AD, $AA
 .byte $AA, $AA, $AA, $A5, $6A, $2E, $A1, $D0

; C800

CharacterSet
; comprised of...
; 000000000000000011111111111111112222222222222222
; 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
; _0123456789@0123456789@ABCDEGHIJLMNPRSTUVWXYZK() **then the title border and logo chars

 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $AA, $AA, $6A, $5A, $96, $A5, $A9, $AA
 .byte $90, $A0, $A0, $A0, $A0, $A0, $60, $50
 .byte $AA, $A0, $A9, $5A, $55, $6A, $50, $00
 .byte $0A, $97, $7F, $EA, $AA, $AA, $A7, $5D
 .byte $7F, $EA, $95, $AA, $AF, $75, $AA, $AA
 .byte $AA, $02, $AA, $AA, $AA, $95, $AA, $AA
 .byte $55, $55, $AA, $95, $57, $0F, $50, $9A
 .byte $AA, $9D, $AA, $5A, $A0, $AA, $AA, $AA
 .byte $A8, $00, $02, $95, $7F, $A9, $5A, $50
 .byte $7F, $FE, $95, $5A, $55, $5F, $3F, $56
 .byte $AA, $BF, $D7, $5A, $50, $56, $AA, $AF
 .byte $D5, $40, $00, $00, $00, $00, $6A, $A9
 .byte $70, $5D, $FF, $A5, $6A, $5F, $FD, $AB
 .byte $F5, $56, $AA, $A9, $5D, $55, $AA, $A8
 .byte $00, $00, $55, $A9, $50, $55, $57, $A5
 .byte $57, $5D, $50, $6A, $A9, $55, $DD, $55
 .byte $5A, $A9, $60, $A0, $02, $AA, $95, $55
 .byte $A5, $95, $55, $55, $A0, $AB, $55, $AA
 .byte $95, $D5, $6A, $AA, $96, $95, $55, $55
 .byte $56, $2A, $AA, $A5, $9A, $A0, $5A, $95
 .byte $FE, $AA, $AA, $AA, $AA, $D5, $6A, $AA
 .byte $AA, $AA, $9A, $A5, $AA, $05, $5A, $A6
 .byte $A5, $56, $A5, $A9, $55, $56, $A5, $F5
 .byte $6A, $A5, $6A, $80, $2A, $A9, $EA, $A6
 .byte $A9, $5A, $A7, $EA, $AA, $AA, $AD, $AA
 .byte $AA, $AA, $AA, $95, $5A, $00, $E0, $85
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $10, $10
 .byte $AA, $6A, $5A, $96, $A5, $A9, $AA, $AA
 .byte $A0, $A0, $A0, $A0, $A0, $60, $50, $90
 .byte $AA, $A0, $A9, $5A, $55, $6A, $50, $00
 .byte $0A, $A5, $5F, $AA, $AA, $AA, $A9, $DD
 .byte $DF, $AA, $95, $AA, $A9, $D6, $AA, $AA
 .byte $AA, $00, $2A, $AA, $AA, $95, $AA, $A9
 .byte $55, $57, $AA, $95, $57, $03, $D0, $96
 .byte $AA, $AA, $AA, $5A, $A0, $AA, $AA, $AA
 .byte $A8, $00, $02, $A7, $5F, $A9, $5A, $70
 .byte $FD, $DE, $95, $5A, $55, $5F, $3F, $5A
 .byte $AA, $75, $75, $6A, $A0, $55, $AA, $AB
 .byte $DD, $55, $40, $00, $00, $00, $6A, $A9
 .byte $70, $5D, $7F, $A5, $6A, $5F, $FD, $AB
 .byte $F7, $56, $AA, $A5, $D5, $D5, $AA, $A8
 .byte $00, $00, $00, $A9, $50, $D5, $75, $A5
 .byte $57, $FD, $50, $6A, $A9, $7D, $D7, $55
 .byte $6A, $A5, $60, $A0, $02, $AA, $95, $55
 .byte $A5, $95, $55, $55, $60, $AB, $55, $AA
 .byte $75, $55, $6A, $AA, $56, $95, $55, $40
 .byte $00, $AA, $AA, $55, $56, $90, $5A, $95
 .byte $7A, $AA, $AA, $AA, $AA, $D5, $6A, $AA
 .byte $AA, $AA, $9A, $A5, $6A, $00, $5A, $A6
 .byte $A5, $56, $A5, $A9, $55, $56, $AF, $55
 .byte $6A, $95, $6A, $80, $AA, $A5, $7A, $A6
 .byte $A9, $5A, $A5, $AA, $AA, $AA, $AD, $AA
 .byte $AA, $AA, $AA, $95, $5A, $00, $B0, $01
 .byte $00, $54, $54, $54, $54, $04, $54, $54
 .byte $10, $54, $04, $00, $A8, $A8, $A8, $A8
 .byte $08, $A8, $A8, $20, $A8, $08, $00, $88
 .byte $A0, $28, $A0, $A8, $A8, $88, $A8, $20
 .byte $A8, $88, $88, $80, $88, $A8, $20, $A8
 .byte $20, $88, $88, $20, $A8, $88, $41, $04
 .byte $6A, $5A, $96, $A5, $A9, $AA, $AA, $AA
 .byte $A0, $A0, $A0, $A0, $60, $50, $90, $A0
 .byte $AA, $A0, $A9, $5A, $55, $6A, $50, $00
 .byte $2A, $A5, $DF, $AA, $AA, $AA, $A9, $D5
 .byte $DD, $AA, $D5, $AA, $AB, $56, $AA, $AA
 .byte $AA, $00, $02, $AA, $AA, $95, $6A, $A9
 .byte $55, $57, $6A, $A5, $57, $03, $D0, $96
 .byte $AA, $AA, $AA, $5A, $A0, $AA, $AA, $AA
 .byte $A8, $00, $02, $A9, $DF, $AA, $5A, $50
 .byte $5F, $DA, $95, $56, $95, $5F, $3F, $5A
 .byte $AB, $D7, $5D, $AA, $A0, $95, $6A, $AB
 .byte $FF, $FD, $80, $00, $00, $01, $6A, $A9
 .byte $50, $7F, $7F, $A5, $5A, $7F, $CF, $AA
 .byte $D5, $5A, $AA, $AD, $55, $55, $AA, $AA
 .byte $40, $00, $00, $AB, $50, $57, $57, $A5
 .byte $DF, $FD, $50, $6A, $AB, $FF, $75, $55
 .byte $6A, $A7, $60, $A0, $00, $AA, $55, $55
 .byte $A5, $95, $55, $55, $60, $AB, $D5, $AA
 .byte $57, $55, $AA, $AA, $56, $95, $00, $00
 .byte $00, $AA, $A9, $55, $56, $90, $5A, $95
 .byte $7A, $AA, $AA, $AA, $AA, $D5, $6A, $AA
 .byte $AA, $AA, $9A, $A5, $6A, $00, $0A, $A6
 .byte $A5, $56, $A5, $A9, $55, $56, $AF, $D5
 .byte $AA, $B5, $6A, $80, $2A, $95, $5A, $A6
 .byte $A5, $56, $A5, $AA, $AA, $AA, $A5, $AA
 .byte $5A, $AA, $AA, $55, $56, $80, $30, $EB
 .byte $00, $44, $10, $40, $04, $04, $04, $44
 .byte $10, $44, $04, $54, $88, $20, $80, $08
 .byte $08, $08, $88, $20, $88, $08, $A8, $A8
 .byte $88, $80, $88, $80, $88, $88, $20, $88
 .byte $80, $88, $A8, $80, $A0, $08, $20, $88
 .byte $20, $A8, $88, $20, $80, $88, $44, $04
 .byte $5A, $96, $A5, $A9, $AA, $AA, $AA, $6A
 .byte $A0, $A0, $A0, $60, $50, $90, $A0, $A0
 .byte $AA, $A0, $A9, $5A, $55, $6A, $50, $00
 .byte $AA, $A5, $57, $AA, $AA, $AA, $AA, $77
 .byte $5D, $AA, $D5, $AA, $AA, $DA, $AA, $AA
 .byte $AA, $00, $00, $AA, $AA, $95, $6A, $A9
 .byte $55, $77, $6A, $A5, $57, $03, $D0, $96
 .byte $AA, $AA, $AA, $55, $50, $AA, $AA, $5A
 .byte $AA, $00, $02, $AA, $77, $AA, $56, $90
 .byte $F5, $DA, $95, $56, $95, $5F, $3F, $5A
 .byte $AB, $75, $D5, $AA, $A0, $AA, $AA, $AA
 .byte $FE, $AA, $80, $00, $00, $05, $6A, $AA
 .byte $50, $7F, $FE, $A5, $5A, $5F, $FD, $AA
 .byte $D5, $6A, $AA, $97, $5D, $55, $AA, $AA
 .byte $00, $00, $00, $A9, $50, $5D, $77, $A5
 .byte $5F, $FD, $D0, $6A, $AB, $FF, $57, $55
 .byte $AA, $95, $60, $A0, $00, $AA, $55, $55
 .byte $A5, $95, $55, $55, $60, $AB, $55, $A9
 .byte $D5, $55, $AA, $AA, $56, $90, $00, $00
 .byte $00, $AA, $A9, $55, $55, $90, $5A, $95
 .byte $5A, $AA, $AA, $AA, $AB, $D5, $6A, $AA
 .byte $A6, $AA, $9A, $95, $6A, $00, $0A, $A6
 .byte $A5, $5E, $A5, $A9, $55, $56, $AD, $D6
 .byte $AA, $55, $6A, $80, $2A, $95, $5A, $A6
 .byte $A5, $56, $A5, $AA, $95, $AA, $A5, $A9
 .byte $56, $AA, $AA, $55, $56, $80, $A2, $14
 .byte $00, $44, $10, $54, $14, $54, $54, $54
 .byte $10, $54, $54, $54, $88, $20, $A8, $28
 .byte $A8, $A8, $A8, $20, $A8, $A8, $A8, $88
 .byte $A0, $80, $88, $A0, $88, $A8, $20, $08
 .byte $80, $88, $A8, $A8, $A8, $20, $20, $88
 .byte $88, $88, $20, $20, $20, $A0, $44, $04
 .byte $96, $A5, $A9, $AA, $AA, $AA, $6A, $5A
 .byte $A0, $A0, $60, $50, $90, $A0, $A0, $A0
 .byte $AA, $A0, $A9, $5A, $55, $6A, $50, $02
 .byte $AA, $A5, $76, $AA, $AA, $AA, $AA, $75
 .byte $7D, $AA, $D5, $6A, $AA, $DA, $AA, $AA
 .byte $AA, $00, $00, $0A, $AA, $A5, $6A, $A9
 .byte $55, $9F, $6A, $A5, $57, $03, $D0, $95
 .byte $AA, $AA, $AA, $03, $50, $55, $55, $56
 .byte $AA, $00, $02, $AA, $9E, $AA, $56, $90
 .byte $5F, $5A, $55, $56, $95, $5F, $0F, $5A
 .byte $AB, $DD, $D6, $AA, $A0, $AA, $AA, $AA
 .byte $AA, $AA, $00, $00, $00, $57, $6A, $AA
 .byte $70, $77, $5E, $A5, $5A, $5C, $FD, $AA
 .byte $D5, $6A, $AA, $75, $55, $55, $6A, $AA
 .byte $40, $00, $00, $A9, $50, $D5, $F7, $A5
 .byte $5F, $CD, $50, $6A, $AB, $FF, $F5, $56
 .byte $AA, $9D, $60, $A0, $00, $2A, $55, $55
 .byte $A5, $55, $55, $D5, $50, $AB, $55, $A9
 .byte $75, $55, $AA, $A9, $56, $80, $00, $00
 .byte $00, $2A, $A5, $55, $55, $50, $56, $95
 .byte $5A, $AA, $AA, $AA, $AB, $55, $6A, $AA
 .byte $55, $AA, $9A, $95, $5A, $00, $0A, $A6
 .byte $A5, $7E, $A5, $A9, $55, $56, $AD, $5A
 .byte $AA, $75, $5A, $80, $0A, $95, $5A, $A6
 .byte $A5, $56, $A5, $AA, $55, $6A, $A5, $A9
 .byte $D6, $AA, $AA, $55, $56, $A8, $04, $06
 .byte $00, $44, $50, $04, $04, $44, $40, $40
 .byte $04, $44, $44, $F4, $88, $A0, $08, $08
 .byte $88, $80, $80, $08, $88, $88, $F8, $88
 .byte $88, $80, $88, $80, $80, $88, $20, $08
 .byte $80, $A8, $A8, $88, $88, $80, $20, $88
 .byte $88, $88, $88, $88, $08, $A0, $44, $04
 .byte $A5, $A9, $AA, $AA, $AA, $6A, $5A, $96
 .byte $A0, $60, $50, $90, $A0, $A0, $A0, $A0
 .byte $AA, $A0, $A9, $5A, $55, $6A, $50, $0A
 .byte $AA, $A9, $56, $AA, $AA, $AA, $AA, $55
 .byte $DE, $AA, $D5, $6A, $AA, $DA, $AA, $95
 .byte $5A, $00, $00, $00, $AA, $A5, $4A, $A9
 .byte $56, $9F, $7A, $A5, $57, $00, $D0, $95
 .byte $AA, $AA, $AA, $00, $10, $55, $55, $55
 .byte $AA, $80, $02, $AA, $AA, $AA, $56, $A0
 .byte $77, $6A, $55, $56, $95, $5F, $0F, $FA
 .byte $AB, $77, $5A, $AA, $A0, $AA, $AA, $AA
 .byte $AA, $A0, $00, $00, $05, $7F, $6A, $AA
 .byte $70, $75, $DE, $A5, $5A, $5F, $FD, $AA
 .byte $D5, $AA, $A9, $F7, $75, $55, $6A, $AA
 .byte $94, $00, $00, $A9, $50, $5D, $77, $A5
 .byte $5F, $FD, $50, $6A, $AB, $F7, $55, $5A
 .byte $AA, $95, $50, $A0, $00, $2A, $55, $55
 .byte $A5, $55, $55, $55, $50, $AB, $D5, $A7
 .byte $55, $56, $AA, $A9, $55, $80, $00, $00
 .byte $00, $2A, $A5, $55, $55, $50, $56, $A5
 .byte $5A, $AA, $96, $AA, $AB, $55, $6A, $A9
 .byte $D5, $6A, $9A, $95, $5A, $00, $0A, $A6
 .byte $A5, $7E, $A5, $A9, $55, $56, $AD, $6A
 .byte $AB, $55, $5A, $A0, $0A, $55, $5A, $A6
 .byte $A5, $56, $A5, $AA, $55, $5A, $AD, $A5
 .byte $5A, $A9, $AA, $55, $00, $80, $F9, $5F
 .byte $00, $54, $10, $54, $54, $44, $54, $54
 .byte $54, $54, $54, $54, $A8, $20, $A8, $A8
 .byte $88, $A8, $A8, $A8, $A8, $A8, $A8, $20
 .byte $A0, $28, $A0, $A8, $A8, $88, $A8, $08
 .byte $80, $88, $88, $A8, $A8, $A8, $A8, $88
 .byte $88, $88, $88, $88, $A8, $88, $41, $04
 .byte $A9, $AA, $AA, $AA, $6A, $5A, $96, $A5
 .byte $60, $50, $90, $A0, $A0, $A0, $A0, $A0
 .byte $AA, $A0, $A9, $5A, $55, $6A, $50, $0A
 .byte $AA, $A9, $56, $AA, $95, $AA, $AA, $5D
 .byte $DE, $AA, $D5, $6A, $AA, $9A, $AA, $55
 .byte $56, $00, $00, $00, $0A, $A9, $0A, $A5
 .byte $56, $AF, $7A, $A5, $57, $00, $D0, $A5
 .byte $6A, $AA, $A8, $00, $00, $55, $55, $55
 .byte $6A, $A0, $02, $AA, $AA, $AA, $56, $A0
 .byte $77, $6A, $55, $55, $95, $57, $0F, $5A
 .byte $AB, $5F, $5A, $AA, $A0, $AA, $AA, $AA
 .byte $AA, $80, $00, $00, $55, $5F, $5A, $AA
 .byte $70, $5F, $FE, $A5, $5A, $5F, $CD, $AA
 .byte $55, $AA, $A9, $F7, $D6, $55, $5A, $AA
 .byte $B5, $00, $00, $A9, $70, $55, $FF, $A5
 .byte $7C, $FF, $50, $5A, $AB, $F5, $D5, $6A
 .byte $AA, $75, $50, $AB, $00, $09, $55, $57
 .byte $A5, $55, $57, $55, $50, $AB, $55, $AD
 .byte $55, $56, $AA, $A9, $55, $80, $00, $00
 .byte $00, $0A, $95, $55, $55, $50, $56, $A5
 .byte $6A, $A9, $55, $5A, $AB, $55, $AA, $A5
 .byte $55, $6A, $9A, $95, $5A, $00, $5A, $A6
 .byte $A5, $FE, $A5, $A9, $55, $56, $AD, $6A
 .byte $A5, $55, $5A, $A0, $02, $55, $5A, $A6
 .byte $A5, $56, $A5, $A9, $55, $5A, $A5, $A5
 .byte $5A, $A5, $AA, $50, $00, $A8, $10, $90
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $00, $00
 .byte $00, $00, $00, $00, $00, $00, $10, $10
 .byte $AA, $AA, $AA, $6A, $5A, $96, $A5, $A9
 .byte $50, $90, $A0, $A0, $A0, $A0, $A0, $60
 .byte $AA, $A0, $A9, $5A, $55, $6A, $50, $2A
 .byte $AA, $A9, $76, $AA, $55, $6A, $AA, $95
 .byte $7E, $AA, $D5, $6A, $AA, $9A, $A9, $55
 .byte $55, $00, $00, $00, $00, $A8, $02, $A5
 .byte $56, $AF, $7A, $A5, $57, $00, $D0, $A5
 .byte $6A, $AA, $A8, $00, $00, $05, $55, $55
 .byte $5A, $A8, $00, $2A, $AA, $AA, $55, $A0
 .byte $9D, $AA, $55, $55, $95, $57, $0F, $9A
 .byte $AA, $77, $6A, $AA, $A0, $AA, $AA, $AA
 .byte $AA, $00, $00, $02, $55, $DF, $5A, $AA
 .byte $50, $F7, $7E, $95, $5A, $5F, $3F, $AA
 .byte $56, $AA, $AD, $DD, $DA, $59, $56, $AA
 .byte $AD, $50, $00, $A9, $50, $75, $DF, $A5
 .byte $5F, $FD, $50, $5A, $AB, $FD, $55, $AA
 .byte $AA, $55, $50, $A8, $00, $00, $00, $77
 .byte $A5, $55, $75, $75, $50, $AB, $D5, $95
 .byte $D5, $5A, $AA, $A9, $55, $80, $00, $00
 .byte $00, $0A, $95, $55, $55, $50, $56, $A5
 .byte $6A, $A5, $55, $55, $AB, $55, $AA, $9D
 .byte $55, $6A, $96, $95, $56, $05, $7A, $A6
 .byte $A5, $FE, $A5, $A9, $57, $D6, $A5, $AA
 .byte $AD, $D5, $56, $AD, $02, $55, $5A, $A6
 .byte $A5, $56, $A5, $A9, $55, $5A, $A5, $B5
 .byte $5A, $A5, $AA, $00, $00, $00, $D0, $28

; ORG $D000

MenuUpdateCounter
  LDA EasterEggJoyDownCount
; easter egg phase 2: if the first phase completed successfully
; the left press counter is allowed to increment.
  CMP #53
  BNE SkipCounting2ndEasterEggPhase
  INC EasterEggJoyLeftCount
SkipCounting2ndEasterEggPhase
  JMP MenuTextUpdate
MenuSelectTwoPlayers
  LDA #$01
  STA PlayerCount
  BNE SkipCounting1stEasterEggPhase

MenuSelectOnePlayer                ; (the joystick was pressed down in the title menu)
  LDA #$00
  STA PlayerCount
                                   ; easter egg phase 1: if the second phase isn't in progress the
                                   ; down press counter is allowed to increment.
  LDA EasterEggJoyLeftCount
  BNE SkipCounting1stEasterEggPhase
  INC EasterEggJoyDownCount
  JMP MenuTextUpdate
SkipCounting1stEasterEggPhase
  LDA #$00
  STA EasterEggJoyDownCount
  STA EasterEggJoyLeftCount
  BEQ MenuTextUpdate
MenuHandlingAndUpdate
  LDA SWCHB
  AND #$02
  BEQ MenuSelectPressed
  LDA #$00
  STA MenuSelectDebounceTimer
  BPL SkipMenuRedraw
MenuSelectPressed
  DEC MenuSelectDebounceTimer
  BPL SkipMenuRedraw
  JSR SetMenuSelectDebounceTimerTo20
  JSR ChangeDifficulty
MenuTextUpdate
  JSR SetMenuResetTimer
  JSR SetupGameOptionsText
SkipMenuRedraw

; implement a ~18 minute timer that just resets the stack and demo mode.
; I'm thinking they had a slow stack leak during development.
  LDA TimerHi
  CMP MenuResetTimer
  BNE ContinueMenuLoop
  JMP ResetStackAndStartGameInDemoMode
ContinueMenuLoop
  LDA TimerLo
MenuWaitForNextFrame
  CMP TimerLo
  BEQ MenuWaitForNextFrame
  JMP GameOptionsMenuLoop

SetMenuJoyDebounceTimerTo20
  LDA #$20
  STA MenuJoyDebounceTimer
  RTS

SetMenuSelectDebounceTimerTo20
  LDA #$20
  STA MenuSelectDebounceTimer
  RTS

SetMenuResetTimer
  LDX TimerHi
  DEX
  STX MenuResetTimer
  RTS

ChangeDifficulty
  INC Difficulty
  LDA Difficulty
  CMP #$04
  BMI FinalizeDifficultyChange
  LDA #$00
  STA Difficulty

  LDA PlayerCount
  EOR #$01
  STA PlayerCount
FinalizeDifficultyChange
  JMP ClearScores
SetupGameOptionsText
  LDX PlayerCount
  LDA HideOrShowTwoPlayerText,X
  STA MenuTwoPlayerTextXCoord   ; set X coordinate of upper DL object = 160 or 48, to hide or display "two player"
  LDA HideOrShowOnePlayerText,X
  STA MenuOnePlayerTextXCoord   ; set X coordinate of lower DL object = 48 or 160, to display of hide "one player"
  LDA Difficulty
  TAX
  ASL
  ASL
  ASL
  ASL                    ; A = Difficulty * 16
  CLC
  ADC #<DifficultyText
  STA Temp5E
  LDA #$00
  ADC ##>DifficultyText
  STA Temp5F             ; ($5E) = current difficulty name

  LDY #$0F               ; copy over 16 characters
CopyDifficultyNameLoop
  LDA (Temp5E),Y
  STA DifficultyLevelCharBuffer,Y
  DEY
  BPL CopyDifficultyNameLoop

  LDA DifficultyTextXCoordinate,X  ; set X coordinate of mid DL object (difficulty level text)
  STA MenuDifficultyTextXCoord     ; differently, depending on the difficulty level.

  LDA EasterEggJoyLeftCount

; easter egg phase 3: check if phase 2 (left 41 times) is done. 
; if so, set us up the egg!
  CMP #41
  BNE ExitMenuEasterEggCheck
  LDA TimerLo
  AND #$03
  CMP #$03
  BNE ExitMenuEasterEggCheck
  DEC EasterEggXCoordinate1
  DEC EasterEggXCoordinate2
  DEC EasterEggXCoordinate3
  LDA #$01
  STA DisplayEasterEggFlag
ExitMenuEasterEggCheck
  RTS

CheckMenuFireButtonAndStartGame
  LDA P0CurrentFire
  BPL ResetStackAndStartGameInDemoMode_Short
  LDA PrevResetState
  BNE MenuUpdateResetButtonState
  LDA SWCHB
  LSR
  BCS ExitMenuEasterEggCheck
  LDA #$01
  STA PrevResetState
ResetStackAndStartGameInDemoMode_Short
  JMP RestartGameFromMenu
MenuUpdateResetButtonState
  JMP UpdatePrevResetStateFlag

HandleDemoModeJoyInput
  LDA P0CurrentFire
CheckForAttractModeExit
  BPL ResetStackAndStartGameInDemoMode_Short
  LDA SWCHA
  CMP #$F0
  BCS CheckResetSwitch
  JMP ResetStackAndDisplayGameOptions


;------------------------------------------------------------------------------
; HandleConsoleSwitches
; Checks the state of Pause, Reset, and Select console switches and acts accordingly.
; This routine is typically called during active gameplay.
;------------------------------------------------------------------------------
HandleConsoleSwitches
  LDA DemoMode
  BNE HandleDemoModeJoyInput  ; If in demo mode, only check for joystick/fire to exit demo

                              ; Fall-through if not in Demo Mode
CheckPauseSwitch
  LDA PrevPausedState         ; Get previous state of Pause switch (was it already pressed?)
  BNE UpdatePauseSwitchState  ; If it was pressed, go update its current debounced state

                          ; Pause switch was not previously pressed, check if it's pressed now
  LDA SWCHB               ; Read console switch register B
  AND #SWCHB_Pause_Mask   ; Isolate Pause switch bit (Bit 3)
  BNE CheckResetSwitch    ; If Pause bit is high (not pressed), check Reset switch

                          ; Pause switch is now pressed (bit 3 is low)
  LDA #$01                ; Mark that Pause has been latched as pressed
  STA PrevPausedState
  BNE EnterPauseSequence  ; Branch always (Pause switch was just pressed, so enter pause logic)

UpdatePauseSwitchState    ; Pause switch was previously latched as pressed
  LDA SWCHB
  AND #SWCHB_Pause_Mask   ; Isolate Pause bit
  EOR #SWCHB_Pause_Mask   ; Invert Pause bit (so $00=released this cycle, $08=still held)
  STA PrevPausedState     ; Update debounced state

CheckResetSwitch
  LDA PrevResetState                  ; Get previous state of Reset switch
  BNE CheckResetSwitchAndCheckSelect  ; If it was pressed, go update its current debounced state

; Reset switch was not previously pressed, check if it's pressed now
  LDA SWCHB
  LSR                           ; Shift Reset bit (Bit 0) into Carry
  BCS CheckSelectSwitch_Direct  ; If Carry is set (Reset bit was high, not pressed), check Select

                                              ; Reset switch is now pressed (Bit 0 was low)
  LDA #$01                                    ; Mark that Reset has been latched as pressed
  STA PrevResetState
  BNE ResetStackAndStartGameInDemoMode_Short  ; Branch always (Reset was just pressed)

CheckResetSwitchAndCheckSelect
  JSR UpdatePrevResetStateFlag          ; Update PrevResetState based on current SWCHB_Reset_Mask state
                                        ; Falls through to CheckSelectSwitch_Direct
CheckSelectSwitch_Direct
  LDA PrevSelectState                   ; Get previous state of Select switch
  BNE UpdateSelectSwitchStateAndFinish  ; If it was pressed, go update its current debounced state

; Select switch was not previously pressed, check if it's pressed now
  LDA SWCHB
  AND #SWCHB_Select_Mask    ; Isolate Select switch bit (Bit 1)
  BNE FinishSwitchHandling  ; If Select bit is high (not pressed), we're done

; Select switch is now pressed (Bit 1 is low)
  LDA #$01                             ; Mark that Select has been latched as pressed
  STA PrevSelectState
  JMP ResetStackAndDisplayGameOptions  ; Go to game options screen

UpdateSelectSwitchStateAndFinish
  LDA SWCHB
  AND #SWCHB_Select_Mask
  EOR #SWCHB_Select_Mask  ; Invert Select bit
  STA PrevSelectState     ; Update debounced state
                          ; Falls through to FinishSwitchHandling
FinishSwitchHandling
  RTS

;------------------------------------------------------------------------------
; EnterPauseSequence / PauseGameAndWaitForInput
; Handles the game pause state. Waits for Pause to be released or other inputs.
; Mutes audio, saves timers, and monitors switches.
;------------------------------------------------------------------------------
EnterPauseSequence
  LDA TimerHi
  PHA  ; Save current game timers
  LDA TimerLo
  PHA
  TXA  ; Save X register (usually current player index)
  PHA

  LDX #$01
  STX DemoMode  ; Temporarily set DemoMode to 1 (likely to alter some behaviors)
  DEX           ; X is now 0
  STX AUDV0     ; Mute audio channel 0
  STX AUDV1     ; Mute audio channel 1

  LDX TimerHi                     ; Use current TimerHi for a short timeout/loop mechanism
  DEX                             ; X = TimerHi - 1
PauseLoop
  JSR CheckResetSwitch            ; Allow Reset/Select to break out of pause. This will RTS if no action.

  CPX TimerHi                     ; Compare saved TimerHi-1 with current TimerHi
  BNE PauseLoop_CheckPlayerInput  ; If TimerHi has incremented, skip DMA off
  LDA #CTRL_DMA_OFF
  STA CTRL

PauseLoop_CheckPlayerInput
  LDA SWCHA                  ; Read Player 1 Joystick/Difficulty Switches
  CMP #$F0                   ; Check if all P1 joystick directions are high (neutral)
  BCS PauseLoop_CheckP1Fire  ; If neutral or invalid, check P1 fire
  JSR DisableDMAOnTimeout    ; If joystick active & timeout, potentially change CTRL

PauseLoop_CheckP1Fire
  LDA P0CurrentFire                      ; Check Player 1 fire button state
  BMI PauseLoop_ProcessPauseButtonState  ; If fire pressed, proceed
  JSR DisableDMAOnTimeout                ; If fire not pressed & timeout, potentially change CTRL

PauseLoop_ProcessPauseButtonState
  LDA PrevPausedState                             ; Get latched/debounced state of Pause
  BEQ PauseLoop_IfPreviouslyReleasedCheckCurrent  ; If PrevPausedState is 0 (was seen as released)

; PrevPausedState is non-zero (was latched as pressed, or seen as still held)
  LDA SWCHB              ; Read current physical switch state
  AND #SWCHB_Pause_Mask  ; Isolate Pause bit
  EOR #SWCHB_Pause_Mask  ; Invert Pause bit (result $00 if physically RELEASED now, $08 if HELD)
  STA PrevPausedState    ; Update internal debounced state
  BPL PauseLoop          ; If $00 (just released) or $08 (still held), continue pause loop

PauseLoop_IfPreviouslyReleasedCheckCurrent
  LDA SWCHB
  AND #SWCHB_Pause_Mask  ; Isolate current physical Pause bit
  BNE PauseLoop          ; If Pause button is high (physically RELEASED), continue pause loop.
                         ; Loop until Pause is physically PRESSED AGAIN.

; Fall-through: Pause button is now physically PRESSED again (value is $00 after AND)
; This is the condition to EXIT the pause state.

ResumeGameFromPause
  PLA
  TAX          ; Restore X register
  PLA
  STA TimerLo  ; Restore game timers
  PLA
  STA TimerHi

  LDA #$01      ; Set PrevPausedState to 1. This indicates the pause cycle is complete
               ; and the pause button is considered "up" for the next check in HandleConsoleSwitches.
  STA PrevPausedState
  LSR           ; A = 0 (from $01 LSR)
  STA DemoMode  ; Clear the temporary DemoMode set during pause
  LDA #CTRL_DMA_ON_160AB
  STA CTRL
  RTS

SetupTitleScrText
; The Titlescreen border in Joust has a bunch of slanty angle characters
; that scroll. The following code just sets up the initial slanty char
; indexes in each string.

SetupTitleColumns
  LDA #$30
  STA TitleBorderStr0
  LDA #$38
  STA TitleBorderStr1
  LDA #$36
  STA TitleBorderStr2
  LDA #$3E
  STA TitleBorderStr3

  LDX #$1E
SetupTitleRowLoop
  LDA #$30
  STA TitleBorderStr4,X
  LDA #$34
  STA TitleBorderStr5,X
  DEX
  DEX
  BPL SetupTitleRowLoop
  LDX #$1C
  STX TitleColorIndex
  LDA TitleColors,X
  STA $0191
  LDA DelayBetweenTitleColorChanges,X
  STA TitleColorChangeCountdown

  LDX #$1C
CopyrghtToBuffer
  LDA StringCopyright,X
  STA TitleCopyrightCharBuffer,X
  DEX
  BPL CopyrghtToBuffer

; Copy character set values that make up most of the JOUST logo.
; The very top and bottom of the logo aren't part of this.
  LDX #$E7
CopyMiddleTitleLogoDataToRamLoop
  LDA MiddleTitleLogoDataCharsLUT,X
  STA TitleScreenLogoCharBuffer,X
  DEX
  BNE CopyMiddleTitleLogoDataToRamLoop

SetDemoPalette
  LDX #$0B
  STX EggsPresentCount
  LDA PaletteFadeInRed,X
  STA $01A5
  STA P7C1
  LDA PaletteFadeInYellow,X
  STA $01A6
  STA P7C2
  LDA PaletteFadeInBrown,X
  STA $01A7
  STA P7C3
  LDA TitleScreenColorChangeDelayLUT,X
  STA EnemyBirdCount
  RTS

AnimateTitleScreenFrame
  DEC TitleColorChangeCountdown
  BPL DoTitleFrameAnimation
  DEC TitleColorIndex
  BPL WrapTitleColorIndex
  LDA #$1C
  STA TitleColorIndex
WrapTitleColorIndex
  LDX TitleColorIndex
  LDA TitleColors,X
  STA $0191
  LDA DelayBetweenTitleColorChanges,X
  STA TitleColorChangeCountdown
DoTitleFrameAnimation
  INC TitleBorderStr0       ; left-most char in left column
  LDA TitleBorderStr0
  CMP #$38
  BCC SkipCharResetStr0
  LDA #$30
  STA TitleBorderStr0
SkipCharResetStr0
  INC TitleBorderStr2       ; left-most char in right column
  LDA TitleBorderStr2
  CMP #$38
  BCC SkipCharResetStr2
  LDA #$30
  STA TitleBorderStr2
SkipCharResetStr2
  INC TitleBorderStr1       ; right-most char in left column
  LDA TitleBorderStr1
  CMP #$40
  BCC SkipCharResetStr1
  LDA #$38
  STA TitleBorderStr1
SkipCharResetStr1
  INC TitleBorderStr3       ; right-most char in right column
  LDA TitleBorderStr3
  CMP #$40
  BCC SkipCharResetStr3
  LDA #$38
  STA TitleBorderStr3
SkipCharResetStr3
  LDX #$1F
AnimateTitleFrameColumnsLoop
  INC TitleBorderStr4,X     ; top row and bottom row of 32 characters
  LDA TitleBorderStr4,X
  CMP #$38
  BCC SkipCharResetStr4plus
  LDA #$30
  STA TitleBorderStr4,X
SkipCharResetStr4plus
  DEX
  BPL AnimateTitleFrameColumnsLoop

CycleTitleScreenPaletteAndCounters
  LDA ColorChangeTimer
  BMI CycleColorExit
  DEC ColorChangeTimer
  BPL CycleColorExit
  DEC ColorCycleIndex
  BPL CycleIndexStillPositive
  LDA #$0B
  STA ColorCycleIndex
CycleIndexStillPositive
  LDX ColorCycleIndex
  LDA PaletteFadeInRed,X
  STA $01A5
  STA P7C1
  LDA PaletteFadeInYellow,X
  STA $01A6
  STA P7C2
  LDA PaletteFadeInBrown,X
  STA $01A7
  STA P7C3
  LDA TitleScreenColorChangeDelayLUT,X
  STA ColorChangeTimer
CycleColorExit
  RTS

UpdatePrevResetStateFlag
  LDA SWCHB
  AND #$01
  EOR #$01
  STA PrevResetState
  RTS

DisableDMAOnTimeout
  LDA #$40
  STA CTRL
  LDX TimerHi
  DEX
  RTS

HideOrShowTwoPlayerText
 .byte 160                ; off-screen
HideOrShowOnePlayerText
 .byte 48                 ; on-screen
 .byte 160                ; off-screen

; 000000000000000011111111111111112222222222222222
; 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
; _0123456789@0123456789@ABCDEGHIJLMNPRSTUVWXYZK() **then the title border and logo chars

DifficultyTextXCoordinate
 .byte $00
 .byte $20
 .byte $45
 .byte $62

DifficultyText
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $18      ; 'B'
 .byte $1B      ; 'E'
 .byte $1C      ; 'G'
 .byte $1E      ; 'I'
 .byte $22      ; 'N'
 .byte $22      ; 'N'
 .byte $1B      ; 'E'
 .byte $24      ; 'R'
 .byte $00
 .byte $00
 .byte $00
 .byte $00

 .byte $00
 .byte $00
 .byte $1E      ; 'I'
 .byte $22      ; 'N'
 .byte $26      ; 'T'
 .byte $1B      ; 'E'
 .byte $24      ; 'R'
 .byte $21      ; 'M'
 .byte $1B      ; 'E'
 .byte $1A      ; 'D'
 .byte $1E      ; 'I'
 .byte $17      ; 'A'
 .byte $26      ; 'T'
 .byte $1B      ; 'E'
 .byte $00
 .byte $00

 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $17      ; 'A'
 .byte $0C      ; '0'
 .byte $28      ; 'V'
 .byte $17      ; 'A'
 .byte $22      ; 'N'
 .byte $19      ; 'C'
 .byte $1B      ; 'E'
 .byte $1A      ; 'D'
 .byte $00
 .byte $00
 .byte $00
 .byte $00

 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $00
 .byte $1B      ; 'E'
 .byte $2A      ; 'X'
 .byte $23      ; 'P'
 .byte $1B      ; 'E'
 .byte $24      ; 'R'
 .byte $26      ; 'T'
 .byte $00
 .byte $00

StringOnePlayer
 .byte $00      ; \ These 3 bytes are also 
 .byte $00      ; > part of the "EXPERT" string
 .byte $00      ; / post-padding.

 .byte $0C      ; '0'
 .byte $22      ; 'N'
 .byte $1B      ; 'E'
 .byte $00      ; 
 .byte $23      ; 'P'
 .byte $20      ; 'L'
 .byte $17      ; 'A'
 .byte $2B      ; 'Y'
 .byte $1B      ; 'E'
 .byte $24      ; 'R'
 .byte $00
 .byte $00
 .byte $00

StringTwoPlayer
 .byte $00
 .byte $00
 .byte $00
 .byte $26  ; 'T'
 .byte $29  ; 'W'
 .byte $0C  ; '0'
 .byte $00
 .byte $23  ; 'P'
 .byte $20  ; 'L'
 .byte $17  ; 'A'
 .byte $2B  ; 'Y'
 .byte $1B  ; 'E'
 .byte $24  ; 'R'
 .byte $00
 .byte $00

MiddleTitleLogoDataCharsLUT
 .byte $00, $59, $5A, $5B, $5C, $5D, $5E, $41
 .byte $40, $40, $5F, $60, $61, $62, $63, $64
 .byte $65, $66, $43, $40, $67, $68, $40, $69
 .byte $40, $6A, $6B, $6C, $6D, $6E, $6F, $70
 .byte $71, $72, $73, $74, $40, $75, $76, $40
 .byte $77, $78, $79, $40, $7A, $7B, $40, $7C
 .byte $7D, $7E, $46, $43, $40, $7F, $40, $80
 .byte $81, $82, $83, $43, $84, $85, $86, $87
 .byte $88, $89, $8A, $8B, $8C, $8D, $40, $42
 .byte $8E, $8F, $90, $91, $92, $40, $93, $43
 .byte $40, $44, $95, $96, $46, $43, $97, $98
 .byte $99, $40, $9A, $9B, $9C, $9D, $46, $9E
 .byte $9F, $A0, $A1, $A2, $40, $42, $45, $A3
 .byte $A4, $A5, $A6, $40, $A7, $45, $40, $44
 .byte $A8, $A9, $AA, $AB, $AC, $AD, $AE, $AF
 .byte $B0, $40, $B1, $44, $B2, $40, $B3, $B4
 .byte $B5, $B6, $44, $44, $40, $42, $45, $42
 .byte $46, $44, $B7, $40, $B8, $45, $40, $B9
 .byte $BA, $BB, $BC, $40, $BD, $BE, $BF, $C0
 .byte $C1, $C2, $C3, $C4, $41, $40, $C5, $C6
 .byte $C7, $C8, $C9, $40, $CA, $CB, $CC, $40
 .byte $42, $43, $40, $CD, $44, $CE, $40, $CF
 .byte $D0, $40, $D1, $D2, $D3, $41, $40, $D4
 .byte $D5, $D6, $D7, $D8, $D9, $40, $DA, $41
 .byte $40, $DB, $44, $44, $DC, $47, $48, $40
 .byte $40, $40, $40, $49, $4A, $4B, $41, $4C
 .byte $4D, $4E, $4F, $50, $51, $40, $40, $40
 .byte $41, $40, $40, $52, $53, $40, $40, $40
 .byte $54, $55, $41, $40, $56, $57, $58, $40

DelayBetweenTitleColorChanges
 .byte $3C, $03, $03, $3C, $03, $03, $03
 .byte $03, $03, $3C, $03, $03, $03, $03
 .byte $03, $03, $3C, $03, $03, $03, $3C
 .byte $03, $03, $03, $03, $03, $03, $03
 .byte $03

TitleColors
 .byte $24, $15, $06, $F7, $D6, $C5, $B5
 .byte $A5, $95, $85, $76, $67, $58, $49
 .byte $3A, $2B, $1D, $1C, $1B, $0C, $0D
 .byte $0C, $0B, $1A, $19, $28, $27, $26 
 .byte $25

PaletteFadeInRed
 .byte $33 
 .byte $33 
 .byte $33 
 .byte $33 
 .byte $33 
 .byte $33 
 .byte $33 
 .byte $32 
 .byte $22 
 .byte $11 
 .byte $10 
 .byte $00 

PaletteFadeInYellow
 .byte $1F 
 .byte $1E 
 .byte $1D 
 .byte $1C 
 .byte $1B 
 .byte $1A 
 .byte $18 
 .byte $16 
 .byte $22 
 .byte $11 
 .byte $10 
 .byte $00 

PaletteFadeInBrown
 .byte $10 
 .byte $10 
 .byte $10 
 .byte $10 
 .byte $10 
 .byte $10 
 .byte $10 
 .byte $10 
 .byte $10 
 .byte $10 
 .byte $10 
 .byte $00 

TitleScreenColorChangeDelayLUT
 .byte $FF 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 

MuteAudio
  LDA #$FF
  STA SFXChannel0Index
  STA SFXChannel1Index
  LDA #$00
  STA AUDV0
  STA AUDV1
  RTS

SilenceSFXIfPlaying
; Check if this particular sound effect is playing on channel 0
  CMP SFXChannel0Index
  BNE SilenceSFXIfPlayingChannel1
; yes? then silence it and mark the channel index as available
  LDA #$00
  STA AUDV0
  LDA #$FF
  STA SFXChannel0Index
  RTS

SilenceSFXIfPlayingChannel1
; Check if this particular sound effect is playing on channel 1
  SEC
  SBC SFXChannel1Index
  BNE EarlyReturn1
; yes? then silence it and mark the channel index as available
  STA AUDV1
  LDA #$FF
  STA SFXChannel1Index
EarlyReturn1
  RTS

ScheduleSFX
  STA Temp6ASFXIndex
  LDA GameActiveFlag
  BNE EarlyReturn1
  LDA DemoMode
  BNE EarlyReturn1
  TYA
  PHA
  TXA
  PHA
  LDY #$01
  LDX Temp6ASFXIndex
FindFreeSFXChannelLoop
  LDA.w SFXChannel0Index,Y
  CMP #$FF
  BEQ ScheduleSFXChannelY
  DEY
  BPL FindFreeSFXChannelLoop
; If we're here there was no free sound effect channel. Instead we'll
; look for a sound effect in the same "interrupt group" and interrupt
; it.
  LDA SFXChannel1Index
  AND #$7F
  TAY
  LDA SFXInterruptGroups,X
  CMP SFXInterruptGroups,Y
  LDY #$01
  BCS ScheduleSFXChannelY
  LDA SFXChannel0Index
  AND #$7F
  TAY
  LDA SFXInterruptGroups,X
  CMP SFXInterruptGroups,Y
  BCC SkipScheduleSFX
  LDY #$00
ScheduleSFXChannelY
  TXA
  ORA #$80
  STA.w SFXChannel0Index,Y
SkipScheduleSFX
  PLA
  TAX
  PLA
  TAY
EarlyReturn2
  RTS

ServiceSFX
  LDA DemoMode
  BNE EarlyReturn2
  LDX #$01
ServiceSFXLoop
  LDY SFXChannel0Index,X
  INY                       ; FF is the voice not-in-use flag. FF+1=0
  BNE ServiceSFXDoVoice
GoServiceSFXVoiceDone
  JMP ServiceSFXVoiceDone
ServiceSFXDoVoice
  DEY                       ; undo the previous INY
  BPL PlaySoundSlice
                           ; If the SFX index top bit it set, the sound was just scheduled.
                           ; We need to do some init.
  TYA
  AND #$7F                  ; clear the "just scheduled" bit
  STA SFXChannel0Index,X
  TAY
  LDA #$01
  STA $2100,X
  LDA #$FF
  STA $2102,X
  STA $2104,X
  STA $2106,X
PlaySoundSlice
  LDA SFX_FreqAndControlDataPtrLoLUT,Y
  STA $5C
  LDA SFX_FreqAndControlDataPtrHiLUT,Y
  STA $5D
  DEC $2100,X
  BNE GoServiceSFXVoiceDone
  LDA SFX_SampleDurationLUT,Y
  STA $2100,X
  LDY $2102,X
  INC $2104,X
  INC $2106,X
  INY
  LDA ($5C),Y
  CMP #$FF                  ; sound EOD marker
  BNE SFXIsStillPlaying
  STA SFXChannel0Index,X
  LDA #$00
  STA AUDV0,X
  BEQ GoServiceSFXVoiceDone
SFXIsStillPlaying
  CMP #$FE
  BEQ SFX_HandleResetAndContinueCommand
  CMP #$FD
  BNE SFX_ProcessStandardPitchData
  INY
  LDA ($5C),Y
  STA SFXChannel0Index,X
  TAY
  LDA SFX_FreqAndControlDataPtrLoLUT,Y
  STA $5C
  LDA SFX_FreqAndControlDataPtrHiLUT,Y
  STA $5D
SFX_HandleResetAndContinueCommand
  LDY #$00
  TYA
  STA $2104,X
  STA $2106,X
  LDA ($5C),Y
SFX_ProcessStandardPitchData
  BPL SFX_ProcessPitchDataFlags
  INY
  LDA ($5C),Y
  STA $2100,X
  DEY
  LDA ($5C),Y
  INY
  JMP SFX_SetChannelPitch
SFX_ProcessPitchDataFlags
  ASL
  BPL SFX_ProcessPitchNoiseFlag
  LSR
  AND #$BF
  STA $2100,X
  TYA
  STA $2102,X
  DEC $2106,X
  DEC $2104,X
  LDA #$00
  STA AUDV0,X
  BEQ ServiceSFXVoiceDone
SFX_ProcessPitchNoiseFlag
  LSR
SFX_SetChannelPitch
  STA AUDF0,X
  TYA
  STA $2102,X
  LDY SFXChannel0Index,X
  LDA SFX_VolumeDataPtrLoLUT,Y
  STA $5C
  LDA SFX_VolumeDataPtrHiLUT,Y
  STA $5D
  LDY $2106,X
  LDA ($5C),Y
  BPL SFX_SetChannelVolume
  DEY
SFX_SetChannelVolume
  STA AUDV0,X
  TYA
  STA $2106,X
  LDY SFXChannel0Index,X
  LDA SFX_DistortionDataPtrLoLUT,Y
  STA $5C
  LDA SFX_DistortionDataPtrHiLUT,Y
  STA $5D
  LDY $2104,X
  LDA ($5C),Y
  BPL SFX_SetChannelDistortion
  DEY
SFX_SetChannelDistortion
  STA AUDC0,X
  TYA
  STA $2104,X
ServiceSFXVoiceDone
  DEX
  BMI ExitSFXServiceLoop
  JMP ServiceSFXLoop
ExitSFXServiceLoop
  RTS

SFX_FreqAndControlDataPtrLoLUT
 .byte <SFX_00_CoinDropAUDF
 .byte <SFX_01_FreeBirdAUDF
 .byte <SFX_02_PickupEggAUDF
 .byte <SFX_03_GameStartAUDF
 .byte <SFX_04_BirdPlatformBounceAUDF
 .byte <SFX_05_BirdBirdBounceAUDF
 .byte <SFX_06_PlayerFootstepAUDF
 .byte <SFX_07_PterryDefeatedAUDF
 .byte <SFX_08_PterryChargingAUDF
 .byte <SFX_09_PlayerBornAUDF
 .byte <SFX_0A_InvulnerableAUDF
 .byte <SFX_0B_EggWigglingAUDF
 .byte <SFX_0C_EnemyBirdBornAUDF
 .byte <SFX_0D_TrollJumpingAUDF
 .byte <SFX_0E_PlatformDissolveAUDF
 .byte <SFX_0F_SkidAUDF
 .byte <SFX_10_BirdExplosionAUDF
 .byte <SFX_11_CheepCheepAUDF
 .byte <SFX_12_PlayerBirdFlapAUDF

SFX_VolumeDataPtrLoLUT
 .byte <SFX_00_CoinDropAUDV
 .byte <SFX_01_FreeBirdAUDV
 .byte <SFX_02_PickupEggAUDV
 .byte <SFX_03_GameStartAUDV
 .byte <SFX_04_BirdPlatformBounceAUDV
 .byte <SFX_05_BirdBirdBounceAUDV
 .byte <SFX_06_PlayerFootstepAUDV
 .byte <SFX_07_PterryDefeatedAUDV
 .byte <SFX_08_PterryChargingAUDV
 .byte <SFX_09_PlayerBornAUDV
 .byte <SFX_0A_InvulnerableAUDV
 .byte <SFX_0B_EggWigglingAUDV
 .byte <SFX_0C_EnemyBirdBornAUDV
 .byte <SFX_0D_TrollJumpingAUDV
 .byte <SFX_0E_PlatformDissolveAUDV
 .byte <SFX_0F_SkidAUDV
 .byte <SFX_10_BirdExplosionAUDV
 .byte <SFX_11_CheepCheepAUDV
 .byte <SFX_12_PlayerBirdFlapAUDV

SFX_DistortionDataPtrLoLUT
 .byte <SFX_00_CoinDropAUDC
 .byte <SFX_01_FreeBirdAUDC
 .byte <SFX_02_PickupEggAUDC
 .byte <SFX_03_GameStartAUDC
 .byte <SFX_04_BirdPlatformBounceAUDC
 .byte <SFX_05_BirdBirdBounceAUDC
 .byte <SFX_06_PlayerFootstepAUDC
 .byte <SFX_07_PterryDefeatedAUDC
 .byte <SFX_08_PterryChargingAUDC
 .byte <SFX_09_PlayerBornAUDC
 .byte <SFX_0A_InvulnerableAUDC
 .byte <SFX_0B_EggWigglingAUDC
 .byte <SFX_0C_EnemyBirdBornAUDC
 .byte <SFX_0D_TrollJumpingAUDC
 .byte <SFX_0E_PlatformDissolveAUDC
 .byte <SFX_0F_SkidAUDC
 .byte <SFX_10_BirdExplosionAUDC
 .byte <SFX_11_CheepCheepAUDC
 .byte <SFX_12_PlayerBirdFlapAUDC

SFX_DistortionDataPtrHiLUT
 .byte >SFX_00_CoinDropAUDC
 .byte >SFX_01_FreeBirdAUDC
 .byte >SFX_02_PickupEggAUDC
 .byte >SFX_03_GameStartAUDC
 .byte >SFX_04_BirdPlatformBounceAUDC
 .byte >SFX_05_BirdBirdBounceAUDC
 .byte >SFX_06_PlayerFootstepAUDC
 .byte >SFX_07_PterryDefeatedAUDC
 .byte >SFX_08_PterryChargingAUDC
 .byte >SFX_09_PlayerBornAUDC
 .byte >SFX_0A_InvulnerableAUDC
 .byte >SFX_0B_EggWigglingAUDC
 .byte >SFX_0C_EnemyBirdBornAUDC
 .byte >SFX_0D_TrollJumpingAUDC
 .byte >SFX_0E_PlatformDissolveAUDC
 .byte >SFX_0F_SkidAUDC
 .byte >SFX_10_BirdExplosionAUDC
 .byte >SFX_11_CheepCheepAUDC
 .byte >SFX_12_PlayerBirdFlapAUDC

SFX_VolumeDataPtrHiLUT
 .byte >SFX_00_CoinDropAUDV
 .byte >SFX_01_FreeBirdAUDV
 .byte >SFX_02_PickupEggAUDV
 .byte >SFX_03_GameStartAUDV
 .byte >SFX_04_BirdPlatformBounceAUDV
 .byte >SFX_05_BirdBirdBounceAUDV
 .byte >SFX_06_PlayerFootstepAUDV
 .byte >SFX_07_PterryDefeatedAUDV
 .byte >SFX_08_PterryChargingAUDV
 .byte >SFX_09_PlayerBornAUDV
 .byte >SFX_0A_InvulnerableAUDV
 .byte >SFX_0B_EggWigglingAUDV
 .byte >SFX_0C_EnemyBirdBornAUDV
 .byte >SFX_0D_TrollJumpingAUDV
 .byte >SFX_0E_PlatformDissolveAUDV
 .byte >SFX_0F_SkidAUDV
 .byte >SFX_10_BirdExplosionAUDV
 .byte >SFX_11_CheepCheepAUDV
 .byte >SFX_12_PlayerBirdFlapAUDV

SFX_FreqAndControlDataPtrHiLUT
 .byte >SFX_00_CoinDropAUDF
 .byte >SFX_01_FreeBirdAUDF
 .byte >SFX_02_PickupEggAUDF
 .byte >SFX_03_GameStartAUDF
 .byte >SFX_04_BirdPlatformBounceAUDF
 .byte >SFX_05_BirdBirdBounceAUDF
 .byte >SFX_06_PlayerFootstepAUDF
 .byte >SFX_07_PterryDefeatedAUDF
 .byte >SFX_08_PterryChargingAUDF
 .byte >SFX_09_PlayerBornAUDF
 .byte >SFX_0A_InvulnerableAUDF
 .byte >SFX_0B_EggWigglingAUDF
 .byte >SFX_0C_EnemyBirdBornAUDF
 .byte >SFX_0D_TrollJumpingAUDF
 .byte >SFX_0E_PlatformDissolveAUDF
 .byte >SFX_0F_SkidAUDF
 .byte >SFX_10_BirdExplosionAUDF
 .byte >SFX_11_CheepCheepAUDF
 .byte >SFX_12_PlayerBirdFlapAUDF

SFX_SampleDurationLUT
 .byte $02, $01, $02, $01, $01, $01, $01, $04
 .byte $02, $04, $07, $02, $04, $02, $0E, $01
 .byte $02, $01, $01 

SFXInterruptGroups
 .byte $00  ; 00:?coin drop sfx?
 .byte $09  ; 01:free bird notification sfx
 .byte $05  ; 02:pickup egg sfx
 .byte $09  ; 03:game start sfx
 .byte $02  ; 04:bird bounced off platform sfx
 .byte $04  ; 05:bird bounced off bird sfx
 .byte $02  ; 06:player footstep sfx
 .byte $08  ; 07:pterry defeated sfx
 .byte $04  ; 08:pterry charging sfx
 .byte $05  ; 09:player born on platform sfx
 .byte $05  ; 0A:player invulnerable sfx
 .byte $00  ; 0B:egg wiggling sfx
 .byte $03  ; 0C:enemy bird born sfx
 .byte $04  ; 0D:troll jumping sfx
 .byte $06  ; 0E:platform dissolve sfx
 .byte $03  ; 0F:skid sfx
 .byte $07  ; 10:bird died explosion sfx
 .byte $00  ; 11:cheep cheep sfx
 .byte $02  ; 12:player bird flap sfx

SFX_00_CoinDropAUDF
 .byte $0B 
 .byte $0B 
 .byte $11 
 .byte $11 
 .byte $0F 
 .byte $0F 
 .byte $17 
 .byte $17 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $0B 
 .byte $0B 
 .byte $11 
 .byte $11 
 .byte $0B 
 .byte $0B 
 .byte $11 
 .byte $11 
 .byte $0F 
 .byte $0F 
 .byte $17 
 .byte $17 
 .byte $00 
 .byte $00 
 .byte $FF 
SFX_00_CoinDropAUDV
 .byte $04 
 .byte $00 
 .byte $03 
 .byte $00 
 .byte $02 
 .byte $00 
 .byte $01 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $04 
 .byte $00 
 .byte $03 
 .byte $00 
 .byte $04 
 .byte $00 
 .byte $03 
 .byte $00 
 .byte $02 
 .byte $00 
 .byte $01 
 .byte $00 
 .byte $00 
 .byte $00 
SFX_00_CoinDropAUDC
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $0D 
 .byte $0D 
 .byte $0D 
 .byte $0D 
 .byte $0D 
 .byte $0D 
 .byte $0D 
 .byte $0D 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $8D 

SFX_01_FreeBirdAUDF
 .byte $0C 
 .byte $0E 
 .byte $11 
 .byte $16 
 .byte $0C 
 .byte $0E 
 .byte $11 
 .byte $16 
 .byte $0C 
 .byte $0E 
 .byte $11 
 .byte $16 
 .byte $0C 
 .byte $0E 
 .byte $11 
 .byte $16 
 .byte $0C 
 .byte $0E 
 .byte $11 
 .byte $16 
 .byte $0C 
 .byte $0E 
 .byte $11 
 .byte $16 
 .byte $FF 
SFX_01_FreeBirdAUDV
 .byte $0B 
 .byte $0B 
 .byte $0B 
 .byte $0B 
 .byte $09 
 .byte $09 
 .byte $09 
 .byte $09 
 .byte $07 
 .byte $07 
 .byte $07 
 .byte $07 
 .byte $06 
 .byte $06 
 .byte $06 
 .byte $06 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $02 
 .byte $02 
 .byte $02 
 .byte $02 

SFX_02_PickupEggAUDF
 .byte $18 
 .byte $16 
 .byte $14 
 .byte $11 
 .byte $0F 
 .byte $0D 
 .byte $0B 
 .byte $FF 
SFX_02_PickupEggAUDV
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $09 
 .byte $07 
 .byte $06 
 .byte $05 

SFX_03_GameStartAUDF
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $FF 
SFX_03_GameStartAUDV
 .byte $0F 
 .byte $0F 
 .byte $0A 
 .byte $09 
 .byte $07 
 .byte $06 
 .byte $05 
 .byte $0E 
 .byte $0E 
 .byte $0A 
 .byte $09 
 .byte $07 
 .byte $06 
 .byte $05 
 .byte $0C 
 .byte $0C 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $06 
 .byte $05 
 .byte $0B 
 .byte $0B 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $06 
 .byte $05 
 .byte $0A 
 .byte $0A 
 .byte $08 
 .byte $07 
 .byte $06 
 .byte $05 
 .byte $04 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $06 
 .byte $05 
 .byte $04 
 .byte $03 
 .byte $08 
 .byte $06 
 .byte $05 
 .byte $04 
 .byte $03 
 .byte $02 
 .byte $02 
 .byte $07 
 .byte $06 
 .byte $05 
 .byte $04 
 .byte $03 
 .byte $02 
 .byte $02 
 .byte $06 
 .byte $05 
 .byte $04 
 .byte $03 
 .byte $03 
 .byte $02 
 .byte $02 
 .byte $05 
 .byte $05 
 .byte $04 
 .byte $04 
 .byte $03 
 .byte $02 
 .byte $02 
 .byte $04 
 .byte $04 
 .byte $03 
 .byte $03 
 .byte $02 
 .byte $02 
 .byte $01 
 .byte $03 
 .byte $03 
 .byte $03 
 .byte $02 
 .byte $02 
 .byte $02 
 .byte $02 

SFX_04_BirdPlatformBounceAUDF
 .byte $0F 
 .byte $10 
 .byte $11 
 .byte $12 
 .byte $1F 
 .byte $FF 

SFX_05_BirdBirdBounceAUDF
 .byte $0F 
 .byte $10 
 .byte $11 
 .byte $12 
 .byte $0F 
 .byte $10 
 .byte $11 
 .byte $12 
 .byte $0F 
 .byte $10 
 .byte $11 
 .byte $12 
 .byte $0F 
 .byte $10 
 .byte $11 
 .byte $12 
 .byte $FF 
SFX_05_BirdBirdBounceAUDV
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $06 
 .byte $06 
 .byte $06 
 .byte $06 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $02 
 .byte $02 
 .byte $02 
 .byte $02 

SFX_06_PlayerFootstepAUDF
 .byte $04 
 .byte $FF 

SFX_07_PterryDefeatedAUDF
 .byte $0F 
 .byte $14 
 .byte $18 
 .byte $1F 
 .byte $0F 
 .byte $14 
 .byte $18 
 .byte $1F 
 .byte $0F 
 .byte $14 
 .byte $18 
 .byte $1F 
 .byte $0F 
 .byte $14 
 .byte $18 
 .byte $1F 
 .byte $18 
 .byte $1A 
 .byte $1C 
 .byte $1E 
 .byte $1F 
 .byte $FF 

SFX_08_PterryChargingAUDF
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $07 
 .byte $08 
 .byte $09 
 .byte $0A 
 .byte $0B 
 .byte $0C 
 .byte $0D 
 .byte $0E 
 .byte $0F 
 .byte $FF 
SFX_08_PterryChargingAUDV
 .byte $0A 
 .byte $0A 
 .byte $0A 
 .byte $0A 
 .byte $0A 
 .byte $0A 
 .byte $0A 
 .byte $0A 
 .byte $09 
 .byte $09 
 .byte $09 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $06 
 .byte $05 
 .byte $04 

SFX_09_PlayerBornAUDF
 .byte $1F 
 .byte $12 
 .byte $00 
 .byte $1E 
 .byte $11 
 .byte $00 
 .byte $1D 
 .byte $10 
 .byte $FF 
SFX_09_PlayerBornAUDV
 .byte $08 
 .byte $08 
 .byte $00 
 .byte $08 
 .byte $08 
 .byte $00 
 .byte $08 
 .byte $08 

SFX_0A_InvulnerableAUDF
 .byte $1F 
 .byte $1F 
 .byte $1E 
 .byte $1E 
 .byte $1D 
 .byte $1C 
 .byte $1B 
 .byte $1A 
 .byte $19 
 .byte $18 
 .byte $17 
 .byte $16 
 .byte $15 
 .byte $14 
 .byte $13 
 .byte $12 
 .byte $11 
 .byte $10 
 .byte $0F 
 .byte $0E 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $FF 
SFX_0A_InvulnerableAUDV
 .byte $0A 
 .byte $0A 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $08 
 .byte $07 
 .byte $06 
 .byte $05 
 .byte $04 
 .byte $03 
 .byte $02 
SFX_0B_EggWigglingAUDF
 .byte $0D 
 .byte $0B 
 .byte $09 
 .byte $00 
 .byte $0D 
 .byte $0B 
 .byte $09 
 .byte $00 
 .byte $0D 
 .byte $0B 
 .byte $09 
 .byte $FF 
SFX_0B_EggWigglingAUDV
 .byte $06 
 .byte $07 
 .byte $08 
 .byte $00 
 .byte $06 
 .byte $06 
 .byte $06 
 .byte $00 
 .byte $04 
 .byte $04 
 .byte $04 

SFX_0C_EnemyBirdBornAUDF
 .byte $1F 
 .byte $1F 
 .byte $1E 
 .byte $1D 
 .byte $1C 
 .byte $1B 
 .byte $1A 
 .byte $19 
 .byte $18 
 .byte $17 
 .byte $16 
 .byte $15 
 .byte $14 
 .byte $13 
 .byte $12 
 .byte $11 
 .byte $FF 
SFX_0C_EnemyBirdBornAUDV
 .byte $0A 
 .byte $0A 
 .byte $07 
 .byte $07 
 .byte $88 

SFX_0D_TrollJumpingAUDF
 .byte $03 
 .byte $03 
 .byte $03 
 .byte $03 
 .byte $FF 
SFX_0D_TrollJumpingAUDV
 .byte $08 
 .byte $07 
 .byte $06 
 .byte $05 

SFX_0E_PlatformDissolveAUDF
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $FF 
SFX_0E_PlatformDissolveAUDV
 .byte $04 
 .byte $05 
 .byte $06 
 .byte $07 
 .byte $07 
 .byte $07 
 .byte $05 
 .byte $04 
 .byte $03 
 .byte $01 

SFX_0F_SkidAUDF
 .byte $14 
 .byte $14 
 .byte $14 
 .byte $14 
 .byte $16 
 .byte $14 
 .byte $17 
 .byte $15 
 .byte $16 
 .byte $14 
 .byte $17 
 .byte $15 
 .byte $17 
 .byte $14 
 .byte $15 
 .byte $14 
 .byte $14 
 .byte $16 
 .byte $14 
 .byte $15 
 .byte $17 
 .byte $14 
 .byte $17 
 .byte $15 
 .byte $16 
 .byte $14 
 .byte $17 
 .byte $15 
 .byte $17 
 .byte $14 
 .byte $15 
 .byte $14 
 .byte $FE 

SFX_10_BirdExplosionAUDF
 .byte $0F 
 .byte $11 
 .byte $13 
 .byte $15 
 .byte $17 
 .byte $19 
 .byte $1B 
 .byte $1D 
 .byte $1E 
 .byte $1F 
 .byte $FF 
SFX_10_BirdExplosionAUDV
 .byte $09 
 .byte $09 
 .byte $08 
 .byte $07 
 .byte $06 
 .byte $05 
 .byte $04 
 .byte $03 
 .byte $02 
 .byte $01 

SFX_11_CheepCheepAUDF
 .byte $0F 
 .byte $0E 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $00 
 .byte $0F 
 .byte $0E 
 .byte $0D 
 .byte $0C 
 .byte $0B 
 .byte $0A 
 .byte $09 
 .byte $08 
 .byte $00 
 .byte $00 
 .byte $FF 

SFX_12_PlayerBirdFlapAUDF
 .byte $18 
 .byte $18 
 .byte $18 
 .byte $18 
 .byte $18 
 .byte $18 
 .byte $18 
 .byte $18 
 .byte $18 
 .byte $18 
 .byte $FF 
SFX_12_PlayerBirdFlapAUDV
 .byte $03 
 .byte $03 
 .byte $03 
 .byte $06 
 .byte $06 
 .byte $06 
 .byte $05 
 .byte $05 
 .byte $05 
 .byte $02 
SFX_07_PterryDefeatedAUDC
SFX_0B_EggWigglingAUDC
SFX_0D_TrollJumpingAUDC
SFX_0E_PlatformDissolveAUDC
SFX_10_BirdExplosionAUDC
SFX_12_PlayerBirdFlapAUDC
 .byte $88 
SFX_01_FreeBirdAUDC
SFX_03_GameStartAUDC
SFX_04_BirdPlatformBounceAUDC
SFX_05_BirdBirdBounceAUDC
SFX_0F_SkidAUDC
SFX_11_CheepCheepAUDC
 .byte $84 
SFX_08_PterryChargingAUDC
SFX_0A_InvulnerableAUDC
 .byte $87 
SFX_02_PickupEggAUDC
SFX_09_PlayerBornAUDC
 .byte $8D 
SFX_06_PlayerFootstepAUDC
 .byte $86 
SFX_0C_EnemyBirdBornAUDC
 .byte $83 
SFX_06_PlayerFootstepAUDV
SFX_0F_SkidAUDV
 .byte $88 
SFX_04_BirdPlatformBounceAUDV
 .byte $84 
SFX_07_PterryDefeatedAUDV
 .byte $8A 
SFX_11_CheepCheepAUDV
 .byte $82 

DoHscStatusCheck
  JSR InitForHsc
  LDA #$00
  STA $250E
  JSR $3FF7      ; HSCSTAT check status of HSC and some cart init
  RTS

DoHscAttractMode
  JSR InitForHsc
  JSR $3FFA      ; HSCATRCT - attract mode score display

RestoreCharbaseAndExit
  LDA #$C8
  STA CHARBASE
  LDA #$00
  STA NMIModeFlag
  RTS

DoHscScoreEntry
  LDA Difficulty
  STA Temp62                 ; save difficulty
  JSR InitForHsc
  LDA PlayerCount
  BEQ HscScoreEntryHandlePlayer1
  LDA CurrentPlayer
  BNE HscScoreEntryHandlePlayer1
  STA $250E
HscScoreEntryHandlePlayer1
  JSR $3FFD                  ; HSC score entry routine
  JMP RestoreCharbaseAndExit

InitForHsc
  LDA $3900                  ; Check two bytes of the HSC rom to ensure it's
  CMP #$C6                   ; plugged in.
  BNE AbortUsingHSC
  LDA $3904
  CMP #$FE
  BNE AbortUsingHSC

  LDX #$0F
CopyArgsToRamLoop
  LDA HscLUTArgumentBlock-1,X
  STA $24FF,X
  DEX
  BNE CopyArgsToRamLoop

  LDX #$28
CopyLogoDls
  LDA HscGameLogoDisplayLists-1,X
  STA $18B3,X
  DEX
  BNE CopyLogoDls

  LDA #$01
  STA NMIModeFlag
  JSR SetDemoPalette

; update the DL text string for current player...
  LDA Temp62
  TAX
  ASL
  ASL
  ORA CurrentPlayer
  STA $2502

; ...and update the DL text string for the difficulty name...
  LDA DifficultyTextLo,X
  STA $2506
  LDA DifficultyTextHi,X
  STA $2507

  LDA CurrentPlayer
  BEQ CopyP0ScoreToHscRam

CopyP1ScoreToHscRam
  LDA #$00
  STA $27AF
  LDX #$03
CopyP1ScoreLoop
  LDA P1Score-1,X
  STA $27AF,X
  DEX
  BNE CopyP1ScoreLoop
  JMP ReturnFromHSCInit

CopyP0ScoreToHscRam
  LDX #$03
CopyP0ScoreLoop
  LDA P0Score-1,X
  STA $27AF,X
  DEX
  BNE CopyP0ScoreLoop
ReturnFromHSCInit
  LDX #$00
  LDY #$25
  RTS

AbortUsingHSC
  PLA
  PLA
  RTS

HscLUTArgumentBlock  ; we copy this to ram for actual use
 .byte $8B,$23       ; Joust's HSC Game ID
 .byte $00           ; Game Difficulty
 .byte $00           ; Game Controller
 .byte $00,$00       ; Game Name (Lo,Hi)
 .byte $C0,$D9       ; Difficulty Name (Lo,Hi)
 .byte $AF,$27       ; Game Score (Lo,Hi)
 .byte <HscGameLogoDisplayListList, >HscGameLogoDisplayListList
 .byte $70,$D9       ; Sound Routine (Lo,Hi)
 .byte $04           ; HS Display Time

HscGameLogoDisplayListList
 .byte $8F,$27,$CB
 .byte $08,$27,$CB
 .byte $07,$18,$B4
 .byte $07,$18,$BB
 .byte $07,$18,$C2
 .byte $07,$18,$C9
 .byte $07,$18,$D0

HscGameLogoDisplayLists

 .byte $CD,$40,$C0,$F2,$3C
 .byte $00,$00

 .byte $DB,$40,$C0,$EF,$30
 .byte $00,$00

 .byte $DD,$40,$C8,$F1,$30
 .byte $00,$00

 .byte $EC,$40,$C8,$EF,$2C
 .byte $00,$00

 .byte $EC,$40,$C0,$EF,$2C
 .byte $EC,$80,$C0,$FF,$A0
 .byte $00,$00

              ; "BEGINNER"
  WATKINS "^"  ; space $D9C0
  WATKINS "^"  ; space $D9C1
  WATKINS "^"  ; space $D9C2
  WATKINS "^"  ; space $D9C3
  WATKINS "^"  ; space $D9C4
  WATKINS "^"  ; space $D9C5
  WATKINS "^"  ; space $D9C6
  WATKINS "^"  ; space $D9C7
  WATKINS "^"  ; space $D9C8
  WATKINS "B"  ; $D9C9
  WATKINS "E"  ; $D9CA
  WATKINS "G"  ; $D9CB
  WATKINS "I"  ; $D9CC
  WATKINS "N"  ; $D9CD
  WATKINS "N"  ; $D9CE
  WATKINS "E"  ; $D9CF
  WATKINS "R"  ; $D9D0
  WATKINS "^"  ; space $D9D1
  WATKINS "^"  ; space $D9D2
  WATKINS "^"  ; space $D9D3

              ; "INTERMEDIATE"
  WATKINS "^"  ; space $D9D4
  WATKINS "^"  ; space $D9D5
  WATKINS "^"  ; space $D9D6
  WATKINS "^"  ; space $D9D7
  WATKINS "^"  ; space $D9D8
  WATKINS "^"  ; space $D9D9
  WATKINS "I"  ; $D9DA
  WATKINS "N"  ; $D9DB
  WATKINS "T"  ; $D9DC
  WATKINS "E"  ; $D9DD
  WATKINS "R"  ; $D9DE
  WATKINS "M"  ; $D9DF
  WATKINS "E"  ; $D9E0
  WATKINS "D"  ; $D9E1
  WATKINS "I"  ; $D9E2
  WATKINS "A"  ; $D9E3
  WATKINS "T"  ; $D9E4
  WATKINS "E"  ; $D9E5

              ; "ADVANCED"
  WATKINS "^"  ; space $D9E6
  WATKINS "^"  ; space $D9E7
  WATKINS "^"  ; space $D9E8
  WATKINS "^"  ; space $D9E9
  WATKINS "^"  ; space $D9EA
  WATKINS "^"  ; space $D9EB
  WATKINS "^"  ; space $D9EC
  WATKINS "^"  ; space $D9ED
  WATKINS "^"  ; space $D9EE
  WATKINS "A"  ; $D9EF
  WATKINS "D"  ; $D9F0
  WATKINS "V"  ; $D9F1
  WATKINS "A"  ; $D9F2
  WATKINS "N"  ; $D9F3
  WATKINS "C"  ; $D9F4
  WATKINS "E"  ; $D9F5
  WATKINS "D"  ; $D9F6

              ; "EXPERT"
  WATKINS "^"  ; space $D9F7
  WATKINS "^"  ; space $D9F8
  WATKINS "^"  ; space $D9F9
  WATKINS "^"  ; space $D9FA
  WATKINS "^"  ; space $D9FB
  WATKINS "^"  ; space $D9FC
  WATKINS "^"  ; space $D9FD
  WATKINS "^"  ; space $D9FE
  WATKINS "^"  ; space $D9FF
  WATKINS "^"  ; space $DA00
  WATKINS "E"  ; $DA01
  WATKINS "X"  ; $DA02
  WATKINS "P"  ; $DA03
  WATKINS "E"  ; $DA04
  WATKINS "R"  ; $DA05
  WATKINS "T"  ; $DA06
  WATKINS "^"  ; space $DA07
  WATKINS "^"  ; space $DA08
  WATKINS "^"  ; space $DA09
  WATKINS "^"  ; space $DA0A
  WATKINS "^"  ; space $DA0B
  WATKINS "^"  ; space $DA0C
  WATKINS "^"  ; space $DA0D
  WATKINS "^"  ; space $DA0E
  WATKINS "^"  ; space $DA0F
  WATKINS "^"  ; space $DA10

; pointers to previous HSC/Watkins encoded text strings

DifficultyTextLo
 .byte $C0  ; BEGINNER text pointer lo
 .byte $D4  ; INTERMEDIATE text pointer lo
 .byte $E6  ; ADVANCED text pointer lo
 .byte $F7  ; EXPERT text pointer lo

DifficultyTextHi
 .byte $D9  ; BEGINNER text pointer hi
 .byte $D9  ; INTERMEDIATE text pointer hi
 .byte $D9  ; ADVANCED text pointer hi
 .byte $D9  ; EXPERT text pointer hi

TitlePalette
 .byte $00  ; P0C1
 .byte $00  ; P0C2
 .byte $00  ; P0C3

 .byte $FA  ; P1C1
 .byte $FF  ; P1C2
 .byte $CF  ; P1C3

 .byte $0D  ; P2C1
 .byte $0D  ; P2C2
 .byte $0D  ; P2C3

 .byte $0D  ; P3C1
 .byte $0D  ; P3C2
 .byte $0D  ; P3C3

 .byte $0D  ; P4C1
 .byte $0D  ; P4C2
 .byte $0D  ; P4C3

 .byte $0D  ; P5C1
 .byte $0D  ; P5C2
 .byte $0D  ; P5C3

 .byte $0D  ; P6C1
 .byte $0D  ; P6C2
 .byte $0D  ; P6C3

 .byte $00  ; P7C1
 .byte $00  ; P7C2
 .byte $00  ; P7C3

GamePalette
 .byte $1A  ; P0C1
 .byte $10  ; P0C2
 .byte $14  ; P0C3

 .byte $00  ; P1C1
 .byte $29  ; P1C2
 .byte $32  ; P1C3

 .byte $46  ; P2C1
 .byte $09  ; P2C2
 .byte $84  ; P2C3

 .byte $1E  ; P3C1
 .byte $BC  ; P3C2
 .byte $00  ; P3C3

 .byte $39  ; P4C1
 .byte $0D  ; P4C2
 .byte $33  ; P4C3

 .byte $15  ; P5C1
 .byte $A9  ; P5C2
 .byte $85  ; P5C3

 .byte $0D  ; P6C1
 .byte $1C  ; P6C2
 .byte $44  ; P6C3

 .byte $16  ; P7C1
 .byte $E3  ; P7C2
 .byte $E8  ; P7C3

TitleScrCharacterObjectLoLUT
 .byte <$18FE
 .byte <$1958
 .byte <$1C2A
 .byte <$18FA
 .byte <$1954
 .byte <$1950 
 .byte <$194C 
 .byte <$1C26 
 .byte <$1C22 
 .byte <$1C1E 
 .byte <$1D93 
 .byte <$1DED 
 .byte <$1E48 

TitleScrCharacterObjectHiLUT
 .byte >$18FE 
 .byte >$1958 
 .byte >$1C2A 
 .byte >$18FA 
 .byte >$1954 
 .byte >$1950 
 .byte >$194C 
 .byte >$1C26 
 .byte >$1C22 
 .byte >$1C1E 
 .byte >$1D93 
 .byte >$1DED 
 .byte >$1E48 

TitleScrSpriteObjectLoLUT
 .byte <$C05E 
 .byte <$C067 
 .byte <$C082 
 .byte <$C064 
 .byte <$C068 
 .byte <$C072 
 .byte <$C07C 
 .byte <$C08B 
 .byte <$C095 
 .byte <$C09F 
 .byte <$C0A5 
 .byte <$C0B2 
 .byte <$C0BF 

TitleScrSpriteObjectHiLUT
 .byte >$C05E 
 .byte >$C067 
 .byte >$C082 
 .byte >$C064 
 .byte >$C068 
 .byte >$C072 
 .byte >$C07C 
 .byte >$C08B 
 .byte >$C095 
 .byte >$C09F 
 .byte >$C0A5 
 .byte >$C0B2 
 .byte >$C0BF 

TitleScrDLAddrLoLUT
 .byte <$1EA1, <$1F55, <$1EFB, <$1FC1, <$1FC6, <$184E, <$1853, <$1FD7 
 .byte <$1FDC, <$18AD, <$18A8, <$1907, <$1902, <$1961, <$195C, <$19BB 
 .byte <$19B6, <$1A16, <$1A11, <$1A70, <$1A6B, <$1ACA, <$1AC5, <$1B25
 .byte <$1B20, <$1B7F, <$1B7A, <$1BD9, <$1BD4, <$1C33, <$1C2E, <$1C8D
 .byte <$1C88, <$1CE7, <$1CE2, <$1D42, <$1D3D, <$1D9C, <$1D97, <$1DF6
 .byte <$1DF1, <$1E51, <$1E4C, <$1EAB, <$1EA6, <$1F05, <$1F00, <$1F5F
 .byte <$1F5A, <$1FBA, <$1FB5, <$1FCB, <$1FD0, <$19B1, <$19AC, <$19A7
 .byte <$19A2, <$1A0C, <$1A07, <$1A02, <$19FD, <$1A66, <$1A61, <$1A5C 
 .byte <$1A57, <$1AC0, <$1ABB, <$1AB6, <$1AB1, <$1B1B, <$1B16, <$1B11
 .byte <$1B0C, <$1B75, <$1B70, <$1B6B, <$1B66, <$1BCF, <$1BCA, <$1BC5
 .byte <$1BC0 

TitleScrDLAddrHiLUT
 .byte >$1EA1, >$1F55, >$1EFB, >$1FC1, >$1FC6, >$184E, >$1853, >$1FD7 
 .byte >$1FDC, >$18AD, >$18A8, >$1907, >$1902, >$1961, >$195C, >$19BB 
 .byte >$19B6, >$1A16, >$1A11, >$1A70, >$1A6B, >$1ACA, >$1AC5, >$1B25
 .byte >$1B20, >$1B7F, >$1B7A, >$1BD9, >$1BD4, >$1C33, >$1C2E, >$1C8D
 .byte >$1C88, >$1CE7, >$1CE2, >$1D42, >$1D3D, >$1D9C, >$1D97, >$1DF6
 .byte >$1DF1, >$1E51, >$1E4C, >$1EAB, >$1EA6, >$1F05, >$1F00, >$1F5F
 .byte >$1F5A, >$1FBA, >$1FB5, >$1FCB, >$1FD0, >$19B1, >$19AC, >$19A7
 .byte >$19A2, >$1A0C, >$1A07, >$1A02, >$19FD, >$1A66, >$1A61, >$1A5C 
 .byte >$1A57, >$1AC0, >$1ABB, >$1AB6, >$1AB1, >$1B1B, >$1B16, >$1B11
 .byte >$1B0C, >$1B75, >$1B70, >$1B6B, >$1B66, >$1BCF, >$1BCA, >$1BC5
 .byte >$1BC0 

TitleScrCharObject_LoAddress
 .byte <$2685, <$2699, <$26AD, <$2244, <$2251, <$2204, <$2204, <$2204
 .byte <$2204, <$2200, <$2202, <$2200, <$2202, <$2200, <$2202, <$2200
 .byte <$2202, <$2200, <$2202, <$2200, <$2202, <$2200, <$2202, <$2200
 .byte <$2202, <$2200, <$2202, <$2200, <$2202, <$2200, <$2202, <$2200
 .byte <$2202, <$2200, <$2202, <$2200, <$2202, <$2200, <$2202, <$2200
 .byte <$2202, <$2200, <$2202, <$2200, <$2202, <$2200, <$2202, <$2200
 .byte <$2202, <$2200, <$2202, <$2200, <$2202, <$2261, <$2268, <$2272
 .byte <$227C, <$2281, <$2289, <$2293, <$229D, <$22A3, <$22AB, <$22B5
 .byte <$22BF, <$22C3, <$22C9, <$22D3, <$22DD, <$22DF, <$22E9, <$22F3
 .byte <$22FD, <$2302, <$230C, <$2316, <$2320, <$2325, <$232F, <$2339
 .byte <$2343

TitleScrCharObject_HiAddress
 .byte >$2685, >$2699, >$26AD, >$2244, >$2251, >$2204, >$2204, >$2204
 .byte >$2204, >$2200, >$2202, >$2200, >$2202, >$2200, >$2202, >$2200
 .byte >$2202, >$2200, >$2202, >$2200, >$2202, >$2200, >$2202, >$2200
 .byte >$2202, >$2200, >$2202, >$2200, >$2202, >$2200, >$2202, >$2200
 .byte >$2202, >$2200, >$2202, >$2200, >$2202, >$2200, >$2202, >$2200
 .byte >$2202, >$2200, >$2202, >$2200, >$2202, >$2200, >$2202, >$2200
 .byte >$2202, >$2200, >$2202, >$2200, >$2202, >$2261, >$2268, >$2272
 .byte >$227C, >$2281, >$2289, >$2293, >$229D, >$22A3, >$22AB, >$22B5
 .byte >$22BF, >$22C3, >$22C9, >$22D3, >$22DD, >$22DF, >$22E9, >$22F3
 .byte >$22FD, >$2302, >$230C, >$2316, >$2320, >$2325, >$232F, >$2339
 .byte >$2343

TitleScrCharObject_PalleteWidth
 .byte $90, $90, $90, $93, $90, $00, $08, $00
 .byte $08, $1E, $1E, $1E, $1E, $1E, $1E, $1E
 .byte $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E
 .byte $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E
 .byte $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E
 .byte $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E
 .byte $1E, $1E, $1E, $1E, $1E, $F9, $F6, $F6
 .byte $FB, $F8, $F6, $F6, $FA, $F8, $F6, $F6
 .byte $FC, $FA, $F6, $F6, $FE, $F6, $F6, $F6
 .byte $FB, $F6, $F6, $F6, $FB, $F6, $F6, $F6
 .byte $FB

TitleScrCharObject_XCoordinate
 .byte $A0, $A0, $A0, $10, $50, $00, $80, $00
 .byte $80, $00, $9A, $00, $9A, $00, $9A, $00
 .byte $9A, $00, $9A, $00, $9A, $00, $9A, $00
 .byte $9A, $00, $9A, $00, $9A, $00, $9A, $00
 .byte $9A, $00, $9A, $00, $9A, $00, $9A, $00
 .byte $9A, $00, $9A, $00, $9A, $00, $9A, $00
 .byte $9A, $00, $9A, $00, $9A, $17, $31, $57
 .byte $7D, $13, $31, $57, $7D, $13, $31, $57
 .byte $7D, $1B, $31, $57, $7D, $0B, $31, $57
 .byte $7D, $0B, $31, $57, $7D, $0B, $31, $57
 .byte $7D

DllLUT
 .byte $0F,$27,$CB
 .byte $08,$27,$CB
 .byte $47,$18,$4E
 .byte $47,$18,$A8
 .byte $47,$18,$A8
 .byte $47,$18,$A8
 .byte $47,$18,$FA
 .byte $47,$19,$4C
 .byte $47,$19,$A2
 .byte $47,$19,$FD
 .byte $47,$1A,$57
 .byte $47,$1A,$B1
 .byte $47,$1B,$0C
 .byte $47,$1B,$66
 .byte $47,$1B,$C0
 .byte $47,$1C,$1E
 .byte $47,$1D,$3D
 .byte $47,$1D,$93
 .byte $47,$1D,$ED
 .byte $47,$1E,$48
 .byte $47,$1E,$A1
 .byte $47,$1E,$FB
 .byte $47,$1F,$55
 .byte $47,$1F,$B5
 .byte $47,$1F,$C1
 .byte $47,$1F,$D7
 .byte $0F,$27,$CB
 .byte $06,$27,$CB
 .byte $8F,$27,$CB

GameDLStartLutLo
 .byte <$1A17, <$1A13, <$1A6D, <$1A71, <$1C30, <$1C34, <$1C8E, <$1C82 
 .byte <$1C86, <$1C8A, <$1D43, <$1F58, <$1F54, <$1F5C, <$1F60, <$1F50
 .byte <$1F4C, <$1FAE, <$1FAA, <$1FB6, <$1FB2, <$1FA6, <$1FA2, <$1FC1
 .byte <$1FC5, <$1FD1, <$1FC9, <$1FCD, <$1FD7, <$1FDB, <$1FE7, <$1FDF
 .byte <$1FE3

GameDLStartLutHi
 .byte >$1A17, >$1A13, >$1A6D, >$1A71, >$1C30, >$1C34, >$1C8E, >$1C82 
 .byte >$1C86, >$1C8A, >$1D43, >$1F58, >$1F54, >$1F5C, >$1F60, >$1F50
 .byte >$1F4C, >$1FAE, >$1FAA, >$1FB6, >$1FB2, >$1FA6, >$1FA2, >$1FC1
 .byte >$1FC5, >$1FD1, >$1FC9, >$1FCD, >$1FD7, >$1FDB, >$1FE7, >$1FDF
 .byte >$1FE3

BackgroundSpriteGfxLoLUT
 .byte <$A8A4, <$A0FB, <$A8AA, <$A8BA, <$A897, <$A8BA, <$A8A0, <$C008 
 .byte <$C011, <$A8BA, <$C017, <$C000, <$C000, <$A08E, <$A8BA, <$C020 
 .byte <$C020, <$AF8E, <$AF8E, <$A0A9, <$A8BE, <$C03C, <$C03C, <$AF8E 
 .byte <$AF8E, <$A0C4, <$A8C0, <$A8C2, <$AF8E, <$AF8E, <$A0DE, <$A8C0 
 .byte <$A8C2 

BackgroundSpriteGfxHiLUT
 .byte >$A8A4, >$A0FB, >$A8AA, >$A8BA, >$A897, >$A8BA, >$A8A0, >$C008 
 .byte >$C011, >$A8BA, >$C017, >$C000, >$C000, >$A08E, >$A8BA, >$C020 
 .byte >$C020, >$AF8E, >$AF8E, >$A0A9, >$A8BE, >$C03C, >$C03C, >$AF8E 
 .byte >$AF8E, >$A0C4, >$A8C0, >$A8C2, >$AF8E, >$AF8E, >$A0DE, >$A8C0 
 .byte >$A8C2 

BackgroundSpritePaletteWidthLUT
 .byte $1A, $1B, $14, $5C, $17, $5C, $1C, $17 
 .byte $1A, $5C, $17, $18, $18, $05, $5C, $3E 
 .byte $3E, $37, $37, $05, $3F, $3E, $3E, $37
 .byte $37, $06, $3F, $3F, $37, $37, $03, $3F
 .byte $3F 

BackgroundSpriteXCoordLUT
 .byte $88, $00, $2E, $38, $6D, $7A, $7C, $00
 .byte $8B, $05, $37, $FD, $82, $19, $3B, $F7
 .byte $A2, $A0, $A0, $1B, $B0, $F7, $A2, $A0
 .byte $A0, $1B, $B0, $B0, $F9, $84, $15, $B0
 .byte $B0

HUDCharDLAddrLoLUT
 .byte <$19BB 
 .byte <$1ACA 
 .byte <$1B7F 
 .byte <$1DF6 
 .byte <$1FBA 
 .byte <$1FEB 

HUDCharDLAddrHiLUT
 .byte >$19BB 
 .byte >$1ACA 
 .byte >$1B7F 
 .byte >$1DF6 
 .byte >$1FBA 
 .byte >$1FEB 

MsgAndHUDCharObjectStringLoLUT
 .byte <$2671 
 .byte <$2685 
 .byte <$2699 
 .byte <$26AD 
 .byte <$2659 
 .byte <$2200 

MsgAndHUDCharObjectStringHiLUT
 .byte >$2671 
 .byte >$2685 
 .byte >$2699 
 .byte >$26AD 
 .byte >$2659 
 .byte >$2200 

MsgAndHUDCharObjectPaletteAndWidthLUT
 .byte $90 
 .byte $90 
 .byte $90 
 .byte $90 
 .byte $68 
 .byte $92 

MsgAndHUDCharObjectXLUT
 .byte $2E 
 .byte $2E 
 .byte $2E 
 .byte $2E 
 .byte $20 
 .byte $A8 

DisplayListLoLUT
 .byte <$1858, <$18B2, <$190C, <$1966, <$19BB, <$1A13, <$1A6D, <$1ACA
 .byte <$1B2A, <$1B7F, <$1BDE, <$1C30, <$1C82, <$1CEC, <$1D43, <$1DA1
 .byte <$1DF6, <$1E56, <$1EB0, <$1F0A, <$1F4C, <$1FA2, <$1FC1, <$1FD7 
DisplayListHiLUT
 .byte >$1858, >$18B2, >$190C, >$1966, >$19BB, >$1A13, >$1A6D, >$1ACA
 .byte >$1B2A, >$1B7F, >$1BDE, >$1C30, >$1C82, >$1CEC, >$1D43, >$1DA1
 .byte >$1DF6, >$1E56, >$1EB0, >$1F0A, >$1F4C, >$1FA2, >$1FC1, >$1FD7 

ES_LavaSparkleAnimationPtrLoLUT
 .byte <$1FB2 
 .byte <$1FC9 
 .byte <$1FCD 
 .byte <$1FDF 
 .byte <$1FE3 
ES_LavaSparkleAnimationPtrHiLUT
 .byte >$1FB2 
 .byte >$1FC9 
 .byte >$1FCD 
 .byte >$1FDF 
 .byte >$1FE3 

SpawnPlatformDLObjectLoLUT
 .byte <$1F60 
 .byte <$1C8A 
 .byte <$1C34 
 .byte <$1A71 
SpawnPlatformDLObjectHiLUT
 .byte >$1F60 
 .byte >$1C8A 
 .byte >$1C34
 .byte >$1A71

SpawnRiderPaletteAndWidthLUT
 .byte $B6, $BA ; spawning player rider 
 .byte $BA, $BA, $BA, $BA, $BA, $BA, $BA, $BA ; spawning enemy rider

SpawnPlatformPaletteAndWidthLUT
 .byte $7C, $7C ; spawning platform with player bird on it
 .byte $9C, $9C, $9C, $9C, $9C, $9C, $9C, $9C ; spawinging platform with enemy birds

SpawnBirdPaletteAndWidthLUT
 .byte $BE, $DE ; spawning player birds
 .byte $9E, $9E, $9E, $9E, $9E, $9E, $9E, $9E ; spawning enemy birds

AITimerDistantAggressive_ByType
 .byte $02 
 .byte $03 
 .byte $01 
 .byte $02 
 .byte $FF 

AITimerDistantPassive_Or_ApproachAggressive_ByType
 .byte $03 
 .byte $03 
 .byte $02 
 .byte $03 
PterryYVelocitySlightUp
 .byte $FF 

AITimerApproachPassive_ByType
 .byte $04 
 .byte $04 
 .byte $04 
 .byte $05 
 .byte $FF 

AITimerCloseDefensive_ByType
 .byte $05 
 .byte $06 
 .byte $06 
 .byte $07 
PterryYVelocityZero
 .byte $00 

AITimerEngagePassive_ByType
 .byte $08 
 .byte $08 
 .byte $09 
 .byte $0A 
 .byte $01 

AITimerMixedEngageAttack_ByType
 .byte $0A 
 .byte $0B 
 .byte $0C 
 .byte $0B 
PterryYVelocitySlightDown
 .byte $01 

AITimerAttackAggressive_ByType
 .byte $0C 
 .byte $0C 
 .byte $0F 
 .byte $0E 
 .byte $01 

; the below tables are largely involved in enemy vertical velocity calcs
BirdWeightByTypeLUT
 .byte $0C,$14,$1E,$26,$00
BirdFlapLiftFactorLoLUT
 .byte $80,$00,$5A,$DC,$00
BirdFlapLiftFactorHiLUT
 .byte $00,$01,$01,$01,$01
BirdMaxDownVelocityByTypeLUT
 .byte $04,$04,$04,$04,$02
BirdMaxUpVelocityByTypeLUT
 .byte $FC,$FC,$FC,$FC,$FD

AITimerByBirdTypeAndDifficultyLoLUT
 .byte $00, $00, $00, $00, $C0, $A0, $80, $40 
AITimerByBirdTypeAndDifficultyHiLUT
 .byte $10, $04, $02, $01, $00, $00, $00, $00 

AIModeByBirdTypeAndDifficultyLUT
 .byte $7F,$05,$04,$03,$7F,$04,$03,$02,$7F,$03,$02,$01

TurnCheckTimerValuesLoLUT
 .byte $00,$00,$00,$40,$00,$E0,$C0,$A0,$80,$00,$80,$40

TurnCheckTimerValuesHiLUT
 .byte $10,$03,$02,$01,$01,$00,$00,$00,$02,$02,$01,$01

; 16-bit speed index tables.
; The fastest left-heading speed is the first value.
; The neutral non-moving speed is the middle value.
; The fastest right-heading speed is the last value.
; These apply to player and enemy birds.
XPositionIncrementTableLoLUT
 .byte $20,$D0,$60,$E0,$80,$00,$80,$20,$A0,$30,$E0
XPositionIncrementTableHiLUT
 .byte $FD,$FD,$FE,$FE,$FF,$00,$00,$01,$01,$02,$02

BirdAIActionCategoryByProximityLUT
 .byte $03,$04,$04,$FF,$02,$00,$03,$FF,$01,$01,$02

TrollGrabHeight  ; If a bird object is at this Y the troll will grab it (game level is index)
 .byte $B3,$B3,$B3,$B3,$B3,$B3,$B3,$B3,$B3,$B3,$B5,$B5,$B5
 .byte $BA,$BA,$BA,$BA,$BA,$BA,$BA,$BA,$BA,$BA,$BA,$BA

InitialBirdDirection
 .byte $01,$FF,$01,$FF,$01,$FF,$01,$FF,$01,$FF,$01

PlayerRespawnY
 .byte $A7,$67,$5F,$37
PlayerRespawnX
 .byte $5E,$29,$9D,$5B

ObjectScreenWrapXRightEdgeLUT
 .byte $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE, $BE            ; Birds
 .byte $C2, $C2, $C2                                               ; Pterrys
 .byte $BB, $BB, $BB, $BB, $BB, $BB, $BB, $BB, $BB, $BB, $BB, $BB  ; Eggs

ObjectScreenWrapXLeftEdgeLUT
 .byte $1A, $1A, $1A, $1A, $1A, $1A, $1A, $1A, $1A, $1A            ; Birds
 .byte $16, $16, $16                                               ; Pterrys
 .byte $1F, $1F, $1F, $1F, $1F, $1F, $1F, $1F, $1F, $1F, $1F, $1F  ; Eggs

; enemy levels are 0:red-bounder, 1:gray-hunter, 2:blue-shadowlord
; this table says enemy upgrades are 0->1, 1->2, and 2->2.
NextEnemyBirdLevel
 .byte $01, $02, $02

PterrySpritePaletteAndWidthLUT
 .byte $9C 
 .byte $9C 
 .byte $9C 
 .byte $1C 
 .byte $1C 
 .byte $1C 

UNUSED_TitleMenuTextAddrLoLUT
 .byte <$2671 
 .byte <$2685 
 .byte <$2699 
 .byte <$26AD 
UNUSED_TitleMenuTextAddrHiLUT
 .byte >$2671 
 .byte >$2685 
 .byte >$2699 
 .byte >$26AD 
UNUSED_TitleMenuDLZoneLUT
 .byte $03 
 .byte $08 
 .byte $0C 
 .byte $10 

; packed 2-bit enemy type data, first set of 4 enemies for each wave...
EnemyWaveDataLUT_A 
 .byte $3F, $0F, $0F, $03, $03, $00, $00, $00
 .byte $00, $00, $00, $54, $54, $55, $55, $55
 .byte $55, $55, $55, $55, $95, $95, $95, $A5
 .byte $A9, $AA, $AA, $AA, $AA, $AA, $AA, $AA
 .byte $AA, $AA, $AA, $AA, $AA, $AA, $AA, $AA
 .byte $AA, $AA, $AA, $AA, $AA, $AA, $AA, $AA
 .byte $AA, $AA, $AA, $AA 

; packed 2-bit enemy type data, second set of 4 enemies for each wave
EnemyWaveDataLUT_B 
 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $3F
 .byte $3F, $0F, $0F, $0F, $0F, $0F, $5F, $5F
 .byte $03, $43, $57, $55, $5F, $5F, $5F, $5F
 .byte $5F, $5F, $5F, $5F, $95, $95, $5F, $95
 .byte $55, $A5, $5F, $A5, $A5, $AA, $AF, $AA
 .byte $AA, $AA, $AF, $AA, $AA, $AA, $AF, $AA
 .byte $AA, $AA, $AF, $AA

 .byte "GCC(C)1984" ; not used by the game, just an embedded text

 .byte $4C 
 .byte $00 
 .byte $B0 
 .byte $3F 
 .byte $39 
 .byte $34 

CART_SIGNATURE_DUPE  ; DF80-DFF7
 .byte $04,$C4,$76,$30,$6A,$8C,$8A,$3A,$CA,$F5,$BC,$71,$CA,$76,$08,$AB
 .byte $75,$58,$CD,$19,$2A,$29,$2E,$F0,$DB,$7B,$84,$98,$A4,$0E,$0A,$7C
 .byte $3F,$D8,$93,$8D,$59,$98,$FA,$77,$03,$FF,$E9,$9B,$70,$A8,$B3,$A5
 .byte $AA,$E7,$7F,$82,$CF,$DD,$5F,$F4,$54,$79,$BA,$D7,$6D,$73,$26,$CE
 .byte $B3,$06,$39,$94,$BA,$74,$A1,$E7,$8F,$AF,$6C,$B2,$E2,$74,$70,$0B
 .byte $2E,$68,$F2,$4F,$27,$E2,$AA,$4D,$F7,$B0,$CD,$C6,$9D,$B1,$42,$59
 .byte $CE,$0D,$59,$14,$13,$8E,$71,$5C,$A0,$09,$4E,$00,$85,$99,$E7,$CA
 .byte $E4,$A5,$D9,$A1,$2B,$47,$A4,$CE

 .byte $FF  ; region verification. $FF=all regions
 .byte $E7  ; encryption check starts at $E000 

 .word NMI_ROUTINE
 .word START
 .word IRQ_ROUTINE

; This rom is a bit weird, in that the last 2 8k blocks are identical
; including the cart vectors and encryption key.
;
; block 1: the graphics and code are live/used, except for the 
; brief "jmp $B000" located at DF7A. The encryption key, BIOS bytes,
; and 6502 vectors are dead/unused.
;
; block 2: the graphics and code are dead/unused, except for the
; brief "jmp $B000" located at FF7A. The encryption key, BIOS bytes,
; and 6502 vectors are (of course) used.
;
; I've left the dead parts of block 2 as uninterpreted byte values,
; to avoid confusion with the active code in block 1.
;
; So... Joust has just under 8K unused in block 2, ready and waiting 
; for rom hacks.
;
; This is block 2 of 2...

; ORG $E000

 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $2a,$a0,$2a,$80,$2a,$20,$aa,$00,$a8,$00,$aa,$00,$aa,$00,$aa,$80
 .byte $aa,$a0,$aa,$a0,$aa,$a0,$aa,$a0,$2a,$a0,$2a,$a0,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$11,$48,$22,$8c,$33,$c8,$aa,$aa
 .byte $ea,$54,$00,$10,$6a,$aa,$a8,$a0,$aa,$aa,$95,$56,$ab,$7a,$a9,$57
 .byte $00,$d0,$6a,$aa,$a5,$5a,$aa,$aa,$aa,$a0,$00,$00,$00,$15,$55,$56
 .byte $aa,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$03,$00,$33,$00,$c0,$33,$00,$c0,$0c,$c0,$cc
 .byte $30,$00,$00,$c0,$00,$00,$00,$00,$00,$0c,$00,$00,$00,$00,$0c,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0a,$a0,$0a,$aa,$56
 .byte $9a,$90,$36,$a9,$6a,$aa,$80,$00,$55,$aa,$00,$09,$7a,$a6,$a5,$fe
 .byte $a5,$a9,$54,$d6,$a5,$aa,$bd,$66,$56,$ab,$70,$00,$00,$00,$a0,$00
 .byte $02,$a0,$00,$00,$a8,$02,$aa,$02,$a0,$00,$00,$aa,$00,$44,$f0,$81
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$ff,$eb,$cf,$ff,$00,$00,$00
 .byte $00,$00,$00,$0f,$cf,$00,$00,$00,$00,$ff,$a0,$00,$00,$00,$00,$00
 .byte $2a,$80,$2a,$80,$2a,$00,$aa,$00,$aa,$00,$aa,$00,$aa,$00,$aa,$80
 .byte $aa,$80,$aa,$a0,$aa,$a0,$aa,$80,$aa,$a0,$2a,$a0,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$04,$48,$08,$8c,$0c,$c8,$2a,$aa
 .byte $aa,$94,$00,$10,$aa,$aa,$a8,$a0,$aa,$aa,$95,$56,$ab,$fe,$a9,$57
 .byte $00,$d0,$6a,$aa,$a5,$56,$aa,$aa,$aa,$a0,$00,$00,$00,$00,$55,$55
 .byte $aa,$80,$00,$00,$2a,$ab,$f4,$00,$00,$00,$00,$aa,$80,$00,$00,$00
 .byte $00,$2a,$aa,$a8,$00,$5a,$aa,$a0,$00,$00,$00,$00,$00,$00,$00,$0a
 .byte $aa,$a0,$00,$00,$00,$03,$30,$0c,$00,$c0,$0c,$00,$c0,$0f,$00,$cc
 .byte $30,$00,$00,$30,$00,$00,$00,$00,$03,$ff,$ff,$ff,$ff,$ff,$f0,$0f
 .byte $fc,$03,$f3,$c3,$cf,$00,$00,$fc,$03,$00,$f0,$28,$28,$02,$a9,$5a
 .byte $9e,$90,$36,$a9,$5a,$aa,$40,$00,$01,$5a,$a0,$0a,$fa,$a6,$a5,$fa
 .byte $a5,$6a,$54,$d6,$a6,$aa,$d7,$a6,$a5,$ab,$fa,$80,$00,$0a,$ab,$c0
 .byte $0a,$a9,$00,$02,$aa,$86,$aa,$0a,$a8,$00,$7a,$aa,$b0,$0c,$f0,$63
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$fa,$bf,$ff,$ea,$be,$f3,$ff,$c0
 .byte $00,$00,$00,$ea,$fb,$ea,$ff,$00,$03,$fa,$bf,$fa,$80,$00,$00,$00
 .byte $0a,$80,$0a,$80,$2a,$80,$2a,$80,$2a,$80,$aa,$80,$aa,$80,$aa,$80
 .byte $2a,$80,$aa,$80,$2a,$80,$aa,$80,$aa,$80,$aa,$80,$aa,$a8,$af,$a8
 .byte $ae,$a8,$aa,$a8,$2a,$a8,$2a,$a8,$2a,$a8,$2a,$a8,$aa,$a8,$aa,$a8
 .byte $aa,$a8,$2a,$a8,$2a,$a8,$2a,$a8,$04,$48,$08,$8c,$0c,$c8,$0a,$aa
 .byte $aa,$94,$00,$10,$aa,$aa,$a8,$20,$aa,$aa,$55,$4a,$aa,$7e,$a9,$57
 .byte $00,$d0,$6a,$aa,$a5,$56,$aa,$aa,$aa,$80,$00,$00,$00,$00,$05,$55
 .byte $6a,$a0,$00,$00,$aa,$aa,$bf,$d0,$00,$00,$a0,$aa,$ab,$40,$00,$00
 .byte $00,$aa,$aa,$aa,$30,$5a,$aa,$a0,$01,$56,$a8,$00,$00,$00,$70,$aa
 .byte $aa,$aa,$b0,$00,$00,$03,$0c,$00,$00,$c0,$00,$03,$00,$0c,$c0,$cf
 .byte $f0,$00,$00,$0f,$fc,$00,$00,$00,$00,$0c,$00,$00,$00,$00,$00,$30
 .byte $03,$0c,$0c,$3c,$30,$c0,$f3,$03,$03,$03,$0c,$80,$08,$00,$55,$4a
 .byte $ae,$90,$06,$aa,$16,$a9,$00,$00,$00,$00,$00,$0a,$ba,$a5,$a9,$da
 .byte $a5,$6a,$54,$d6,$a6,$aa,$fd,$a6,$aa,$aa,$aa,$00,$00,$2a,$aa,$7f
 .byte $2a,$aa,$f0,$02,$aa,$a6,$aa,$6a,$aa,$3f,$6a,$aa,$a0,$00,$12,$91
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$ff,$aa,$aa,$ba,$ab,$fa,$ee,$a0
 .byte $00,$00,$02,$aa,$ab,$ff,$ff,$00,$3f,$eb,$ff,$ab,$fa,$c0,$00,$00
 .byte $0a,$80,$0a,$80,$0a,$80,$0a,$80,$2a,$80,$2a,$80,$2a,$80,$2a,$80
 .byte $2a,$80,$2a,$80,$2a,$80,$2a,$80,$2a,$80,$aa,$80,$bb,$a8,$ba,$a8
 .byte $ae,$e8,$ba,$ab,$be,$a8,$ba,$a0,$3b,$a8,$3a,$a8,$be,$a8,$3a,$a8
 .byte $ba,$a8,$3b,$e8,$3a,$e8,$3a,$a8,$45,$48,$8a,$8c,$cf,$c8,$02,$aa
 .byte $aa,$94,$00,$10,$aa,$a8,$00,$20,$aa,$a9,$54,$0a,$aa,$be,$a9,$57
 .byte $00,$d0,$6a,$aa,$a8,$55,$aa,$aa,$aa,$00,$00,$00,$00,$00,$00,$05
 .byte $52,$a8,$00,$0a,$aa,$aa,$af,$fd,$40,$00,$a0,$aa,$aa,$f4,$00,$00
 .byte $02,$aa,$aa,$aa,$90,$5a,$aa,$a0,$15,$aa,$aa,$a0,$00,$07,$e0,$aa
 .byte $aa,$aa,$a8,$00,$00,$00,$c3,$00,$00,$c0,$00,$0c,$00,$0c,$30,$00
 .byte $30,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$c0
 .byte $03,$0c,$0c,$0c,$c0,$33,$0f,$03,$03,$03,$03,$00,$00,$00,$00,$0a
 .byte $a6,$90,$06,$aa,$00,$00,$00,$00,$00,$00,$00,$0a,$aa,$a5,$a9,$5a
 .byte $95,$6a,$50,$d6,$a6,$aa,$76,$a6,$aa,$aa,$a8,$00,$00,$aa,$aa,$5d
 .byte $ea,$aa,$ff,$0a,$aa,$aa,$a9,$aa,$aa,$bf,$aa,$aa,$a8,$00,$12,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$fe,$ff,$ff,$ff,$ff,$ea,$ba,$a8
 .byte $00,$00,$0e,$ee,$af,$ff,$ff,$00,$af,$ef,$ee,$bf,$fa,$af,$c0,$00
 .byte $0a,$80,$0a,$80,$0a,$80,$0a,$80,$0a,$80,$0a,$80,$2a,$00,$2a,$80
 .byte $2a,$80,$2a,$80,$2a,$80,$2a,$00,$2a,$20,$2a,$80,$ae,$a8,$ae,$a0
 .byte $aa,$a0,$ab,$a0,$af,$a0,$af,$a0,$af,$a0,$2e,$a8,$2f,$a8,$ab,$b8
 .byte $2b,$a8,$2f,$e8,$ae,$a8,$2b,$a8,$15,$48,$2a,$8c,$3f,$c8,$00,$2a
 .byte $aa,$a4,$00,$00,$aa,$00,$00,$00,$2a,$95,$00,$0a,$aa,$bf,$a9,$57
 .byte $00,$10,$6a,$aa,$a8,$05,$aa,$aa,$a8,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$2a,$aa,$aa,$a9,$5f,$fc,$02,$a0,$aa,$aa,$7f,$c0,$00
 .byte $02,$aa,$aa,$aa,$a0,$5a,$aa,$81,$5a,$aa,$aa,$aa,$0f,$ff,$60,$aa
 .byte $aa,$aa,$aa,$80,$00,$30,$03,$00,$3f,$ff,$ff,$f0,$00,$0c,$00,$00
 .byte $30,$00,$00,$0f,$c0,$fc,$03,$c0,$fc,$00,$15,$55,$55,$54,$00,$c0
 .byte $ff,$0c,$0c,$0f,$00,$0c,$03,$03,$03,$03,$03,$00,$00,$00,$00,$02
 .byte $ab,$94,$06,$aa,$00,$00,$00,$00,$00,$00,$00,$00,$aa,$a9,$aa,$aa
 .byte $95,$6a,$50,$d6,$aa,$aa,$ba,$a6,$aa,$aa,$a0,$00,$02,$aa,$aa,$97
 .byte $aa,$aa,$9d,$fa,$aa,$aa,$a9,$aa,$aa,$b5,$aa,$aa,$aa,$0b,$b0,$38
 .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$fe,$fd,$55,$55,$57,$aa,$ea,$ab
 .byte $c0,$00,$ff,$ef,$ab,$bf,$ff,$03,$eb,$fa,$bf,$ff,$eb,$ff,$ff,$a8
 .byte $2a,$00,$8a,$00,$0a,$00,$02,$80,$02,$a0,$0a,$a0,$0a,$80,$0a,$80
 .byte $2a,$00,$2a,$00,$2a,$00,$2a,$20,$2a,$00,$2a,$00,$bb,$a0,$ab,$a0
 .byte $ab,$a0,$ae,$80,$ae,$80,$ae,$80,$ab,$a0,$ae,$a0,$af,$eb,$af,$a8
 .byte $2f,$e8,$ab,$a8,$ab,$e8,$ab,$e8,$05,$50,$0a,$a0,$0f,$f0,$00,$0a
 .byte $aa,$a8,$00,$00,$80,$00,$00,$00,$00,$00,$00,$2a,$aa,$af,$aa,$57
 .byte $00,$10,$6a,$aa,$a8,$00,$02,$aa,$80,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$aa,$aa,$aa,$aa,$55,$ff,$ca,$a0,$aa,$aa,$97,$fd,$00
 .byte $0a,$aa,$aa,$aa,$a0,$5a,$aa,$85,$6a,$aa,$aa,$aa,$bf,$fd,$60,$aa
 .byte $aa,$aa,$aa,$a0,$00,$30,$03,$00,$00,$c0,$00,$00,$00,$0c,$00,$c0
 .byte $30,$00,$00,$30,$33,$03,$0c,$33,$03,$00,$aa,$aa,$aa,$aa,$00,$c0
 .byte $00,$03,$f0,$0c,$00,$0c,$00,$fc,$fc,$fc,$03,$00,$00,$00,$00,$00
 .byte $2a,$a4,$0a,$aa,$00,$00,$00,$00,$00,$00,$00,$00,$0a,$a9,$6a,$aa
 .byte $95,$da,$50,$36,$a9,$aa,$aa,$a6,$aa,$aa,$80,$00,$02,$aa,$aa,$9f
 .byte $aa,$aa,$b7,$ea,$aa,$aa,$ad,$aa,$aa,$a6,$aa,$aa,$aa,$3d,$f2,$fe
 .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$55,$75,$55,$55,$55,$d5,$af,$ff
 .byte $fc,$03,$ff,$ff,$ea,$bf,$ff,$ff,$fa,$ff,$ff,$ff,$bf,$ff,$ff,$af
 .byte $28,$00,$28,$00,$08,$00,$02,$80,$02,$a0,$02,$a0,$02,$a0,$0a,$a0
 .byte $28,$a0,$28,$20,$aa,$00,$28,$20,$28,$28,$a8,$80,$aa,$a0,$ae,$a0
 .byte $ae,$80,$aa,$80,$aa,$00,$aa,$00,$aa,$80,$ab,$a0,$af,$a0,$aa,$e0
 .byte $ab,$a0,$2b,$a0,$ab,$a0,$ae,$a0,$01,$08,$02,$0c,$03,$08,$00,$00
 .byte $aa,$a8,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0a,$aa,$a7,$aa,$57
 .byte $00,$10,$6a,$aa,$a8,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$aa,$aa,$aa,$aa,$57,$f7,$ea,$a0,$aa,$aa,$ad,$ff,$f0
 .byte $0a,$aa,$aa,$aa,$a0,$9a,$aa,$b5,$aa,$aa,$aa,$aa,$af,$fd,$a0,$aa
 .byte $aa,$aa,$aa,$a8,$00,$0c,$0c,$00,$00,$00,$00,$00,$00,$cc,$00,$00
 .byte $33,$00,$00,$c0,$0c,$00,$f0,$0c,$00,$c0,$00,$00,$00,$00,$00,$c0
 .byte $00,$00,$00,$00,$00,$0c,$00,$00,$00,$00,$03,$00,$00,$00,$00,$00
 .byte $0a,$a4,$0a,$80,$00,$00,$00,$00,$00,$00,$00,$00,$02,$a9,$6a,$aa
 .byte $96,$da,$50,$36,$a9,$aa,$aa,$a1,$5a,$aa,$a0,$00,$0a,$aa,$aa,$95
 .byte $aa,$aa,$95,$ea,$aa,$aa,$ad,$aa,$aa,$ae,$aa,$aa,$aa,$2a,$a0,$89
 .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$55,$d5,$55,$55,$55,$75,$55,$55
 .byte $55,$15,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55,$55
 .byte $88,$00,$08,$00,$08,$00,$00,$00,$00,$20,$00,$20,$00,$20,$00,$88
 .byte $02,$00,$28,$80,$a0,$00,$00,$00,$22,$08,$a0,$20,$aa,$a0,$aa,$a0
 .byte $aa,$80,$ae,$00,$ae,$00,$ba,$00,$aa,$00,$aa,$80,$aa,$a0,$aa,$a0
 .byte $aa,$80,$2a,$a0,$2a,$a0,$aa,$80,$05,$08,$0a,$0c,$0f,$08,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02,$aa,$ab,$ea,$54
 .byte $00,$10,$6a,$aa,$a8,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$02,$aa,$aa,$aa,$aa,$95,$77,$ea,$a0,$aa,$aa,$a5,$df,$ff
 .byte $2a,$aa,$aa,$aa,$a0,$aa,$aa,$95,$aa,$aa,$aa,$aa,$af,$75,$a0,$aa
 .byte $aa,$aa,$aa,$aa,$00,$03,$f0,$00,$00,$00,$00,$00,$03,$00,$00,$00
 .byte $00,$c0,$03,$00,$33,$00,$c0,$33,$00,$c0,$0c,$30,$cf,$f0,$00,$c0
 .byte $00,$00,$00,$00,$00,$0c,$00,$00,$00,$00,$03,$00,$00,$00,$00,$00
 .byte $00,$a8,$08,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$2a,$2a,$aa
 .byte $56,$da,$50,$36,$a9,$6a,$aa,$a0,$05,$56,$a8,$00,$2a,$aa,$aa,$a6
 .byte $aa,$6a,$af,$ea,$aa,$aa,$ad,$aa,$aa,$aa,$aa,$a5,$6a,$2e,$a1,$d0
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $aa,$aa,$6a,$5a,$96,$a5,$a9,$aa,$90,$a0,$a0,$a0,$a0,$a0,$60,$50
 .byte $aa,$a0,$a9,$5a,$55,$6a,$50,$00,$0a,$97,$7f,$ea,$aa,$aa,$a7,$5d
 .byte $7f,$ea,$95,$aa,$af,$75,$aa,$aa,$aa,$02,$aa,$aa,$aa,$95,$aa,$aa
 .byte $55,$55,$aa,$95,$57,$0f,$50,$9a,$aa,$9d,$aa,$5a,$a0,$aa,$aa,$aa
 .byte $a8,$00,$02,$95,$7f,$a9,$5a,$50,$7f,$fe,$95,$5a,$55,$5f,$3f,$56
 .byte $aa,$bf,$d7,$5a,$50,$56,$aa,$af,$d5,$40,$00,$00,$00,$00,$6a,$a9
 .byte $70,$5d,$ff,$a5,$6a,$5f,$fd,$ab,$f5,$56,$aa,$a9,$5d,$55,$aa,$a8
 .byte $00,$00,$55,$a9,$50,$55,$57,$a5,$57,$5d,$50,$6a,$a9,$55,$dd,$55
 .byte $5a,$a9,$60,$a0,$02,$aa,$95,$55,$a5,$95,$55,$55,$a0,$ab,$55,$aa
 .byte $95,$d5,$6a,$aa,$96,$95,$55,$55,$56,$2a,$aa,$a5,$9a,$a0,$5a,$95
 .byte $fe,$aa,$aa,$aa,$aa,$d5,$6a,$aa,$aa,$aa,$9a,$a5,$aa,$05,$5a,$a6
 .byte $a5,$56,$a5,$a9,$55,$56,$a5,$f5,$6a,$a5,$6a,$80,$2a,$a9,$ea,$a6
 .byte $a9,$5a,$a7,$ea,$aa,$aa,$ad,$aa,$aa,$aa,$aa,$95,$5a,$00,$e0,$85
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$10,$10
 .byte $aa,$6a,$5a,$96,$a5,$a9,$aa,$aa,$a0,$a0,$a0,$a0,$a0,$60,$50,$90
 .byte $aa,$a0,$a9,$5a,$55,$6a,$50,$00,$0a,$a5,$5f,$aa,$aa,$aa,$a9,$dd
 .byte $df,$aa,$95,$aa,$a9,$d6,$aa,$aa,$aa,$00,$2a,$aa,$aa,$95,$aa,$a9
 .byte $55,$57,$aa,$95,$57,$03,$d0,$96,$aa,$aa,$aa,$5a,$a0,$aa,$aa,$aa
 .byte $a8,$00,$02,$a7,$5f,$a9,$5a,$70,$fd,$de,$95,$5a,$55,$5f,$3f,$5a
 .byte $aa,$75,$75,$6a,$a0,$55,$aa,$ab,$dd,$55,$40,$00,$00,$00,$6a,$a9
 .byte $70,$5d,$7f,$a5,$6a,$5f,$fd,$ab,$f7,$56,$aa,$a5,$d5,$d5,$aa,$a8
 .byte $00,$00,$00,$a9,$50,$d5,$75,$a5,$57,$fd,$50,$6a,$a9,$7d,$d7,$55
 .byte $6a,$a5,$60,$a0,$02,$aa,$95,$55,$a5,$95,$55,$55,$60,$ab,$55,$aa
 .byte $75,$55,$6a,$aa,$56,$95,$55,$40,$00,$aa,$aa,$55,$56,$90,$5a,$95
 .byte $7a,$aa,$aa,$aa,$aa,$d5,$6a,$aa,$aa,$aa,$9a,$a5,$6a,$00,$5a,$a6
 .byte $a5,$56,$a5,$a9,$55,$56,$af,$55,$6a,$95,$6a,$80,$aa,$a5,$7a,$a6
 .byte $a9,$5a,$a5,$aa,$aa,$aa,$ad,$aa,$aa,$aa,$aa,$95,$5a,$00,$b0,$01
 .byte $00,$54,$54,$54,$54,$04,$54,$54,$10,$54,$04,$00,$a8,$a8,$a8,$a8
 .byte $08,$a8,$a8,$20,$a8,$08,$00,$88,$a0,$28,$a0,$a8,$a8,$88,$a8,$20
 .byte $a8,$88,$88,$80,$88,$a8,$20,$a8,$20,$88,$88,$20,$a8,$88,$41,$04
 .byte $6a,$5a,$96,$a5,$a9,$aa,$aa,$aa,$a0,$a0,$a0,$a0,$60,$50,$90,$a0
 .byte $aa,$a0,$a9,$5a,$55,$6a,$50,$00,$2a,$a5,$df,$aa,$aa,$aa,$a9,$d5
 .byte $dd,$aa,$d5,$aa,$ab,$56,$aa,$aa,$aa,$00,$02,$aa,$aa,$95,$6a,$a9
 .byte $55,$57,$6a,$a5,$57,$03,$d0,$96,$aa,$aa,$aa,$5a,$a0,$aa,$aa,$aa
 .byte $a8,$00,$02,$a9,$df,$aa,$5a,$50,$5f,$da,$95,$56,$95,$5f,$3f,$5a
 .byte $ab,$d7,$5d,$aa,$a0,$95,$6a,$ab,$ff,$fd,$80,$00,$00,$01,$6a,$a9
 .byte $50,$7f,$7f,$a5,$5a,$7f,$cf,$aa,$d5,$5a,$aa,$ad,$55,$55,$aa,$aa
 .byte $40,$00,$00,$ab,$50,$57,$57,$a5,$df,$fd,$50,$6a,$ab,$ff,$75,$55
 .byte $6a,$a7,$60,$a0,$00,$aa,$55,$55,$a5,$95,$55,$55,$60,$ab,$d5,$aa
 .byte $57,$55,$aa,$aa,$56,$95,$00,$00,$00,$aa,$a9,$55,$56,$90,$5a,$95
 .byte $7a,$aa,$aa,$aa,$aa,$d5,$6a,$aa,$aa,$aa,$9a,$a5,$6a,$00,$0a,$a6
 .byte $a5,$56,$a5,$a9,$55,$56,$af,$d5,$aa,$b5,$6a,$80,$2a,$95,$5a,$a6
 .byte $a5,$56,$a5,$aa,$aa,$aa,$a5,$aa,$5a,$aa,$aa,$55,$56,$80,$30,$eb
 .byte $00,$44,$10,$40,$04,$04,$04,$44,$10,$44,$04,$54,$88,$20,$80,$08
 .byte $08,$08,$88,$20,$88,$08,$a8,$a8,$88,$80,$88,$80,$88,$88,$20,$88
 .byte $80,$88,$a8,$80,$a0,$08,$20,$88,$20,$a8,$88,$20,$80,$88,$44,$04
 .byte $5a,$96,$a5,$a9,$aa,$aa,$aa,$6a,$a0,$a0,$a0,$60,$50,$90,$a0,$a0
 .byte $aa,$a0,$a9,$5a,$55,$6a,$50,$00,$aa,$a5,$57,$aa,$aa,$aa,$aa,$77
 .byte $5d,$aa,$d5,$aa,$aa,$da,$aa,$aa,$aa,$00,$00,$aa,$aa,$95,$6a,$a9
 .byte $55,$77,$6a,$a5,$57,$03,$d0,$96,$aa,$aa,$aa,$55,$50,$aa,$aa,$5a
 .byte $aa,$00,$02,$aa,$77,$aa,$56,$90,$f5,$da,$95,$56,$95,$5f,$3f,$5a
 .byte $ab,$75,$d5,$aa,$a0,$aa,$aa,$aa,$fe,$aa,$80,$00,$00,$05,$6a,$aa
 .byte $50,$7f,$fe,$a5,$5a,$5f,$fd,$aa,$d5,$6a,$aa,$97,$5d,$55,$aa,$aa
 .byte $00,$00,$00,$a9,$50,$5d,$77,$a5,$5f,$fd,$d0,$6a,$ab,$ff,$57,$55
 .byte $aa,$95,$60,$a0,$00,$aa,$55,$55,$a5,$95,$55,$55,$60,$ab,$55,$a9
 .byte $d5,$55,$aa,$aa,$56,$90,$00,$00,$00,$aa,$a9,$55,$55,$90,$5a,$95
 .byte $5a,$aa,$aa,$aa,$ab,$d5,$6a,$aa,$a6,$aa,$9a,$95,$6a,$00,$0a,$a6
 .byte $a5,$5e,$a5,$a9,$55,$56,$ad,$d6,$aa,$55,$6a,$80,$2a,$95,$5a,$a6
 .byte $a5,$56,$a5,$aa,$95,$aa,$a5,$a9,$56,$aa,$aa,$55,$56,$80,$a2,$14
 .byte $00,$44,$10,$54,$14,$54,$54,$54,$10,$54,$54,$54,$88,$20,$a8,$28
 .byte $a8,$a8,$a8,$20,$a8,$a8,$a8,$88,$a0,$80,$88,$a0,$88,$a8,$20,$08
 .byte $80,$88,$a8,$a8,$a8,$20,$20,$88,$88,$88,$20,$20,$20,$a0,$44,$04
 .byte $96,$a5,$a9,$aa,$aa,$aa,$6a,$5a,$a0,$a0,$60,$50,$90,$a0,$a0,$a0
 .byte $aa,$a0,$a9,$5a,$55,$6a,$50,$02,$aa,$a5,$76,$aa,$aa,$aa,$aa,$75
 .byte $7d,$aa,$d5,$6a,$aa,$da,$aa,$aa,$aa,$00,$00,$0a,$aa,$a5,$6a,$a9
 .byte $55,$9f,$6a,$a5,$57,$03,$d0,$95,$aa,$aa,$aa,$03,$50,$55,$55,$56
 .byte $aa,$00,$02,$aa,$9e,$aa,$56,$90,$5f,$5a,$55,$56,$95,$5f,$0f,$5a
 .byte $ab,$dd,$d6,$aa,$a0,$aa,$aa,$aa,$aa,$aa,$00,$00,$00,$57,$6a,$aa
 .byte $70,$77,$5e,$a5,$5a,$5c,$fd,$aa,$d5,$6a,$aa,$75,$55,$55,$6a,$aa
 .byte $40,$00,$00,$a9,$50,$d5,$f7,$a5,$5f,$cd,$50,$6a,$ab,$ff,$f5,$56
 .byte $aa,$9d,$60,$a0,$00,$2a,$55,$55,$a5,$55,$55,$d5,$50,$ab,$55,$a9
 .byte $75,$55,$aa,$a9,$56,$80,$00,$00,$00,$2a,$a5,$55,$55,$50,$56,$95
 .byte $5a,$aa,$aa,$aa,$ab,$55,$6a,$aa,$55,$aa,$9a,$95,$5a,$00,$0a,$a6
 .byte $a5,$7e,$a5,$a9,$55,$56,$ad,$5a,$aa,$75,$5a,$80,$0a,$95,$5a,$a6
 .byte $a5,$56,$a5,$aa,$55,$6a,$a5,$a9,$d6,$aa,$aa,$55,$56,$a8,$04,$06
 .byte $00,$44,$50,$04,$04,$44,$40,$40,$04,$44,$44,$f4,$88,$a0,$08,$08
 .byte $88,$80,$80,$08,$88,$88,$f8,$88,$88,$80,$88,$80,$80,$88,$20,$08
 .byte $80,$a8,$a8,$88,$88,$80,$20,$88,$88,$88,$88,$88,$08,$a0,$44,$04
 .byte $a5,$a9,$aa,$aa,$aa,$6a,$5a,$96,$a0,$60,$50,$90,$a0,$a0,$a0,$a0
 .byte $aa,$a0,$a9,$5a,$55,$6a,$50,$0a,$aa,$a9,$56,$aa,$aa,$aa,$aa,$55
 .byte $de,$aa,$d5,$6a,$aa,$da,$aa,$95,$5a,$00,$00,$00,$aa,$a5,$4a,$a9
 .byte $56,$9f,$7a,$a5,$57,$00,$d0,$95,$aa,$aa,$aa,$00,$10,$55,$55,$55
 .byte $aa,$80,$02,$aa,$aa,$aa,$56,$a0,$77,$6a,$55,$56,$95,$5f,$0f,$fa
 .byte $ab,$77,$5a,$aa,$a0,$aa,$aa,$aa,$aa,$a0,$00,$00,$05,$7f,$6a,$aa
 .byte $70,$75,$de,$a5,$5a,$5f,$fd,$aa,$d5,$aa,$a9,$f7,$75,$55,$6a,$aa
 .byte $94,$00,$00,$a9,$50,$5d,$77,$a5,$5f,$fd,$50,$6a,$ab,$f7,$55,$5a
 .byte $aa,$95,$50,$a0,$00,$2a,$55,$55,$a5,$55,$55,$55,$50,$ab,$d5,$a7
 .byte $55,$56,$aa,$a9,$55,$80,$00,$00,$00,$2a,$a5,$55,$55,$50,$56,$a5
 .byte $5a,$aa,$96,$aa,$ab,$55,$6a,$a9,$d5,$6a,$9a,$95,$5a,$00,$0a,$a6
 .byte $a5,$7e,$a5,$a9,$55,$56,$ad,$6a,$ab,$55,$5a,$a0,$0a,$55,$5a,$a6
 .byte $a5,$56,$a5,$aa,$55,$5a,$ad,$a5,$5a,$a9,$aa,$55,$00,$80,$f9,$5f
 .byte $00,$54,$10,$54,$54,$44,$54,$54,$54,$54,$54,$54,$a8,$20,$a8,$a8
 .byte $88,$a8,$a8,$a8,$a8,$a8,$a8,$20,$a0,$28,$a0,$a8,$a8,$88,$a8,$08
 .byte $80,$88,$88,$a8,$a8,$a8,$a8,$88,$88,$88,$88,$88,$a8,$88,$41,$04
 .byte $a9,$aa,$aa,$aa,$6a,$5a,$96,$a5,$60,$50,$90,$a0,$a0,$a0,$a0,$a0
 .byte $aa,$a0,$a9,$5a,$55,$6a,$50,$0a,$aa,$a9,$56,$aa,$95,$aa,$aa,$5d
 .byte $de,$aa,$d5,$6a,$aa,$9a,$aa,$55,$56,$00,$00,$00,$0a,$a9,$0a,$a5
 .byte $56,$af,$7a,$a5,$57,$00,$d0,$a5,$6a,$aa,$a8,$00,$00,$55,$55,$55
 .byte $6a,$a0,$02,$aa,$aa,$aa,$56,$a0,$77,$6a,$55,$55,$95,$57,$0f,$5a
 .byte $ab,$5f,$5a,$aa,$a0,$aa,$aa,$aa,$aa,$80,$00,$00,$55,$5f,$5a,$aa
 .byte $70,$5f,$fe,$a5,$5a,$5f,$cd,$aa,$55,$aa,$a9,$f7,$d6,$55,$5a,$aa
 .byte $b5,$00,$00,$a9,$70,$55,$ff,$a5,$7c,$ff,$50,$5a,$ab,$f5,$d5,$6a
 .byte $aa,$75,$50,$ab,$00,$09,$55,$57,$a5,$55,$57,$55,$50,$ab,$55,$ad
 .byte $55,$56,$aa,$a9,$55,$80,$00,$00,$00,$0a,$95,$55,$55,$50,$56,$a5
 .byte $6a,$a9,$55,$5a,$ab,$55,$aa,$a5,$55,$6a,$9a,$95,$5a,$00,$5a,$a6
 .byte $a5,$fe,$a5,$a9,$55,$56,$ad,$6a,$a5,$55,$5a,$a0,$02,$55,$5a,$a6
 .byte $a5,$56,$a5,$a9,$55,$5a,$a5,$a5,$5a,$a5,$aa,$50,$00,$a8,$10,$90
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$10,$10
 .byte $aa,$aa,$aa,$6a,$5a,$96,$a5,$a9,$50,$90,$a0,$a0,$a0,$a0,$a0,$60
 .byte $aa,$a0,$a9,$5a,$55,$6a,$50,$2a,$aa,$a9,$76,$aa,$55,$6a,$aa,$95
 .byte $7e,$aa,$d5,$6a,$aa,$9a,$a9,$55,$55,$00,$00,$00,$00,$a8,$02,$a5
 .byte $56,$af,$7a,$a5,$57,$00,$d0,$a5,$6a,$aa,$a8,$00,$00,$05,$55,$55
 .byte $5a,$a8,$00,$2a,$aa,$aa,$55,$a0,$9d,$aa,$55,$55,$95,$57,$0f,$9a
 .byte $aa,$77,$6a,$aa,$a0,$aa,$aa,$aa,$aa,$00,$00,$02,$55,$df,$5a,$aa
 .byte $50,$f7,$7e,$95,$5a,$5f,$3f,$aa,$56,$aa,$ad,$dd,$da,$59,$56,$aa
 .byte $ad,$50,$00,$a9,$50,$75,$df,$a5,$5f,$fd,$50,$5a,$ab,$fd,$55,$aa
 .byte $aa,$55,$50,$a8,$00,$00,$00,$77,$a5,$55,$75,$75,$50,$ab,$d5,$95
 .byte $d5,$5a,$aa,$a9,$55,$80,$00,$00,$00,$0a,$95,$55,$55,$50,$56,$a5
 .byte $6a,$a5,$55,$55,$ab,$55,$aa,$9d,$55,$6a,$96,$95,$56,$05,$7a,$a6
 .byte $a5,$fe,$a5,$a9,$57,$d6,$a5,$aa,$ad,$d5,$56,$ad,$02,$55,$5a,$a6
 .byte $a5,$56,$a5,$a9,$55,$5a,$a5,$b5,$5a,$a5,$aa,$00,$00,$00,$d0,$28
 .byte $a5,$79,$c9,$35,$d0,$02,$e6,$7a,$4c,$3d,$d0,$a9,$01,$85,$4a,$d0
 .byte $0d,$a9,$00,$85,$4a,$a5,$7a,$d0,$05,$e6,$79,$4c,$3d,$d0,$a9,$00
 .byte $85,$79,$85,$7a,$f0,$17,$ad,$82,$02,$29,$02,$f0,$06,$a9,$00,$85
 .byte $55,$10,$10,$c6,$55,$10,$0c,$20,$5a,$d0,$20,$65,$d0,$20,$5f,$d0
 .byte $20,$7a,$d0,$a5,$4d,$c5,$56,$d0,$03,$4c,$a0,$b0,$a5,$4e,$c5,$4e
 .byte $f0,$fc,$4c,$a3,$9f,$a9,$20,$85,$54,$60,$a9,$20,$85,$55,$60,$a6
 .byte $4d,$ca,$86,$56,$60,$e6,$4c,$a5,$4c,$c9,$04,$30,$0a,$a9,$00,$85
 .byte $4c,$a5,$4a,$49,$01,$85,$4a,$4c,$e9,$b4,$a6,$4a,$bd,$aa,$d2,$8d
 .byte $a5,$1e,$bd,$ab,$d2,$8d,$59,$1f,$a5,$4c,$aa,$0a,$0a,$0a,$0a,$18
 .byte $69,$b1,$85,$5e,$a9,$00,$69,$d2,$85,$5f,$a0,$0f,$b1,$5e,$99,$ad
 .byte $26,$88,$10,$f8,$bd,$ad,$d2,$8d,$ff,$1e,$a5,$7a,$c9,$29,$d0,$16
 .byte $a5,$4e,$29,$03,$c9,$03,$d0,$0e,$ce,$96,$1d,$ce,$f0,$1d,$ce,$4b
 .byte $1e,$a9,$01,$8d,$ce,$27,$60,$a5,$52,$10,$0e,$a5,$58,$d0,$0d,$ad
 .byte $82,$02,$4a,$b0,$f1,$a9,$01,$85,$58,$4c,$94,$b0,$4c,$98,$d2,$a5
 .byte $52,$10,$f6,$ad,$80,$02,$c9,$f0,$b0,$21,$4c,$9a,$b0,$a5,$4b,$d0
 .byte $ee,$a5,$57,$d0,$0d,$ad,$82,$02,$29,$08,$d0,$0f,$a9,$01,$85,$57
 .byte $d0,$38,$ad,$82,$02,$29,$08,$49,$08,$85,$57,$a5,$58,$d0,$0c,$ad
 .byte $82,$02,$4a,$b0,$09,$a9,$01,$85,$58,$d0,$be,$20,$98,$d2,$a5,$59
 .byte $d0,$0e,$ad,$82,$02,$29,$02,$d0,$10,$a9,$01,$85,$59,$4c,$9a,$b0
 .byte $ad,$82,$02,$29,$02,$49,$02,$85,$59,$60,$a5,$4d,$48,$a5,$4e,$48
 .byte $8a,$48,$a2,$01,$86,$4b,$ca,$86,$19,$86,$1a,$a6,$4d,$ca,$20,$0b
 .byte $d1,$e4,$4d,$d0,$04,$a9,$60,$85,$3c,$ad,$80,$02,$c9,$f0,$b0,$03
 .byte $20,$a2,$d2,$a5,$52,$30,$03,$20,$a2,$d2,$a5,$57,$f0,$0b,$ad,$82
 .byte $02,$29,$08,$49,$08,$85,$57,$10,$d5,$ad,$82,$02,$29,$08,$d0,$ce
 .byte $68,$aa,$68,$85,$4e,$68,$85,$4d,$a9,$01,$85,$57,$4a,$85,$4b,$a9
 .byte $40,$85,$3c,$60,$a9,$30,$8d,$00,$22,$a9,$38,$8d,$01,$22,$a9,$36
 .byte $8d,$02,$22,$a9,$3e,$8d,$03,$22,$a2,$1e,$a9,$30,$9d,$04,$22,$a9
 .byte $34,$9d,$05,$22,$ca,$ca,$10,$f2,$a2,$1c,$86,$70,$bd,$12,$d4,$8d
 .byte $91,$01,$bd,$f5,$d3,$85,$71,$a2,$1c,$bd,$c5,$98,$9d,$44,$22,$ca
 .byte $10,$f7,$a2,$e7,$bd,$0d,$d3,$9d,$60,$22,$ca,$d0,$f7,$a2,$0b,$86
 .byte $72,$bd,$2f,$d4,$8d,$a5,$01,$85,$3d,$bd,$3b,$d4,$8d,$a6,$01,$85
 .byte $3e,$bd,$47,$d4,$8d,$a7,$01,$85,$3f,$bd,$53,$d4,$85,$73,$60,$c6
 .byte $71,$10,$15,$c6,$70,$10,$04,$a9,$1c,$85,$70,$a6,$70,$bd,$12,$d4
 .byte $8d,$91,$01,$bd,$f5,$d3,$85,$71,$ee,$00,$22,$ad,$00,$22,$c9,$38
 .byte $90,$05,$a9,$30,$8d,$00,$22,$ee,$02,$22,$ad,$02,$22,$c9,$38,$90
 .byte $05,$a9,$30,$8d,$02,$22,$ee,$01,$22,$ad,$01,$22,$c9,$40,$90,$05
 .byte $a9,$38,$8d,$01,$22,$ee,$03,$22,$ad,$03,$22,$c9,$40,$90,$05,$a9
 .byte $38,$8d,$03,$22,$a2,$1f,$fe,$04,$22,$bd,$04,$22,$c9,$38,$90,$05
 .byte $a9,$30,$9d,$04,$22,$ca,$10,$ee,$a5,$73,$30,$2b,$c6,$73,$10,$27
 .byte $c6,$72,$10,$04,$a9,$0b,$85,$72,$a6,$72,$bd,$2f,$d4,$8d,$a5,$01
 .byte $85,$3d,$bd,$3b,$d4,$8d,$a6,$01,$85,$3e,$bd,$47,$d4,$8d,$a7,$01
 .byte $85,$3f,$bd,$53,$d4,$85,$73,$60,$ad,$82,$02,$29,$01,$49,$01,$85
 .byte $58,$60,$a9,$40,$85,$3c,$a6,$4d,$ca,$60,$a0,$30,$a0,$00,$20,$45
 .byte $62,$00,$00,$00,$00,$18,$1b,$1c,$1e,$22,$22,$1b,$24,$00,$00,$00
 .byte $00,$00,$00,$1e,$22,$26,$1b,$24,$21,$1b,$1a,$1e,$17,$26,$1b,$00
 .byte $00,$00,$00,$00,$00,$17,$0c,$28,$17,$22,$19,$1b,$1a,$00,$00,$00
 .byte $00,$00,$00,$00,$00,$00,$1b,$2a,$23,$1b,$24,$26,$00,$00,$00,$00
 .byte $00,$0c,$22,$1b,$00,$23,$20,$17,$2b,$1b,$24,$00,$00,$00,$00,$00
 .byte $00,$26,$29,$0c,$00,$23,$20,$17,$2b,$1b,$24,$00,$00,$00,$59,$5a
 .byte $5b,$5c,$5d,$5e,$41,$40,$40,$5f,$60,$61,$62,$63,$64,$65,$66,$43
 .byte $40,$67,$68,$40,$69,$40,$6a,$6b,$6c,$6d,$6e,$6f,$70,$71,$72,$73
 .byte $74,$40,$75,$76,$40,$77,$78,$79,$40,$7a,$7b,$40,$7c,$7d,$7e,$46
 .byte $43,$40,$7f,$40,$80,$81,$82,$83,$43,$84,$85,$86,$87,$88,$89,$8a
 .byte $8b,$8c,$8d,$40,$42,$8e,$8f,$90,$91,$92,$40,$93,$43,$40,$44,$95
 .byte $96,$46,$43,$97,$98,$99,$40,$9a,$9b,$9c,$9d,$46,$9e,$9f,$a0,$a1
 .byte $a2,$40,$42,$45,$a3,$a4,$a5,$a6,$40,$a7,$45,$40,$44,$a8,$a9,$aa
 .byte $ab,$ac,$ad,$ae,$af,$b0,$40,$b1,$44,$b2,$40,$b3,$b4,$b5,$b6,$44
 .byte $44,$40,$42,$45,$42,$46,$44,$b7,$40,$b8,$45,$40,$b9,$ba,$bb,$bc
 .byte $40,$bd,$be,$bf,$c0,$c1,$c2,$c3,$c4,$41,$40,$c5,$c6,$c7,$c8,$c9
 .byte $40,$ca,$cb,$cc,$40,$42,$43,$40,$cd,$44,$ce,$40,$cf,$d0,$40,$d1
 .byte $d2,$d3,$41,$40,$d4,$d5,$d6,$d7,$d8,$d9,$40,$da,$41,$40,$db,$44
 .byte $44,$dc,$47,$48,$40,$40,$40,$40,$49,$4a,$4b,$41,$4c,$4d,$4e,$4f
 .byte $50,$51,$40,$40,$40,$41,$40,$40,$52,$53,$40,$40,$40,$54,$55,$41
 .byte $40,$56,$57,$58,$40,$3c,$03,$03,$3c,$03,$03,$03,$03,$03,$3c,$03
 .byte $03,$03,$03,$03,$03,$3c,$03,$03,$03,$3c,$03,$03,$03,$03,$03,$03
 .byte $03,$03,$24,$15,$06,$f7,$d6,$c5,$b5,$a5,$95,$85,$76,$67,$58,$49
 .byte $3a,$2b,$1d,$1c,$1b,$0c,$0d,$0c,$0b,$1a,$19,$28,$27,$26,$25,$33
 .byte $33,$33,$33,$33,$33,$33,$32,$22,$11,$10,$00,$1f,$1e,$1d,$1c,$1b
 .byte $1a,$18,$16,$22,$11,$10,$00,$10,$10,$10,$10,$10,$10,$10,$10,$10
 .byte $10,$10,$00,$ff,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$a9
 .byte $ff,$85,$6b,$85,$6c,$a9,$00,$85,$19,$85,$1a,$60,$c5,$6b,$d0,$09
 .byte $a9,$00,$85,$19,$a9,$ff,$85,$6b,$60,$38,$e5,$6c,$d0,$06,$85,$1a
 .byte $a9,$ff,$85,$6c,$60,$85,$6a,$a5,$95,$d0,$f9,$a5,$4b,$d0,$f5,$98
 .byte $48,$8a,$48,$a0,$01,$a6,$6a,$b9,$6b,$00,$c9,$ff,$f0,$21,$88,$10
 .byte $f6,$a5,$6c,$29,$7f,$a8,$bd,$30,$d6,$d9,$30,$d6,$a0,$01,$b0,$0f
 .byte $a5,$6b,$29,$7f,$a8,$bd,$30,$d6,$d9,$30,$d6,$90,$08,$a0,$00,$8a
 .byte $09,$80,$99,$6b,$00,$68,$aa,$68,$a8,$60,$a5,$4b,$d0,$fb,$a2,$01
 .byte $b4,$6b,$c8,$d0,$03,$4c,$a4,$d5,$88,$10,$16,$98,$29,$7f,$95,$6b
 .byte $a8,$a9,$01,$9d,$00,$21,$a9,$ff,$9d,$02,$21,$9d,$04,$21,$9d,$06
 .byte $21,$b9,$ab,$d5,$85,$5c,$b9,$0a,$d6,$85,$5d,$de,$00,$21,$d0,$d5
 .byte $b9,$1d,$d6,$9d,$00,$21,$bc,$02,$21,$fe,$04,$21,$fe,$06,$21,$c8
 .byte $b1,$5c,$c9,$ff,$d0,$08,$95,$6b,$a9,$00,$95,$19,$f0,$b7,$c9,$fe
 .byte $f0,$14,$c9,$fd,$d0,$1b,$c8,$b1,$5c,$95,$6b,$a8,$b9,$ab,$d5,$85
 .byte $5c,$b9,$0a,$d6,$85,$5d,$a0,$00,$98,$9d,$04,$21,$9d,$06,$21,$b1
 .byte $5c,$10,$0d,$c8,$b1,$5c,$9d,$00,$21,$88,$b1,$5c,$c8,$4c,$6a,$d5
 .byte $0a,$10,$16,$4a,$29,$bf,$9d,$00,$21,$98,$9d,$02,$21,$de,$06,$21
 .byte $de,$04,$21,$a9,$00,$95,$19,$f0,$3b,$4a,$95,$17,$98,$9d,$02,$21
 .byte $b4,$6b,$b9,$be,$d5,$85,$5c,$b9,$f7,$d5,$85,$5d,$bc,$06,$21,$b1
 .byte $5c,$10,$01,$88,$95,$19,$98,$9d,$06,$21,$b4,$6b,$b9,$d1,$d5,$85
 .byte $5c,$b9,$e4,$d5,$85,$5d,$bc,$04,$21,$b1,$5c,$10,$01,$88,$95,$15
 .byte $98,$9d,$04,$21,$ca,$30,$03,$4c,$d0,$d4,$60,$43,$8d,$be,$cd,$76
 .byte $7c,$9d,$9f,$b5,$d8,$e9,$1a,$31,$47,$50,$65,$86,$9b,$b6,$5e,$a6
 .byte $c6,$22,$d2,$8d,$d1,$d3,$c7,$e1,$02,$26,$42,$4c,$5b,$d1,$91,$d4
 .byte $c1,$78,$cc,$ce,$cc,$cc,$cc,$cf,$cb,$cd,$ce,$cd,$cb,$d0,$cb,$cb
 .byte $cc,$cb,$cc,$cb,$d6,$d8,$d8,$d8,$d8,$d8,$d8,$d8,$d8,$d8,$d8,$d8
 .byte $d8,$d8,$d8,$d8,$d8,$d8,$d8,$d6,$d6,$d6,$d7,$d8,$d7,$d8,$d8,$d7
 .byte $d7,$d8,$d8,$d8,$d8,$d8,$d8,$d8,$d8,$d8,$d6,$d6,$d6,$d6,$d7,$d7
 .byte $d7,$d7,$d7,$d7,$d7,$d8,$d8,$d8,$d8,$d8,$d8,$d8,$d8,$02,$01,$02
 .byte $01,$01,$01,$01,$04,$02,$04,$07,$02,$04,$02,$0e,$01,$02,$01,$01
 .byte $00,$09,$05,$09,$02,$04,$02,$08,$04,$05,$05,$00,$03,$04,$06,$03
 .byte $07,$00,$02,$0b,$0b,$11,$11,$0f,$0f,$17,$17,$00,$00,$00,$00,$0b
 .byte $0b,$11,$11,$0b,$0b,$11,$11,$0f,$0f,$17,$17,$00,$00,$ff,$04,$00
 .byte $03,$00,$02,$00,$01,$00,$00,$00,$00,$00,$04,$00,$03,$00,$04,$00
 .byte $03,$00,$02,$00,$01,$00,$00,$00,$04,$04,$04,$04,$0d,$0d,$0d,$0d
 .byte $0d,$0d,$0d,$0d,$04,$04,$04,$04,$04,$04,$04,$04,$8d,$0c,$0e,$11
 .byte $16,$0c,$0e,$11,$16,$0c,$0e,$11,$16,$0c,$0e,$11,$16,$0c,$0e,$11
 .byte $16,$0c,$0e,$11,$16,$ff,$0b,$0b,$0b,$0b,$09,$09,$09,$09,$07,$07
 .byte $07,$07,$06,$06,$06,$06,$04,$04,$04,$04,$02,$02,$02,$02,$18,$16
 .byte $14,$11,$0f,$0d,$0b,$ff,$0b,$0a,$09,$09,$07,$06,$05,$0d,$0c,$0b
 .byte $0a,$09,$08,$07,$0d,$0c,$0b,$0a,$09,$08,$07,$0d,$0c,$0b,$0a,$09
 .byte $08,$07,$0d,$0c,$0b,$0a,$09,$08,$07,$0d,$0c,$0b,$0a,$09,$08,$07
 .byte $0d,$0c,$0b,$0a,$09,$08,$07,$0d,$0c,$0b,$0a,$09,$08,$07,$0d,$0c
 .byte $0b,$0a,$09,$08,$07,$0d,$0c,$0b,$0a,$09,$08,$07,$0d,$0c,$0b,$0a
 .byte $09,$08,$07,$0d,$0c,$0b,$0a,$09,$08,$07,$0d,$0c,$0b,$0a,$09,$08
 .byte $07,$ff,$0f,$0f,$0a,$09,$07,$06,$05,$0e,$0e,$0a,$09,$07,$06,$05
 .byte $0c,$0c,$09,$08,$07,$06,$05,$0b,$0b,$09,$08,$07,$06,$05,$0a,$0a
 .byte $08,$07,$06,$05,$04,$09,$08,$07,$06,$05,$04,$03,$08,$06,$05,$04
 .byte $03,$02,$02,$07,$06,$05,$04,$03,$02,$02,$06,$05,$04,$03,$03,$02
 .byte $02,$05,$05,$04,$04,$03,$02,$02,$04,$04,$03,$03,$02,$02,$01,$03
 .byte $03,$03,$02,$02,$02,$02,$0f,$10,$11,$12,$1f,$ff,$0f,$10,$11,$12
 .byte $0f,$10,$11,$12,$0f,$10,$11,$12,$0f,$10,$11,$12,$ff,$08,$08,$08
 .byte $08,$06,$06,$06,$06,$04,$04,$04,$04,$02,$02,$02,$02,$04,$ff,$0f
 .byte $14,$18,$1f,$0f,$14,$18,$1f,$0f,$14,$18,$1f,$0f,$14,$18,$1f,$18
 .byte $1a,$1c,$1e,$1f,$ff,$08,$08,$08,$08,$08,$08,$08,$08,$07,$08,$09
 .byte $0a,$0b,$0c,$0d,$0e,$0f,$ff,$0a,$0a,$0a,$0a,$0a,$0a,$0a,$0a,$09
 .byte $09,$09,$09,$08,$07,$06,$05,$04,$1f,$12,$00,$1e,$11,$00,$1d,$10
 .byte $ff,$08,$08,$00,$08,$08,$00,$08,$08,$1f,$1f,$1e,$1e,$1d,$1c,$1b
 .byte $1a,$19,$18,$17,$16,$15,$14,$13,$12,$11,$10,$0f,$0e,$0d,$0c,$0b
 .byte $0a,$ff,$0a,$0a,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08
 .byte $08,$08,$08,$08,$07,$06,$05,$04,$03,$02,$0d,$0b,$09,$00,$0d,$0b
 .byte $09,$00,$0d,$0b,$09,$ff,$06,$07,$08,$00,$06,$06,$06,$00,$04,$04
 .byte $04,$1f,$1f,$1e,$1d,$1c,$1b,$1a,$19,$18,$17,$16,$15,$14,$13,$12
 .byte $11,$ff,$0a,$0a,$07,$07,$88,$03,$03,$03,$03,$ff,$08,$07,$06,$05
 .byte $04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$ff,$04,$05,$06,$07,$07
 .byte $07,$05,$04,$03,$01,$14,$14,$14,$14,$16,$14,$17,$15,$16,$14,$17
 .byte $15,$17,$14,$15,$14,$14,$16,$14,$15,$17,$14,$17,$15,$16,$14,$17
 .byte $15,$17,$14,$15,$14,$fe,$0f,$11,$13,$15,$17,$19,$1b,$1d,$1e,$1f
 .byte $ff,$09,$09,$08,$07,$06,$05,$04,$03,$02,$01,$0f,$0e,$0d,$0c,$0b
 .byte $0a,$09,$08,$00,$00,$00,$00,$00,$00,$00,$00,$0f,$0e,$0d,$0c,$0b
 .byte $0a,$09,$08,$00,$00,$ff,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18
 .byte $ff,$03,$03,$03,$06,$06,$06,$05,$05,$05,$02,$88,$84,$87,$8d,$86
 .byte $83,$88,$84,$8a,$82,$20,$09,$d9,$a9,$00,$8d,$0e,$25,$20,$f7,$3f
 .byte $60,$20,$09,$d9,$20,$fa,$3f,$a9,$c8,$85,$34,$a9,$00,$85,$fd,$60
 .byte $a5,$4c,$85,$62,$20,$09,$d9,$a5,$4a,$f0,$08,$ad,$ae,$27,$d0,$03
 .byte $8d,$0e,$25,$20,$fd,$3f,$4c,$e7,$d8,$ad,$00,$39,$c9,$c6,$d0,$61
 .byte $ad,$04,$39,$c9,$fe,$d0,$5a,$a2,$0f,$bd,$73,$d9,$9d,$ff,$24,$ca
 .byte $d0,$f7,$a2,$28,$bd,$97,$d9,$9d,$b3,$18,$ca,$d0,$f7,$a9,$01,$85
 .byte $fd,$20,$dd,$d1,$a5,$62,$aa,$0a,$0a,$0d,$ae,$27,$8d,$02,$25,$bd
 .byte $11,$da,$8d,$06,$25,$bd,$15,$da,$8d,$07,$25,$ad,$ae,$27,$f0,$12
 .byte $a9,$00,$8d,$af,$27,$a2,$03,$b5,$44,$9d,$af,$27,$ca,$d0,$f8,$4c
 .byte $6c,$d9,$a2,$03,$b5,$3f,$9d,$af,$27,$ca,$d0,$f8,$a2,$00,$a0,$25
 .byte $60,$68,$68,$60,$8b,$23,$00,$00,$00,$00,$c0,$d9,$af,$27,$83,$d9
 .byte $70,$d9,$04,$8f,$27,$cb,$08,$27,$cb,$07,$18,$b4,$07,$18,$bb,$07
 .byte $18,$c2,$07,$18,$c9,$07,$18,$d0,$cd,$40,$c0,$f2,$3c,$00,$00,$db
 .byte $40,$c0,$ef,$30,$00,$00,$dd,$40,$c8,$f1,$30,$00,$00,$ec,$40,$c8
 .byte $ef,$2c,$00,$00,$ec,$40,$c0,$ef,$2c,$ec,$80,$c0,$ff,$a0,$00,$00
 .byte $1d,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$01,$04,$06,$08,$0d,$0d,$04
 .byte $11,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$08,$0d,$13,$04,$11,$0c
 .byte $04,$03,$08,$00,$13,$04,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$00
 .byte $03,$15,$00,$0d,$02,$04,$03,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$1d
 .byte $1d,$04,$17,$0f,$04,$11,$13,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$1d
 .byte $1d,$c0,$d4,$e6,$f7,$d9,$d9,$d9,$d9,$00,$00,$00,$fa,$ff,$cf,$0d
 .byte $0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$00,$00
 .byte $00,$1a,$10,$14,$00,$29,$32,$46,$09,$84,$1e,$bc,$00,$39,$0d,$33
 .byte $15,$a9,$85,$0d,$1c,$44,$16,$e3,$e8,$fe,$58,$2a,$fa,$54,$50,$4c
 .byte $26,$22,$1e,$93,$ed,$48,$18,$19,$1c,$18,$19,$19,$19,$1c,$1c,$1c
 .byte $1d,$1d,$1e,$5e,$67,$82,$64,$68,$72,$7c,$8b,$95,$9f,$a5,$b2,$bf
 .byte $c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$a1,$55,$fb
 .byte $c1,$c6,$4e,$53,$d7,$dc,$ad,$a8,$07,$02,$61,$5c,$bb,$b6,$16,$11
 .byte $70,$6b,$ca,$c5,$25,$20,$7f,$7a,$d9,$d4,$33,$2e,$8d,$88,$e7,$e2
 .byte $42,$3d,$9c,$97,$f6,$f1,$51,$4c,$ab,$a6,$05,$00,$5f,$5a,$ba,$b5
 .byte $cb,$d0,$b1,$ac,$a7,$a2,$0c,$07,$02,$fd,$66,$61,$5c,$57,$c0,$bb
 .byte $b6,$b1,$1b,$16,$11,$0c,$75,$70,$6b,$66,$cf,$ca,$c5,$c0,$1e,$1f
 .byte $1e,$1f,$1f,$18,$18,$1f,$1f,$18,$18,$19,$19,$19,$19,$19,$19,$1a
 .byte $1a,$1a,$1a,$1a,$1a,$1b,$1b,$1b,$1b,$1b,$1b,$1c,$1c,$1c,$1c,$1c
 .byte $1c,$1d,$1d,$1d,$1d,$1d,$1d,$1e,$1e,$1e,$1e,$1f,$1f,$1f,$1f,$1f
 .byte $1f,$1f,$1f,$19,$19,$19,$19,$1a,$1a,$1a,$19,$1a,$1a,$1a,$1a,$1a
 .byte $1a,$1a,$1a,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$85
 .byte $99,$ad,$44,$51,$04,$04,$04,$04,$00,$02,$00,$02,$00,$02,$00,$02
 .byte $00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02
 .byte $00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02
 .byte $00,$02,$00,$02,$61,$68,$72,$7c,$81,$89,$93,$9d,$a3,$ab,$b5,$bf
 .byte $c3,$c9,$d3,$dd,$df,$e9,$f3,$fd,$02,$0c,$16,$20,$25,$2f,$39,$43
 .byte $26,$26,$26,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22
 .byte $22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22
 .byte $22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22
 .byte $22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22
 .byte $22,$22,$22,$22,$22,$22,$22,$22,$22,$23,$23,$23,$23,$23,$23,$23
 .byte $23,$90,$90,$90,$93,$90,$00,$08,$00,$08,$1e,$1e,$1e,$1e,$1e,$1e
 .byte $1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e
 .byte $1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e,$1e
 .byte $1e,$1e,$1e,$1e,$1e,$1e,$f9,$f6,$f6,$fb,$f8,$f6,$f6,$fa,$f8,$f6
 .byte $f6,$fc,$fa,$f6,$f6,$fe,$f6,$f6,$f6,$fb,$f6,$f6,$f6,$fb,$f6,$f6
 .byte $f6,$fb,$a0,$a0,$a0,$10,$50,$00,$80,$00,$80,$00,$9a,$00,$9a,$00
 .byte $9a,$00,$9a,$00,$9a,$00,$9a,$00,$9a,$00,$9a,$00,$9a,$00,$9a,$00
 .byte $9a,$00,$9a,$00,$9a,$00,$9a,$00,$9a,$00,$9a,$00,$9a,$00,$9a,$00
 .byte $9a,$00,$9a,$00,$9a,$00,$9a,$17,$31,$57,$7d,$13,$31,$57,$7d,$13
 .byte $31,$57,$7d,$1b,$31,$57,$7d,$0b,$31,$57,$7d,$0b,$31,$57,$7d,$0b
 .byte $31,$57,$7d,$0f,$27,$cb,$08,$27,$cb,$47,$18,$4e,$47,$18,$a8,$47
 .byte $18,$a8,$47,$18,$a8,$47,$18,$fa,$47,$19,$4c,$47,$19,$a2,$47,$19
 .byte $fd,$47,$1a,$57,$47,$1a,$b1,$47,$1b,$0c,$47,$1b,$66,$47,$1b,$c0
 .byte $47,$1c,$1e,$47,$1d,$3d,$47,$1d,$93,$47,$1d,$ed,$47,$1e,$48,$47
 .byte $1e,$a1,$47,$1e,$fb,$47,$1f,$55,$47,$1f,$b5,$47,$1f,$c1,$47,$1f
 .byte $d7,$0f,$27,$cb,$06,$27,$cb,$8f,$27,$cb,$17,$13,$6d,$71,$30,$34
 .byte $8e,$82,$86,$8a,$43,$58,$54,$5c,$60,$50,$4c,$ae,$aa,$b6,$b2,$a6
 .byte $a2,$c1,$c5,$d1,$c9,$cd,$d7,$db,$e7,$df,$e3,$1a,$1a,$1a,$1a,$1c
 .byte $1c,$1c,$1c,$1c,$1c,$1d,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$1f
 .byte $1f,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$a4,$fb,$aa,$ba
 .byte $97,$ba,$a0,$08,$11,$ba,$17,$00,$00,$8e,$ba,$20,$20,$8e,$8e,$a9
 .byte $be,$3c,$3c,$8e,$8e,$c4,$c0,$c2,$8e,$8e,$de,$c0,$c2,$a8,$a0,$a8
 .byte $a8,$a8,$a8,$a8,$c0,$c0,$a8,$c0,$c0,$c0,$a0,$a8,$c0,$c0,$af,$af
 .byte $a0,$a8,$c0,$c0,$af,$af,$a0,$a8,$a8,$af,$af,$a0,$a8,$a8,$1a,$1b
 .byte $14,$5c,$17,$5c,$1c,$17,$1a,$5c,$17,$18,$18,$05,$5c,$3e,$3e,$37
 .byte $37,$05,$3f,$3e,$3e,$37,$37,$06,$3f,$3f,$37,$37,$03,$3f,$3f,$88
 .byte $00,$2e,$38,$6d,$7a,$7c,$00,$8b,$05,$37,$fd,$82,$19,$3b,$f7,$a2
 .byte $a0,$a0,$1b,$b0,$f7,$a2,$a0,$a0,$1b,$b0,$b0,$f9,$84,$15,$b0,$b0
 .byte $bb,$ca,$7f,$f6,$ba,$eb,$19,$1a,$1b,$1d,$1f,$1f,$71,$85,$99,$ad
 .byte $59,$00,$26,$26,$26,$26,$26,$22,$90,$90,$90,$90,$68,$92,$2e,$2e
 .byte $2e,$2e,$20,$a8,$58,$b2,$0c,$66,$bb,$13,$6d,$ca,$2a,$7f,$de,$30
 .byte $82,$ec,$43,$a1,$f6,$56,$b0,$0a,$4c,$a2,$c1,$d7,$18,$18,$19,$19
 .byte $19,$1a,$1a,$1a,$1b,$1b,$1b,$1c,$1c,$1c,$1d,$1d,$1d,$1e,$1e,$1f
 .byte $1f,$1f,$1f,$1f,$b2,$c9,$cd,$df,$e3,$1f,$1f,$1f,$1f,$1f,$60,$8a
 .byte $34,$71,$1f,$1c,$1c,$1a,$b6,$ba,$ba,$ba,$ba,$ba,$ba,$ba,$ba,$ba
 .byte $7c,$7c,$9c,$9c,$9c,$9c,$9c,$9c,$9c,$9c,$be,$de,$9e,$9e,$9e,$9e
 .byte $9e,$9e,$9e,$9e,$02,$03,$01,$02,$ff,$03,$03,$02,$03,$ff,$04,$04
 .byte $04,$05,$ff,$05,$06,$06,$07,$00,$08,$08,$09,$0a,$01,$0a,$0b,$0c
 .byte $0b,$01,$0c,$0c,$0f,$0e,$01,$0c,$14,$1e,$26,$00,$80,$00,$5a,$dc
 .byte $00,$00,$01,$01,$01,$01,$04,$04,$04,$04,$02,$fc,$fc,$fc,$fc,$fd
 .byte $00,$00,$00,$00,$c0,$a0,$80,$40,$10,$04,$02,$01,$00,$00,$00,$00
 .byte $7f,$05,$04,$03,$7f,$04,$03,$02,$7f,$03,$02,$01,$00,$00,$00,$40
 .byte $00,$e0,$c0,$a0,$80,$00,$80,$40,$10,$03,$02,$01,$01,$00,$00,$00
 .byte $02,$02,$01,$01,$20,$d0,$60,$e0,$80,$00,$80,$20,$a0,$30,$e0,$fd
 .byte $fd,$fe,$fe,$ff,$00,$00,$01,$01,$02,$02,$03,$04,$04,$ff,$02,$00
 .byte $03,$ff,$01,$01,$02,$b3,$b3,$b3,$b3,$b3,$b3,$b3,$b3,$b3,$b3,$b5
 .byte $b5,$b5,$ba,$ba,$ba,$ba,$ba,$ba,$ba,$ba,$ba,$ba,$ba,$ba,$01,$ff
 .byte $01,$ff,$01,$ff,$01,$ff,$01,$ff,$01,$a7,$67,$5f,$37,$5e,$29,$9d
 .byte $5b,$be,$be,$be,$be,$be,$be,$be,$be,$be,$be,$c2,$c2,$c2,$bb,$bb
 .byte $bb,$bb,$bb,$bb,$bb,$bb,$bb,$bb,$bb,$bb,$1a,$1a,$1a,$1a,$1a,$1a
 .byte $1a,$1a,$1a,$1a,$16,$16,$16,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$1f
 .byte $1f,$1f,$1f,$01,$02,$02,$9c,$9c,$9c,$1c,$1c,$1c,$71,$85,$99,$ad
 .byte $26,$26,$26,$26,$03,$08,$0c,$10,$3f,$0f,$0f,$03,$03,$00,$00,$00
 .byte $00,$00,$00,$54,$54,$55,$55,$55,$55,$55,$55,$55,$95,$95,$95,$a5
 .byte $a9,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa
 .byte $aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$aa,$ff,$ff,$ff,$ff
 .byte $ff,$ff,$ff,$3f,$3f,$0f,$0f,$0f,$0f,$0f,$5f,$5f,$03,$43,$57,$55
 .byte $5f,$5f,$5f,$5f,$5f,$5f,$5f,$5f,$95,$95,$5f,$95,$55,$a5,$5f,$a5
 .byte $a5,$aa,$af,$aa,$aa,$aa,$af,$aa,$aa,$aa,$af,$aa,$aa,$aa,$af,$aa
 .byte $47,$43,$43,$28,$43,$29,$31,$39,$38,$34

  ORG $FF7A
START
  JMP GameColdInit

 .byte $3F 
 .byte $39 
 .byte $34 

CART_SIGNATURE:  ; FF80-FFF7
 .byte $04,$C4,$76,$30,$6A,$8C,$8A,$3A,$CA,$F5,$BC,$71,$CA,$76,$08,$AB
 .byte $75,$58,$CD,$19,$2A,$29,$2E,$F0,$DB,$7B,$84,$98,$A4,$0E,$0A,$7C
 .byte $3F,$D8,$93,$8D,$59,$98,$FA,$77,$03,$FF,$E9,$9B,$70,$A8,$B3,$A5
 .byte $AA,$E7,$7F,$82,$CF,$DD,$5F,$F4,$54,$79,$BA,$D7,$6D,$73,$26,$CE
 .byte $B3,$06,$39,$94,$BA,$74,$A1,$E7,$8F,$AF,$6C,$B2,$E2,$74,$70,$0B
 .byte $2E,$68,$F2,$4F,$27,$E2,$AA,$4D,$F7,$B0,$CD,$C6,$9D,$B1,$42,$59
 .byte $CE,$0D,$59,$14,$13,$8E,$71,$5C,$A0,$09,$4E,$00,$85,$99,$E7,$CA
 .byte $E4,$A5,$D9,$A1,$2B,$47,$A4,$CE

 .byte $FF  ; region verification. $FF=all regions
 .byte $E7  ; encryption check starts at $E000 

 .word NMI_ROUTINE
 .word START
 .word IRQ_ROUTINE