JOUST 7800 NTSC Source Code
From 8BitDev.org - Atari 7800 Development Wiki
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