introducao-a-assembly
Repositório dos arquivos usados na apresentação "Introdução a Assembly" da CriptoGoma de 2020 🖥️
mario.asm (751389B)
1 ;SMBDIS.ASM - A COMPREHENSIVE SUPER MARIO BROS. DISASSEMBLY 2 ;by doppelganger (doppelheathen@gmail.com) 3 4 ;This file is provided for your own use as-is. It will require the character rom data 5 ;and an iNES file header to get it to work. 6 7 ;There are so many people I have to thank for this, that taking all the credit for 8 ;myself would be an unforgivable act of arrogance. Without their help this would 9 ;probably not be possible. So I thank all the peeps in the nesdev scene whose insight into 10 ;the 6502 and the NES helped me learn how it works (you guys know who you are, there's no 11 ;way I could have done this without your help), as well as the authors of x816 and SMB 12 ;Utility, and the reverse-engineers who did the original Super Mario Bros. Hacking Project, 13 ;which I compared notes with but did not copy from. Last but certainly not least, I thank 14 ;Nintendo for creating this game and the NES, without which this disassembly would 15 ;only be theory. 16 17 ;Assembles with x816. 18 19 ;------------------------------------------------------------------------------------- 20 ;DEFINES 21 22 ;NES specific hardware defines 23 24 PPU_CTRL_REG1 = $2000 25 PPU_CTRL_REG2 = $2001 26 PPU_STATUS = $2002 27 PPU_SPR_ADDR = $2003 28 PPU_SPR_DATA = $2004 29 PPU_SCROLL_REG = $2005 30 PPU_ADDRESS = $2006 31 PPU_DATA = $2007 32 33 SND_REGISTER = $4000 34 SND_SQUARE1_REG = $4000 35 SND_SQUARE2_REG = $4004 36 SND_TRIANGLE_REG = $4008 37 SND_NOISE_REG = $400c 38 SND_DELTA_REG = $4010 39 SND_MASTERCTRL_REG = $4015 40 41 SPR_DMA = $4014 42 JOYPAD_PORT = $4016 43 JOYPAD_PORT1 = $4016 44 JOYPAD_PORT2 = $4017 45 46 ; GAME SPECIFIC DEFINES 47 48 ObjectOffset = $08 49 50 FrameCounter = $09 51 52 SavedJoypadBits = $06fc 53 SavedJoypad1Bits = $06fc 54 SavedJoypad2Bits = $06fd 55 JoypadBitMask = $074a 56 JoypadOverride = $0758 57 58 A_B_Buttons = $0a 59 PreviousA_B_Buttons = $0d 60 Up_Down_Buttons = $0b 61 Left_Right_Buttons = $0c 62 63 GameEngineSubroutine = $0e 64 65 Mirror_PPU_CTRL_REG1 = $0778 66 Mirror_PPU_CTRL_REG2 = $0779 67 68 OperMode = $0770 69 OperMode_Task = $0772 70 ScreenRoutineTask = $073c 71 72 GamePauseStatus = $0776 73 GamePauseTimer = $0777 74 75 DemoAction = $0717 76 DemoActionTimer = $0718 77 78 TimerControl = $0747 79 IntervalTimerControl = $077f 80 81 Timers = $0780 82 SelectTimer = $0780 83 PlayerAnimTimer = $0781 84 JumpSwimTimer = $0782 85 RunningTimer = $0783 86 BlockBounceTimer = $0784 87 SideCollisionTimer = $0785 88 JumpspringTimer = $0786 89 GameTimerCtrlTimer = $0787 90 ClimbSideTimer = $0789 91 EnemyFrameTimer = $078a 92 FrenzyEnemyTimer = $078f 93 BowserFireBreathTimer = $0790 94 StompTimer = $0791 95 AirBubbleTimer = $0792 96 ScrollIntervalTimer = $0795 97 EnemyIntervalTimer = $0796 98 BrickCoinTimer = $079d 99 InjuryTimer = $079e 100 StarInvincibleTimer = $079f 101 ScreenTimer = $07a0 102 WorldEndTimer = $07a1 103 DemoTimer = $07a2 104 105 Sprite_Data = $0200 106 107 Sprite_Y_Position = $0200 108 Sprite_Tilenumber = $0201 109 Sprite_Attributes = $0202 110 Sprite_X_Position = $0203 111 112 ScreenEdge_PageLoc = $071a 113 ScreenEdge_X_Pos = $071c 114 ScreenLeft_PageLoc = $071a 115 ScreenRight_PageLoc = $071b 116 ScreenLeft_X_Pos = $071c 117 ScreenRight_X_Pos = $071d 118 119 PlayerFacingDir = $33 120 DestinationPageLoc = $34 121 VictoryWalkControl = $35 122 ScrollFractional = $0768 123 PrimaryMsgCounter = $0719 124 SecondaryMsgCounter = $0749 125 126 HorizontalScroll = $073f 127 VerticalScroll = $0740 128 ScrollLock = $0723 129 ScrollThirtyTwo = $073d 130 Player_X_Scroll = $06ff 131 Player_Pos_ForScroll = $0755 132 ScrollAmount = $0775 133 134 AreaData = $e7 135 AreaDataLow = $e7 136 AreaDataHigh = $e8 137 EnemyData = $e9 138 EnemyDataLow = $e9 139 EnemyDataHigh = $ea 140 141 AreaParserTaskNum = $071f 142 ColumnSets = $071e 143 CurrentPageLoc = $0725 144 CurrentColumnPos = $0726 145 BackloadingFlag = $0728 146 BehindAreaParserFlag = $0729 147 AreaObjectPageLoc = $072a 148 AreaObjectPageSel = $072b 149 AreaDataOffset = $072c 150 AreaObjOffsetBuffer = $072d 151 AreaObjectLength = $0730 152 StaircaseControl = $0734 153 AreaObjectHeight = $0735 154 MushroomLedgeHalfLen = $0736 155 EnemyDataOffset = $0739 156 EnemyObjectPageLoc = $073a 157 EnemyObjectPageSel = $073b 158 MetatileBuffer = $06a1 159 BlockBufferColumnPos = $06a0 160 CurrentNTAddr_Low = $0721 161 CurrentNTAddr_High = $0720 162 AttributeBuffer = $03f9 163 164 LoopCommand = $0745 165 166 DisplayDigits = $07d7 167 TopScoreDisplay = $07d7 168 ScoreAndCoinDisplay = $07dd 169 PlayerScoreDisplay = $07dd 170 GameTimerDisplay = $07f8 171 DigitModifier = $0134 172 173 VerticalFlipFlag = $0109 174 FloateyNum_Control = $0110 175 ShellChainCounter = $0125 176 FloateyNum_Timer = $012c 177 FloateyNum_X_Pos = $0117 178 FloateyNum_Y_Pos = $011e 179 FlagpoleFNum_Y_Pos = $010d 180 FlagpoleFNum_YMFDummy = $010e 181 FlagpoleScore = $010f 182 FlagpoleCollisionYPos = $070f 183 StompChainCounter = $0484 184 185 VRAM_Buffer1_Offset = $0300 186 VRAM_Buffer1 = $0301 187 VRAM_Buffer2_Offset = $0340 188 VRAM_Buffer2 = $0341 189 VRAM_Buffer_AddrCtrl = $0773 190 Sprite0HitDetectFlag = $0722 191 DisableScreenFlag = $0774 192 DisableIntermediate = $0769 193 ColorRotateOffset = $06d4 194 195 TerrainControl = $0727 196 AreaStyle = $0733 197 ForegroundScenery = $0741 198 BackgroundScenery = $0742 199 CloudTypeOverride = $0743 200 BackgroundColorCtrl = $0744 201 AreaType = $074e 202 AreaAddrsLOffset = $074f 203 AreaPointer = $0750 204 205 PlayerEntranceCtrl = $0710 206 GameTimerSetting = $0715 207 AltEntranceControl = $0752 208 EntrancePage = $0751 209 NumberOfPlayers = $077a 210 WarpZoneControl = $06d6 211 ChangeAreaTimer = $06de 212 213 MultiLoopCorrectCntr = $06d9 214 MultiLoopPassCntr = $06da 215 216 FetchNewGameTimerFlag = $0757 217 GameTimerExpiredFlag = $0759 218 219 PrimaryHardMode = $076a 220 SecondaryHardMode = $06cc 221 WorldSelectNumber = $076b 222 WorldSelectEnableFlag = $07fc 223 ContinueWorld = $07fd 224 225 CurrentPlayer = $0753 226 PlayerSize = $0754 227 PlayerStatus = $0756 228 229 OnscreenPlayerInfo = $075a 230 NumberofLives = $075a ;used by current player 231 HalfwayPage = $075b 232 LevelNumber = $075c ;the actual dash number 233 Hidden1UpFlag = $075d 234 CoinTally = $075e 235 WorldNumber = $075f 236 AreaNumber = $0760 ;internal number used to find areas 237 238 CoinTallyFor1Ups = $0748 239 240 OffscreenPlayerInfo = $0761 241 OffScr_NumberofLives = $0761 ;used by offscreen player 242 OffScr_HalfwayPage = $0762 243 OffScr_LevelNumber = $0763 244 OffScr_Hidden1UpFlag = $0764 245 OffScr_CoinTally = $0765 246 OffScr_WorldNumber = $0766 247 OffScr_AreaNumber = $0767 248 249 BalPlatformAlignment = $03a0 250 Platform_X_Scroll = $03a1 251 PlatformCollisionFlag = $03a2 252 YPlatformTopYPos = $0401 253 YPlatformCenterYPos = $58 254 255 BrickCoinTimerFlag = $06bc 256 StarFlagTaskControl = $0746 257 258 PseudoRandomBitReg = $07a7 259 WarmBootValidation = $07ff 260 261 SprShuffleAmtOffset = $06e0 262 SprShuffleAmt = $06e1 263 SprDataOffset = $06e4 264 Player_SprDataOffset = $06e4 265 Enemy_SprDataOffset = $06e5 266 Block_SprDataOffset = $06ec 267 Alt_SprDataOffset = $06ec 268 Bubble_SprDataOffset = $06ee 269 FBall_SprDataOffset = $06f1 270 Misc_SprDataOffset = $06f3 271 SprDataOffset_Ctrl = $03ee 272 273 Player_State = $1d 274 Enemy_State = $1e 275 Fireball_State = $24 276 Block_State = $26 277 Misc_State = $2a 278 279 Player_MovingDir = $45 280 Enemy_MovingDir = $46 281 282 SprObject_X_Speed = $57 283 Player_X_Speed = $57 284 Enemy_X_Speed = $58 285 Fireball_X_Speed = $5e 286 Block_X_Speed = $60 287 Misc_X_Speed = $64 288 289 Jumpspring_FixedYPos = $58 290 JumpspringAnimCtrl = $070e 291 JumpspringForce = $06db 292 293 SprObject_PageLoc = $6d 294 Player_PageLoc = $6d 295 Enemy_PageLoc = $6e 296 Fireball_PageLoc = $74 297 Block_PageLoc = $76 298 Misc_PageLoc = $7a 299 Bubble_PageLoc = $83 300 301 SprObject_X_Position = $86 302 Player_X_Position = $86 303 Enemy_X_Position = $87 304 Fireball_X_Position = $8d 305 Block_X_Position = $8f 306 Misc_X_Position = $93 307 Bubble_X_Position = $9c 308 309 SprObject_Y_Speed = $9f 310 Player_Y_Speed = $9f 311 Enemy_Y_Speed = $a0 312 Fireball_Y_Speed = $a6 313 Block_Y_Speed = $a8 314 Misc_Y_Speed = $ac 315 316 SprObject_Y_HighPos = $b5 317 Player_Y_HighPos = $b5 318 Enemy_Y_HighPos = $b6 319 Fireball_Y_HighPos = $bc 320 Block_Y_HighPos = $be 321 Misc_Y_HighPos = $c2 322 Bubble_Y_HighPos = $cb 323 324 SprObject_Y_Position = $ce 325 Player_Y_Position = $ce 326 Enemy_Y_Position = $cf 327 Fireball_Y_Position = $d5 328 Block_Y_Position = $d7 329 Misc_Y_Position = $db 330 Bubble_Y_Position = $e4 331 332 SprObject_Rel_XPos = $03ad 333 Player_Rel_XPos = $03ad 334 Enemy_Rel_XPos = $03ae 335 Fireball_Rel_XPos = $03af 336 Bubble_Rel_XPos = $03b0 337 Block_Rel_XPos = $03b1 338 Misc_Rel_XPos = $03b3 339 340 SprObject_Rel_YPos = $03b8 341 Player_Rel_YPos = $03b8 342 Enemy_Rel_YPos = $03b9 343 Fireball_Rel_YPos = $03ba 344 Bubble_Rel_YPos = $03bb 345 Block_Rel_YPos = $03bc 346 Misc_Rel_YPos = $03be 347 348 SprObject_SprAttrib = $03c4 349 Player_SprAttrib = $03c4 350 Enemy_SprAttrib = $03c5 351 352 SprObject_X_MoveForce = $0400 353 Enemy_X_MoveForce = $0401 354 355 SprObject_YMF_Dummy = $0416 356 Player_YMF_Dummy = $0416 357 Enemy_YMF_Dummy = $0417 358 Bubble_YMF_Dummy = $042c 359 360 SprObject_Y_MoveForce = $0433 361 Player_Y_MoveForce = $0433 362 Enemy_Y_MoveForce = $0434 363 Block_Y_MoveForce = $043c 364 365 DisableCollisionDet = $0716 366 Player_CollisionBits = $0490 367 Enemy_CollisionBits = $0491 368 369 SprObj_BoundBoxCtrl = $0499 370 Player_BoundBoxCtrl = $0499 371 Enemy_BoundBoxCtrl = $049a 372 Fireball_BoundBoxCtrl = $04a0 373 Misc_BoundBoxCtrl = $04a2 374 375 EnemyFrenzyBuffer = $06cb 376 EnemyFrenzyQueue = $06cd 377 Enemy_Flag = $0f 378 Enemy_ID = $16 379 380 PlayerGfxOffset = $06d5 381 Player_XSpeedAbsolute = $0700 382 FrictionAdderHigh = $0701 383 FrictionAdderLow = $0702 384 RunningSpeed = $0703 385 SwimmingFlag = $0704 386 Player_X_MoveForce = $0705 387 DiffToHaltJump = $0706 388 JumpOrigin_Y_HighPos = $0707 389 JumpOrigin_Y_Position = $0708 390 VerticalForce = $0709 391 VerticalForceDown = $070a 392 PlayerChangeSizeFlag = $070b 393 PlayerAnimTimerSet = $070c 394 PlayerAnimCtrl = $070d 395 DeathMusicLoaded = $0712 396 FlagpoleSoundQueue = $0713 397 CrouchingFlag = $0714 398 MaximumLeftSpeed = $0450 399 MaximumRightSpeed = $0456 400 401 SprObject_OffscrBits = $03d0 402 Player_OffscreenBits = $03d0 403 Enemy_OffscreenBits = $03d1 404 FBall_OffscreenBits = $03d2 405 Bubble_OffscreenBits = $03d3 406 Block_OffscreenBits = $03d4 407 Misc_OffscreenBits = $03d6 408 EnemyOffscrBitsMasked = $03d8 409 410 Cannon_Offset = $046a 411 Cannon_PageLoc = $046b 412 Cannon_X_Position = $0471 413 Cannon_Y_Position = $0477 414 Cannon_Timer = $047d 415 416 Whirlpool_Offset = $046a 417 Whirlpool_PageLoc = $046b 418 Whirlpool_LeftExtent = $0471 419 Whirlpool_Length = $0477 420 Whirlpool_Flag = $047d 421 422 VineFlagOffset = $0398 423 VineHeight = $0399 424 VineObjOffset = $039a 425 VineStart_Y_Position = $039d 426 427 Block_Orig_YPos = $03e4 428 Block_BBuf_Low = $03e6 429 Block_Metatile = $03e8 430 Block_PageLoc2 = $03ea 431 Block_RepFlag = $03ec 432 Block_ResidualCounter = $03f0 433 Block_Orig_XPos = $03f1 434 435 BoundingBox_UL_XPos = $04ac 436 BoundingBox_UL_YPos = $04ad 437 BoundingBox_DR_XPos = $04ae 438 BoundingBox_DR_YPos = $04af 439 BoundingBox_UL_Corner = $04ac 440 BoundingBox_LR_Corner = $04ae 441 EnemyBoundingBoxCoord = $04b0 442 443 PowerUpType = $39 444 445 FireballBouncingFlag = $3a 446 FireballCounter = $06ce 447 FireballThrowingTimer = $0711 448 449 HammerEnemyOffset = $06ae 450 JumpCoinMiscOffset = $06b7 451 452 Block_Buffer_1 = $0500 453 Block_Buffer_2 = $05d0 454 455 HammerThrowingTimer = $03a2 456 HammerBroJumpTimer = $3c 457 Misc_Collision_Flag = $06be 458 459 RedPTroopaOrigXPos = $0401 460 RedPTroopaCenterYPos = $58 461 462 XMovePrimaryCounter = $a0 463 XMoveSecondaryCounter = $58 464 465 CheepCheepMoveMFlag = $58 466 CheepCheepOrigYPos = $0434 467 BitMFilter = $06dd 468 469 LakituReappearTimer = $06d1 470 LakituMoveSpeed = $58 471 LakituMoveDirection = $a0 472 473 FirebarSpinState_Low = $58 474 FirebarSpinState_High = $a0 475 FirebarSpinSpeed = $0388 476 FirebarSpinDirection = $34 477 478 DuplicateObj_Offset = $06cf 479 NumberofGroupEnemies = $06d3 480 481 BlooperMoveCounter = $a0 482 BlooperMoveSpeed = $58 483 484 BowserBodyControls = $0363 485 BowserFeetCounter = $0364 486 BowserMovementSpeed = $0365 487 BowserOrigXPos = $0366 488 BowserFlameTimerCtrl = $0367 489 BowserFront_Offset = $0368 490 BridgeCollapseOffset = $0369 491 BowserGfxFlag = $036a 492 BowserHitPoints = $0483 493 MaxRangeFromOrigin = $06dc 494 495 BowserFlamePRandomOfs = $0417 496 497 PiranhaPlantUpYPos = $0417 498 PiranhaPlantDownYPos = $0434 499 PiranhaPlant_Y_Speed = $58 500 PiranhaPlant_MoveFlag = $a0 501 502 FireworksCounter = $06d7 503 ExplosionGfxCounter = $58 504 ExplosionTimerCounter = $a0 505 506 ;sound related defines 507 Squ2_NoteLenBuffer = $07b3 508 Squ2_NoteLenCounter = $07b4 509 Squ2_EnvelopeDataCtrl = $07b5 510 Squ1_NoteLenCounter = $07b6 511 Squ1_EnvelopeDataCtrl = $07b7 512 Tri_NoteLenBuffer = $07b8 513 Tri_NoteLenCounter = $07b9 514 Noise_BeatLenCounter = $07ba 515 Squ1_SfxLenCounter = $07bb 516 Squ2_SfxLenCounter = $07bd 517 Sfx_SecondaryCounter = $07be 518 Noise_SfxLenCounter = $07bf 519 520 PauseSoundQueue = $fa 521 Square1SoundQueue = $ff 522 Square2SoundQueue = $fe 523 NoiseSoundQueue = $fd 524 AreaMusicQueue = $fb 525 EventMusicQueue = $fc 526 527 Square1SoundBuffer = $f1 528 Square2SoundBuffer = $f2 529 NoiseSoundBuffer = $f3 530 AreaMusicBuffer = $f4 531 EventMusicBuffer = $07b1 532 PauseSoundBuffer = $07b2 533 534 MusicData = $f5 535 MusicDataLow = $f5 536 MusicDataHigh = $f6 537 MusicOffset_Square2 = $f7 538 MusicOffset_Square1 = $f8 539 MusicOffset_Triangle = $f9 540 MusicOffset_Noise = $07b0 541 542 NoteLenLookupTblOfs = $f0 543 DAC_Counter = $07c0 544 NoiseDataLoopbackOfs = $07c1 545 NoteLengthTblAdder = $07c4 546 AreaMusicBuffer_Alt = $07c5 547 PauseModeFlag = $07c6 548 GroundMusicHeaderOfs = $07c7 549 AltRegContentFlag = $07ca 550 551 ;------------------------------------------------------------------------------------- 552 ;CONSTANTS 553 554 ;sound effects constants 555 Sfx_SmallJump = %10000000 556 Sfx_Flagpole = %01000000 557 Sfx_Fireball = %00100000 558 Sfx_PipeDown_Injury = %00010000 559 Sfx_EnemySmack = %00001000 560 Sfx_EnemyStomp = %00000100 561 Sfx_Bump = %00000010 562 Sfx_BigJump = %00000001 563 564 Sfx_BowserFall = %10000000 565 Sfx_ExtraLife = %01000000 566 Sfx_PowerUpGrab = %00100000 567 Sfx_TimerTick = %00010000 568 Sfx_Blast = %00001000 569 Sfx_GrowVine = %00000100 570 Sfx_GrowPowerUp = %00000010 571 Sfx_CoinGrab = %00000001 572 573 Sfx_BowserFlame = %00000010 574 Sfx_BrickShatter = %00000001 575 576 ;music constants 577 Silence = %10000000 578 579 StarPowerMusic = %01000000 580 PipeIntroMusic = %00100000 581 CloudMusic = %00010000 582 CastleMusic = %00001000 583 UndergroundMusic = %00000100 584 WaterMusic = %00000010 585 GroundMusic = %00000001 586 587 TimeRunningOutMusic = %01000000 588 EndOfLevelMusic = %00100000 589 AltGameOverMusic = %00010000 590 EndOfCastleMusic = %00001000 591 VictoryMusic = %00000100 592 GameOverMusic = %00000010 593 DeathMusic = %00000001 594 595 ;enemy object constants 596 GreenKoopa = $00 597 BuzzyBeetle = $02 598 RedKoopa = $03 599 HammerBro = $05 600 Goomba = $06 601 Bloober = $07 602 BulletBill_FrenzyVar = $08 603 GreyCheepCheep = $0a 604 RedCheepCheep = $0b 605 Podoboo = $0c 606 PiranhaPlant = $0d 607 GreenParatroopaJump = $0e 608 RedParatroopa = $0f 609 GreenParatroopaFly = $10 610 Lakitu = $11 611 Spiny = $12 612 FlyCheepCheepFrenzy = $14 613 FlyingCheepCheep = $14 614 BowserFlame = $15 615 Fireworks = $16 616 BBill_CCheep_Frenzy = $17 617 Stop_Frenzy = $18 618 Bowser = $2d 619 PowerUpObject = $2e 620 VineObject = $2f 621 FlagpoleFlagObject = $30 622 StarFlagObject = $31 623 JumpspringObject = $32 624 BulletBill_CannonVar = $33 625 RetainerObject = $35 626 TallEnemy = $09 627 628 ;other constants 629 World1 = 0 630 World2 = 1 631 World3 = 2 632 World4 = 3 633 World5 = 4 634 World6 = 5 635 World7 = 6 636 World8 = 7 637 Level1 = 0 638 Level2 = 1 639 Level3 = 2 640 Level4 = 3 641 642 WarmBootOffset = <$07d6 643 ColdBootOffset = <$07fe 644 TitleScreenDataOffset = $1ec0 645 SoundMemory = $07b0 646 SwimTileRepOffset = PlayerGraphicsTable + $9e 647 MusicHeaderOffsetData = MusicHeaderData - 1 648 MHD = MusicHeaderData 649 650 A_Button = %10000000 651 B_Button = %01000000 652 Select_Button = %00100000 653 Start_Button = %00010000 654 Up_Dir = %00001000 655 Down_Dir = %00000100 656 Left_Dir = %00000010 657 Right_Dir = %00000001 658 659 TitleScreenModeValue = 0 660 GameModeValue = 1 661 VictoryModeValue = 2 662 GameOverModeValue = 3 663 664 ;------------------------------------------------------------------------------------- 665 ;DIRECTIVES 666 667 .index 8 668 .mem 8 669 670 .org $8000 671 672 ;------------------------------------------------------------------------------------- 673 674 Start: 675 sei ;pretty standard 6502 type init here 676 cld 677 lda #%00010000 ;init PPU control register 1 678 sta PPU_CTRL_REG1 679 ldx #$ff ;reset stack pointer 680 txs 681 VBlank1: lda PPU_STATUS ;wait two frames 682 bpl VBlank1 683 VBlank2: lda PPU_STATUS 684 bpl VBlank2 685 ldy #ColdBootOffset ;load default cold boot pointer 686 ldx #$05 ;this is where we check for a warm boot 687 WBootCheck: lda TopScoreDisplay,x ;check each score digit in the top score 688 cmp #10 ;to see if we have a valid digit 689 bcs ColdBoot ;if not, give up and proceed with cold boot 690 dex 691 bpl WBootCheck 692 lda WarmBootValidation ;second checkpoint, check to see if 693 cmp #$a5 ;another location has a specific value 694 bne ColdBoot 695 ldy #WarmBootOffset ;if passed both, load warm boot pointer 696 ColdBoot: jsr InitializeMemory ;clear memory using pointer in Y 697 sta SND_DELTA_REG+1 ;reset delta counter load register 698 sta OperMode ;reset primary mode of operation 699 lda #$a5 ;set warm boot flag 700 sta WarmBootValidation 701 sta PseudoRandomBitReg ;set seed for pseudorandom register 702 lda #%00001111 703 sta SND_MASTERCTRL_REG ;enable all sound channels except dmc 704 lda #%00000110 705 sta PPU_CTRL_REG2 ;turn off clipping for OAM and background 706 jsr MoveAllSpritesOffscreen 707 jsr InitializeNameTables ;initialize both name tables 708 inc DisableScreenFlag ;set flag to disable screen output 709 lda Mirror_PPU_CTRL_REG1 710 ora #%10000000 ;enable NMIs 711 jsr WritePPUReg1 712 EndlessLoop: jmp EndlessLoop ;endless loop, need I say more? 713 714 ;------------------------------------------------------------------------------------- 715 ;$00 - vram buffer address table low, also used for pseudorandom bit 716 ;$01 - vram buffer address table high 717 718 VRAM_AddrTable_Low: 719 .db <VRAM_Buffer1, <WaterPaletteData, <GroundPaletteData 720 .db <UndergroundPaletteData, <CastlePaletteData, <VRAM_Buffer1_Offset 721 .db <VRAM_Buffer2, <VRAM_Buffer2, <BowserPaletteData 722 .db <DaySnowPaletteData, <NightSnowPaletteData, <MushroomPaletteData 723 .db <MarioThanksMessage, <LuigiThanksMessage, <MushroomRetainerSaved 724 .db <PrincessSaved1, <PrincessSaved2, <WorldSelectMessage1 725 .db <WorldSelectMessage2 726 727 VRAM_AddrTable_High: 728 .db >VRAM_Buffer1, >WaterPaletteData, >GroundPaletteData 729 .db >UndergroundPaletteData, >CastlePaletteData, >VRAM_Buffer1_Offset 730 .db >VRAM_Buffer2, >VRAM_Buffer2, >BowserPaletteData 731 .db >DaySnowPaletteData, >NightSnowPaletteData, >MushroomPaletteData 732 .db >MarioThanksMessage, >LuigiThanksMessage, >MushroomRetainerSaved 733 .db >PrincessSaved1, >PrincessSaved2, >WorldSelectMessage1 734 .db >WorldSelectMessage2 735 736 VRAM_Buffer_Offset: 737 .db <VRAM_Buffer1_Offset, <VRAM_Buffer2_Offset 738 739 NonMaskableInterrupt: 740 lda Mirror_PPU_CTRL_REG1 ;disable NMIs in mirror reg 741 and #%01111111 ;save all other bits 742 sta Mirror_PPU_CTRL_REG1 743 and #%01111110 ;alter name table address to be $2800 744 sta PPU_CTRL_REG1 ;(essentially $2000) but save other bits 745 lda Mirror_PPU_CTRL_REG2 ;disable OAM and background display by default 746 and #%11100110 747 ldy DisableScreenFlag ;get screen disable flag 748 bne ScreenOff ;if set, used bits as-is 749 lda Mirror_PPU_CTRL_REG2 ;otherwise reenable bits and save them 750 ora #%00011110 751 ScreenOff: sta Mirror_PPU_CTRL_REG2 ;save bits for later but not in register at the moment 752 and #%11100111 ;disable screen for now 753 sta PPU_CTRL_REG2 754 ldx PPU_STATUS ;reset flip-flop and reset scroll registers to zero 755 lda #$00 756 jsr InitScroll 757 sta PPU_SPR_ADDR ;reset spr-ram address register 758 lda #$02 ;perform spr-ram DMA access on $0200-$02ff 759 sta SPR_DMA 760 ldx VRAM_Buffer_AddrCtrl ;load control for pointer to buffer contents 761 lda VRAM_AddrTable_Low,x ;set indirect at $00 to pointer 762 sta $00 763 lda VRAM_AddrTable_High,x 764 sta $01 765 jsr UpdateScreen ;update screen with buffer contents 766 ldy #$00 767 ldx VRAM_Buffer_AddrCtrl ;check for usage of $0341 768 cpx #$06 769 bne InitBuffer 770 iny ;get offset based on usage 771 InitBuffer: ldx VRAM_Buffer_Offset,y 772 lda #$00 ;clear buffer header at last location 773 sta VRAM_Buffer1_Offset,x 774 sta VRAM_Buffer1,x 775 sta VRAM_Buffer_AddrCtrl ;reinit address control to $0301 776 lda Mirror_PPU_CTRL_REG2 ;copy mirror of $2001 to register 777 sta PPU_CTRL_REG2 778 jsr SoundEngine ;play sound 779 jsr ReadJoypads ;read joypads 780 jsr PauseRoutine ;handle pause 781 jsr UpdateTopScore 782 lda GamePauseStatus ;check for pause status 783 lsr 784 bcs PauseSkip 785 lda TimerControl ;if master timer control not set, decrement 786 beq DecTimers ;all frame and interval timers 787 dec TimerControl 788 bne NoDecTimers 789 DecTimers: ldx #$14 ;load end offset for end of frame timers 790 dec IntervalTimerControl ;decrement interval timer control, 791 bpl DecTimersLoop ;if not expired, only frame timers will decrement 792 lda #$14 793 sta IntervalTimerControl ;if control for interval timers expired, 794 ldx #$23 ;interval timers will decrement along with frame timers 795 DecTimersLoop: lda Timers,x ;check current timer 796 beq SkipExpTimer ;if current timer expired, branch to skip, 797 dec Timers,x ;otherwise decrement the current timer 798 SkipExpTimer: dex ;move onto next timer 799 bpl DecTimersLoop ;do this until all timers are dealt with 800 NoDecTimers: inc FrameCounter ;increment frame counter 801 PauseSkip: ldx #$00 802 ldy #$07 803 lda PseudoRandomBitReg ;get first memory location of LSFR bytes 804 and #%00000010 ;mask out all but d1 805 sta $00 ;save here 806 lda PseudoRandomBitReg+1 ;get second memory location 807 and #%00000010 ;mask out all but d1 808 eor $00 ;perform exclusive-OR on d1 from first and second bytes 809 clc ;if neither or both are set, carry will be clear 810 beq RotPRandomBit 811 sec ;if one or the other is set, carry will be set 812 RotPRandomBit: ror PseudoRandomBitReg,x ;rotate carry into d7, and rotate last bit into carry 813 inx ;increment to next byte 814 dey ;decrement for loop 815 bne RotPRandomBit 816 lda Sprite0HitDetectFlag ;check for flag here 817 beq SkipSprite0 818 Sprite0Clr: lda PPU_STATUS ;wait for sprite 0 flag to clear, which will 819 and #%01000000 ;not happen until vblank has ended 820 bne Sprite0Clr 821 lda GamePauseStatus ;if in pause mode, do not bother with sprites at all 822 lsr 823 bcs Sprite0Hit 824 jsr MoveSpritesOffscreen 825 jsr SpriteShuffler 826 Sprite0Hit: lda PPU_STATUS ;do sprite #0 hit detection 827 and #%01000000 828 beq Sprite0Hit 829 ldy #$14 ;small delay, to wait until we hit horizontal blank time 830 HBlankDelay: dey 831 bne HBlankDelay 832 SkipSprite0: lda HorizontalScroll ;set scroll registers from variables 833 sta PPU_SCROLL_REG 834 lda VerticalScroll 835 sta PPU_SCROLL_REG 836 lda Mirror_PPU_CTRL_REG1 ;load saved mirror of $2000 837 pha 838 sta PPU_CTRL_REG1 839 lda GamePauseStatus ;if in pause mode, do not perform operation mode stuff 840 lsr 841 bcs SkipMainOper 842 jsr OperModeExecutionTree ;otherwise do one of many, many possible subroutines 843 SkipMainOper: lda PPU_STATUS ;reset flip-flop 844 pla 845 ora #%10000000 ;reactivate NMIs 846 sta PPU_CTRL_REG1 847 rti ;we are done until the next frame! 848 849 ;------------------------------------------------------------------------------------- 850 851 PauseRoutine: 852 lda OperMode ;are we in victory mode? 853 cmp #VictoryModeValue ;if so, go ahead 854 beq ChkPauseTimer 855 cmp #GameModeValue ;are we in game mode? 856 bne ExitPause ;if not, leave 857 lda OperMode_Task ;if we are in game mode, are we running game engine? 858 cmp #$03 859 bne ExitPause ;if not, leave 860 ChkPauseTimer: lda GamePauseTimer ;check if pause timer is still counting down 861 beq ChkStart 862 dec GamePauseTimer ;if so, decrement and leave 863 rts 864 ChkStart: lda SavedJoypad1Bits ;check to see if start is pressed 865 and #Start_Button ;on controller 1 866 beq ClrPauseTimer 867 lda GamePauseStatus ;check to see if timer flag is set 868 and #%10000000 ;and if so, do not reset timer (residual, 869 bne ExitPause ;joypad reading routine makes this unnecessary) 870 lda #$2b ;set pause timer 871 sta GamePauseTimer 872 lda GamePauseStatus 873 tay 874 iny ;set pause sfx queue for next pause mode 875 sty PauseSoundQueue 876 eor #%00000001 ;invert d0 and set d7 877 ora #%10000000 878 bne SetPause ;unconditional branch 879 ClrPauseTimer: lda GamePauseStatus ;clear timer flag if timer is at zero and start button 880 and #%01111111 ;is not pressed 881 SetPause: sta GamePauseStatus 882 ExitPause: rts 883 884 ;------------------------------------------------------------------------------------- 885 ;$00 - used for preset value 886 887 SpriteShuffler: 888 ldy AreaType ;load level type, likely residual code 889 lda #$28 ;load preset value which will put it at 890 sta $00 ;sprite #10 891 ldx #$0e ;start at the end of OAM data offsets 892 ShuffleLoop: lda SprDataOffset,x ;check for offset value against 893 cmp $00 ;the preset value 894 bcc NextSprOffset ;if less, skip this part 895 ldy SprShuffleAmtOffset ;get current offset to preset value we want to add 896 clc 897 adc SprShuffleAmt,y ;get shuffle amount, add to current sprite offset 898 bcc StrSprOffset ;if not exceeded $ff, skip second add 899 clc 900 adc $00 ;otherwise add preset value $28 to offset 901 StrSprOffset: sta SprDataOffset,x ;store new offset here or old one if branched to here 902 NextSprOffset: dex ;move backwards to next one 903 bpl ShuffleLoop 904 ldx SprShuffleAmtOffset ;load offset 905 inx 906 cpx #$03 ;check if offset + 1 goes to 3 907 bne SetAmtOffset ;if offset + 1 not 3, store 908 ldx #$00 ;otherwise, init to 0 909 SetAmtOffset: stx SprShuffleAmtOffset 910 ldx #$08 ;load offsets for values and storage 911 ldy #$02 912 SetMiscOffset: lda SprDataOffset+5,y ;load one of three OAM data offsets 913 sta Misc_SprDataOffset-2,x ;store first one unmodified, but 914 clc ;add eight to the second and eight 915 adc #$08 ;more to the third one 916 sta Misc_SprDataOffset-1,x ;note that due to the way X is set up, 917 clc ;this code loads into the misc sprite offsets 918 adc #$08 919 sta Misc_SprDataOffset,x 920 dex 921 dex 922 dex 923 dey 924 bpl SetMiscOffset ;do this until all misc spr offsets are loaded 925 rts 926 927 ;------------------------------------------------------------------------------------- 928 929 OperModeExecutionTree: 930 lda OperMode ;this is the heart of the entire program, 931 jsr JumpEngine ;most of what goes on starts here 932 933 .dw TitleScreenMode 934 .dw GameMode 935 .dw VictoryMode 936 .dw GameOverMode 937 938 ;------------------------------------------------------------------------------------- 939 940 MoveAllSpritesOffscreen: 941 ldy #$00 ;this routine moves all sprites off the screen 942 .db $2c ;BIT instruction opcode 943 944 MoveSpritesOffscreen: 945 ldy #$04 ;this routine moves all but sprite 0 946 lda #$f8 ;off the screen 947 SprInitLoop: sta Sprite_Y_Position,y ;write 248 into OAM data's Y coordinate 948 iny ;which will move it off the screen 949 iny 950 iny 951 iny 952 bne SprInitLoop 953 rts 954 955 ;------------------------------------------------------------------------------------- 956 957 TitleScreenMode: 958 lda OperMode_Task 959 jsr JumpEngine 960 961 .dw InitializeGame 962 .dw ScreenRoutines 963 .dw PrimaryGameSetup 964 .dw GameMenuRoutine 965 966 ;------------------------------------------------------------------------------------- 967 968 WSelectBufferTemplate: 969 .db $04, $20, $73, $01, $00, $00 970 971 GameMenuRoutine: 972 ldy #$00 973 lda SavedJoypad1Bits ;check to see if either player pressed 974 ora SavedJoypad2Bits ;only the start button (either joypad) 975 cmp #Start_Button 976 beq StartGame 977 cmp #A_Button+Start_Button ;check to see if A + start was pressed 978 bne ChkSelect ;if not, branch to check select button 979 StartGame: jmp ChkContinue ;if either start or A + start, execute here 980 ChkSelect: cmp #Select_Button ;check to see if the select button was pressed 981 beq SelectBLogic ;if so, branch reset demo timer 982 ldx DemoTimer ;otherwise check demo timer 983 bne ChkWorldSel ;if demo timer not expired, branch to check world selection 984 sta SelectTimer ;set controller bits here if running demo 985 jsr DemoEngine ;run through the demo actions 986 bcs ResetTitle ;if carry flag set, demo over, thus branch 987 jmp RunDemo ;otherwise, run game engine for demo 988 ChkWorldSel: ldx WorldSelectEnableFlag ;check to see if world selection has been enabled 989 beq NullJoypad 990 cmp #B_Button ;if so, check to see if the B button was pressed 991 bne NullJoypad 992 iny ;if so, increment Y and execute same code as select 993 SelectBLogic: lda DemoTimer ;if select or B pressed, check demo timer one last time 994 beq ResetTitle ;if demo timer expired, branch to reset title screen mode 995 lda #$18 ;otherwise reset demo timer 996 sta DemoTimer 997 lda SelectTimer ;check select/B button timer 998 bne NullJoypad ;if not expired, branch 999 lda #$10 ;otherwise reset select button timer 1000 sta SelectTimer 1001 cpy #$01 ;was the B button pressed earlier? if so, branch 1002 beq IncWorldSel ;note this will not be run if world selection is disabled 1003 lda NumberOfPlayers ;if no, must have been the select button, therefore 1004 eor #%00000001 ;change number of players and draw icon accordingly 1005 sta NumberOfPlayers 1006 jsr DrawMushroomIcon 1007 jmp NullJoypad 1008 IncWorldSel: ldx WorldSelectNumber ;increment world select number 1009 inx 1010 txa 1011 and #%00000111 ;mask out higher bits 1012 sta WorldSelectNumber ;store as current world select number 1013 jsr GoContinue 1014 UpdateShroom: lda WSelectBufferTemplate,x ;write template for world select in vram buffer 1015 sta VRAM_Buffer1-1,x ;do this until all bytes are written 1016 inx 1017 cpx #$06 1018 bmi UpdateShroom 1019 ldy WorldNumber ;get world number from variable and increment for 1020 iny ;proper display, and put in blank byte before 1021 sty VRAM_Buffer1+3 ;null terminator 1022 NullJoypad: lda #$00 ;clear joypad bits for player 1 1023 sta SavedJoypad1Bits 1024 RunDemo: jsr GameCoreRoutine ;run game engine 1025 lda GameEngineSubroutine ;check to see if we're running lose life routine 1026 cmp #$06 1027 bne ExitMenu ;if not, do not do all the resetting below 1028 ResetTitle: lda #$00 ;reset game modes, disable 1029 sta OperMode ;sprite 0 check and disable 1030 sta OperMode_Task ;screen output 1031 sta Sprite0HitDetectFlag 1032 inc DisableScreenFlag 1033 rts 1034 ChkContinue: ldy DemoTimer ;if timer for demo has expired, reset modes 1035 beq ResetTitle 1036 asl ;check to see if A button was also pushed 1037 bcc StartWorld1 ;if not, don't load continue function's world number 1038 lda ContinueWorld ;load previously saved world number for secret 1039 jsr GoContinue ;continue function when pressing A + start 1040 StartWorld1: jsr LoadAreaPointer 1041 inc Hidden1UpFlag ;set 1-up box flag for both players 1042 inc OffScr_Hidden1UpFlag 1043 inc FetchNewGameTimerFlag ;set fetch new game timer flag 1044 inc OperMode ;set next game mode 1045 lda WorldSelectEnableFlag ;if world select flag is on, then primary 1046 sta PrimaryHardMode ;hard mode must be on as well 1047 lda #$00 1048 sta OperMode_Task ;set game mode here, and clear demo timer 1049 sta DemoTimer 1050 ldx #$17 1051 lda #$00 1052 InitScores: sta ScoreAndCoinDisplay,x ;clear player scores and coin displays 1053 dex 1054 bpl InitScores 1055 ExitMenu: rts 1056 GoContinue: sta WorldNumber ;start both players at the first area 1057 sta OffScr_WorldNumber ;of the previously saved world number 1058 ldx #$00 ;note that on power-up using this function 1059 stx AreaNumber ;will make no difference 1060 stx OffScr_AreaNumber 1061 rts 1062 1063 ;------------------------------------------------------------------------------------- 1064 1065 MushroomIconData: 1066 .db $07, $22, $49, $83, $ce, $24, $24, $00 1067 1068 DrawMushroomIcon: 1069 ldy #$07 ;read eight bytes to be read by transfer routine 1070 IconDataRead: lda MushroomIconData,y ;note that the default position is set for a 1071 sta VRAM_Buffer1-1,y ;1-player game 1072 dey 1073 bpl IconDataRead 1074 lda NumberOfPlayers ;check number of players 1075 beq ExitIcon ;if set to 1-player game, we're done 1076 lda #$24 ;otherwise, load blank tile in 1-player position 1077 sta VRAM_Buffer1+3 1078 lda #$ce ;then load shroom icon tile in 2-player position 1079 sta VRAM_Buffer1+5 1080 ExitIcon: rts 1081 1082 ;------------------------------------------------------------------------------------- 1083 1084 DemoActionData: 1085 .db $01, $80, $02, $81, $41, $80, $01 1086 .db $42, $c2, $02, $80, $41, $c1, $41, $c1 1087 .db $01, $c1, $01, $02, $80, $00 1088 1089 DemoTimingData: 1090 .db $9b, $10, $18, $05, $2c, $20, $24 1091 .db $15, $5a, $10, $20, $28, $30, $20, $10 1092 .db $80, $20, $30, $30, $01, $ff, $00 1093 1094 DemoEngine: 1095 ldx DemoAction ;load current demo action 1096 lda DemoActionTimer ;load current action timer 1097 bne DoAction ;if timer still counting down, skip 1098 inx 1099 inc DemoAction ;if expired, increment action, X, and 1100 sec ;set carry by default for demo over 1101 lda DemoTimingData-1,x ;get next timer 1102 sta DemoActionTimer ;store as current timer 1103 beq DemoOver ;if timer already at zero, skip 1104 DoAction: lda DemoActionData-1,x ;get and perform action (current or next) 1105 sta SavedJoypad1Bits 1106 dec DemoActionTimer ;decrement action timer 1107 clc ;clear carry if demo still going 1108 DemoOver: rts 1109 1110 ;------------------------------------------------------------------------------------- 1111 1112 VictoryMode: 1113 jsr VictoryModeSubroutines ;run victory mode subroutines 1114 lda OperMode_Task ;get current task of victory mode 1115 beq AutoPlayer ;if on bridge collapse, skip enemy processing 1116 ldx #$00 1117 stx ObjectOffset ;otherwise reset enemy object offset 1118 jsr EnemiesAndLoopsCore ;and run enemy code 1119 AutoPlayer: jsr RelativePlayerPosition ;get player's relative coordinates 1120 jmp PlayerGfxHandler ;draw the player, then leave 1121 1122 VictoryModeSubroutines: 1123 lda OperMode_Task 1124 jsr JumpEngine 1125 1126 .dw BridgeCollapse 1127 .dw SetupVictoryMode 1128 .dw PlayerVictoryWalk 1129 .dw PrintVictoryMessages 1130 .dw PlayerEndWorld 1131 1132 ;------------------------------------------------------------------------------------- 1133 1134 SetupVictoryMode: 1135 ldx ScreenRight_PageLoc ;get page location of right side of screen 1136 inx ;increment to next page 1137 stx DestinationPageLoc ;store here 1138 lda #EndOfCastleMusic 1139 sta EventMusicQueue ;play win castle music 1140 jmp IncModeTask_B ;jump to set next major task in victory mode 1141 1142 ;------------------------------------------------------------------------------------- 1143 1144 PlayerVictoryWalk: 1145 ldy #$00 ;set value here to not walk player by default 1146 sty VictoryWalkControl 1147 lda Player_PageLoc ;get player's page location 1148 cmp DestinationPageLoc ;compare with destination page location 1149 bne PerformWalk ;if page locations don't match, branch 1150 lda Player_X_Position ;otherwise get player's horizontal position 1151 cmp #$60 ;compare with preset horizontal position 1152 bcs DontWalk ;if still on other page, branch ahead 1153 PerformWalk: inc VictoryWalkControl ;otherwise increment value and Y 1154 iny ;note Y will be used to walk the player 1155 DontWalk: tya ;put contents of Y in A and 1156 jsr AutoControlPlayer ;use A to move player to the right or not 1157 lda ScreenLeft_PageLoc ;check page location of left side of screen 1158 cmp DestinationPageLoc ;against set value here 1159 beq ExitVWalk ;branch if equal to change modes if necessary 1160 lda ScrollFractional 1161 clc ;do fixed point math on fractional part of scroll 1162 adc #$80 1163 sta ScrollFractional ;save fractional movement amount 1164 lda #$01 ;set 1 pixel per frame 1165 adc #$00 ;add carry from previous addition 1166 tay ;use as scroll amount 1167 jsr ScrollScreen ;do sub to scroll the screen 1168 jsr UpdScrollVar ;do another sub to update screen and scroll variables 1169 inc VictoryWalkControl ;increment value to stay in this routine 1170 ExitVWalk: lda VictoryWalkControl ;load value set here 1171 beq IncModeTask_A ;if zero, branch to change modes 1172 rts ;otherwise leave 1173 1174 ;------------------------------------------------------------------------------------- 1175 1176 PrintVictoryMessages: 1177 lda SecondaryMsgCounter ;load secondary message counter 1178 bne IncMsgCounter ;if set, branch to increment message counters 1179 lda PrimaryMsgCounter ;otherwise load primary message counter 1180 beq ThankPlayer ;if set to zero, branch to print first message 1181 cmp #$09 ;if at 9 or above, branch elsewhere (this comparison 1182 bcs IncMsgCounter ;is residual code, counter never reaches 9) 1183 ldy WorldNumber ;check world number 1184 cpy #World8 1185 bne MRetainerMsg ;if not at world 8, skip to next part 1186 cmp #$03 ;check primary message counter again 1187 bcc IncMsgCounter ;if not at 3 yet (world 8 only), branch to increment 1188 sbc #$01 ;otherwise subtract one 1189 jmp ThankPlayer ;and skip to next part 1190 MRetainerMsg: cmp #$02 ;check primary message counter 1191 bcc IncMsgCounter ;if not at 2 yet (world 1-7 only), branch 1192 ThankPlayer: tay ;put primary message counter into Y 1193 bne SecondPartMsg ;if counter nonzero, skip this part, do not print first message 1194 lda CurrentPlayer ;otherwise get player currently on the screen 1195 beq EvalForMusic ;if mario, branch 1196 iny ;otherwise increment Y once for luigi and 1197 bne EvalForMusic ;do an unconditional branch to the same place 1198 SecondPartMsg: iny ;increment Y to do world 8's message 1199 lda WorldNumber 1200 cmp #World8 ;check world number 1201 beq EvalForMusic ;if at world 8, branch to next part 1202 dey ;otherwise decrement Y for world 1-7's message 1203 cpy #$04 ;if counter at 4 (world 1-7 only) 1204 bcs SetEndTimer ;branch to set victory end timer 1205 cpy #$03 ;if counter at 3 (world 1-7 only) 1206 bcs IncMsgCounter ;branch to keep counting 1207 EvalForMusic: cpy #$03 ;if counter not yet at 3 (world 8 only), branch 1208 bne PrintMsg ;to print message only (note world 1-7 will only 1209 lda #VictoryMusic ;reach this code if counter = 0, and will always branch) 1210 sta EventMusicQueue ;otherwise load victory music first (world 8 only) 1211 PrintMsg: tya ;put primary message counter in A 1212 clc ;add $0c or 12 to counter thus giving an appropriate value, 1213 adc #$0c ;($0c-$0d = first), ($0e = world 1-7's), ($0f-$12 = world 8's) 1214 sta VRAM_Buffer_AddrCtrl ;write message counter to vram address controller 1215 IncMsgCounter: lda SecondaryMsgCounter 1216 clc 1217 adc #$04 ;add four to secondary message counter 1218 sta SecondaryMsgCounter 1219 lda PrimaryMsgCounter 1220 adc #$00 ;add carry to primary message counter 1221 sta PrimaryMsgCounter 1222 cmp #$07 ;check primary counter one more time 1223 SetEndTimer: bcc ExitMsgs ;if not reached value yet, branch to leave 1224 lda #$06 1225 sta WorldEndTimer ;otherwise set world end timer 1226 IncModeTask_A: inc OperMode_Task ;move onto next task in mode 1227 ExitMsgs: rts ;leave 1228 1229 ;------------------------------------------------------------------------------------- 1230 1231 PlayerEndWorld: 1232 lda WorldEndTimer ;check to see if world end timer expired 1233 bne EndExitOne ;branch to leave if not 1234 ldy WorldNumber ;check world number 1235 cpy #World8 ;if on world 8, player is done with game, 1236 bcs EndChkBButton ;thus branch to read controller 1237 lda #$00 1238 sta AreaNumber ;otherwise initialize area number used as offset 1239 sta LevelNumber ;and level number control to start at area 1 1240 sta OperMode_Task ;initialize secondary mode of operation 1241 inc WorldNumber ;increment world number to move onto the next world 1242 jsr LoadAreaPointer ;get area address offset for the next area 1243 inc FetchNewGameTimerFlag ;set flag to load game timer from header 1244 lda #GameModeValue 1245 sta OperMode ;set mode of operation to game mode 1246 EndExitOne: rts ;and leave 1247 EndChkBButton: lda SavedJoypad1Bits 1248 ora SavedJoypad2Bits ;check to see if B button was pressed on 1249 and #B_Button ;either controller 1250 beq EndExitTwo ;branch to leave if not 1251 lda #$01 ;otherwise set world selection flag 1252 sta WorldSelectEnableFlag 1253 lda #$ff ;remove onscreen player's lives 1254 sta NumberofLives 1255 jsr TerminateGame ;do sub to continue other player or end game 1256 EndExitTwo: rts ;leave 1257 1258 ;------------------------------------------------------------------------------------- 1259 1260 ;data is used as tiles for numbers 1261 ;that appear when you defeat enemies 1262 FloateyNumTileData: 1263 .db $ff, $ff ;dummy 1264 .db $f6, $fb ; "100" 1265 .db $f7, $fb ; "200" 1266 .db $f8, $fb ; "400" 1267 .db $f9, $fb ; "500" 1268 .db $fa, $fb ; "800" 1269 .db $f6, $50 ; "1000" 1270 .db $f7, $50 ; "2000" 1271 .db $f8, $50 ; "4000" 1272 .db $f9, $50 ; "5000" 1273 .db $fa, $50 ; "8000" 1274 .db $fd, $fe ; "1-UP" 1275 1276 ;high nybble is digit number, low nybble is number to 1277 ;add to the digit of the player's score 1278 ScoreUpdateData: 1279 .db $ff ;dummy 1280 .db $41, $42, $44, $45, $48 1281 .db $31, $32, $34, $35, $38, $00 1282 1283 FloateyNumbersRoutine: 1284 lda FloateyNum_Control,x ;load control for floatey number 1285 beq EndExitOne ;if zero, branch to leave 1286 cmp #$0b ;if less than $0b, branch 1287 bcc ChkNumTimer 1288 lda #$0b ;otherwise set to $0b, thus keeping 1289 sta FloateyNum_Control,x ;it in range 1290 ChkNumTimer: tay ;use as Y 1291 lda FloateyNum_Timer,x ;check value here 1292 bne DecNumTimer ;if nonzero, branch ahead 1293 sta FloateyNum_Control,x ;initialize floatey number control and leave 1294 rts 1295 DecNumTimer: dec FloateyNum_Timer,x ;decrement value here 1296 cmp #$2b ;if not reached a certain point, branch 1297 bne ChkTallEnemy 1298 cpy #$0b ;check offset for $0b 1299 bne LoadNumTiles ;branch ahead if not found 1300 inc NumberofLives ;give player one extra life (1-up) 1301 lda #Sfx_ExtraLife 1302 sta Square2SoundQueue ;and play the 1-up sound 1303 LoadNumTiles: lda ScoreUpdateData,y ;load point value here 1304 lsr ;move high nybble to low 1305 lsr 1306 lsr 1307 lsr 1308 tax ;use as X offset, essentially the digit 1309 lda ScoreUpdateData,y ;load again and this time 1310 and #%00001111 ;mask out the high nybble 1311 sta DigitModifier,x ;store as amount to add to the digit 1312 jsr AddToScore ;update the score accordingly 1313 ChkTallEnemy: ldy Enemy_SprDataOffset,x ;get OAM data offset for enemy object 1314 lda Enemy_ID,x ;get enemy object identifier 1315 cmp #Spiny 1316 beq FloateyPart ;branch if spiny 1317 cmp #PiranhaPlant 1318 beq FloateyPart ;branch if piranha plant 1319 cmp #HammerBro 1320 beq GetAltOffset ;branch elsewhere if hammer bro 1321 cmp #GreyCheepCheep 1322 beq FloateyPart ;branch if cheep-cheep of either color 1323 cmp #RedCheepCheep 1324 beq FloateyPart 1325 cmp #TallEnemy 1326 bcs GetAltOffset ;branch elsewhere if enemy object => $09 1327 lda Enemy_State,x 1328 cmp #$02 ;if enemy state defeated or otherwise 1329 bcs FloateyPart ;$02 or greater, branch beyond this part 1330 GetAltOffset: ldx SprDataOffset_Ctrl ;load some kind of control bit 1331 ldy Alt_SprDataOffset,x ;get alternate OAM data offset 1332 ldx ObjectOffset ;get enemy object offset again 1333 FloateyPart: lda FloateyNum_Y_Pos,x ;get vertical coordinate for 1334 cmp #$18 ;floatey number, if coordinate in the 1335 bcc SetupNumSpr ;status bar, branch 1336 sbc #$01 1337 sta FloateyNum_Y_Pos,x ;otherwise subtract one and store as new 1338 SetupNumSpr: lda FloateyNum_Y_Pos,x ;get vertical coordinate 1339 sbc #$08 ;subtract eight and dump into the 1340 jsr DumpTwoSpr ;left and right sprite's Y coordinates 1341 lda FloateyNum_X_Pos,x ;get horizontal coordinate 1342 sta Sprite_X_Position,y ;store into X coordinate of left sprite 1343 clc 1344 adc #$08 ;add eight pixels and store into X 1345 sta Sprite_X_Position+4,y ;coordinate of right sprite 1346 lda #$02 1347 sta Sprite_Attributes,y ;set palette control in attribute bytes 1348 sta Sprite_Attributes+4,y ;of left and right sprites 1349 lda FloateyNum_Control,x 1350 asl ;multiply our floatey number control by 2 1351 tax ;and use as offset for look-up table 1352 lda FloateyNumTileData,x 1353 sta Sprite_Tilenumber,y ;display first half of number of points 1354 lda FloateyNumTileData+1,x 1355 sta Sprite_Tilenumber+4,y ;display the second half 1356 ldx ObjectOffset ;get enemy object offset and leave 1357 rts 1358 1359 ;------------------------------------------------------------------------------------- 1360 1361 ScreenRoutines: 1362 lda ScreenRoutineTask ;run one of the following subroutines 1363 jsr JumpEngine 1364 1365 .dw InitScreen 1366 .dw SetupIntermediate 1367 .dw WriteTopStatusLine 1368 .dw WriteBottomStatusLine 1369 .dw DisplayTimeUp 1370 .dw ResetSpritesAndScreenTimer 1371 .dw DisplayIntermediate 1372 .dw ResetSpritesAndScreenTimer 1373 .dw AreaParserTaskControl 1374 .dw GetAreaPalette 1375 .dw GetBackgroundColor 1376 .dw GetAlternatePalette1 1377 .dw DrawTitleScreen 1378 .dw ClearBuffersDrawIcon 1379 .dw WriteTopScore 1380 1381 ;------------------------------------------------------------------------------------- 1382 1383 InitScreen: 1384 jsr MoveAllSpritesOffscreen ;initialize all sprites including sprite #0 1385 jsr InitializeNameTables ;and erase both name and attribute tables 1386 lda OperMode 1387 beq NextSubtask ;if mode still 0, do not load 1388 ldx #$03 ;into buffer pointer 1389 jmp SetVRAMAddr_A 1390 1391 ;------------------------------------------------------------------------------------- 1392 1393 SetupIntermediate: 1394 lda BackgroundColorCtrl ;save current background color control 1395 pha ;and player status to stack 1396 lda PlayerStatus 1397 pha 1398 lda #$00 ;set background color to black 1399 sta PlayerStatus ;and player status to not fiery 1400 lda #$02 ;this is the ONLY time background color control 1401 sta BackgroundColorCtrl ;is set to less than 4 1402 jsr GetPlayerColors 1403 pla ;we only execute this routine for 1404 sta PlayerStatus ;the intermediate lives display 1405 pla ;and once we're done, we return bg 1406 sta BackgroundColorCtrl ;color ctrl and player status from stack 1407 jmp IncSubtask ;then move onto the next task 1408 1409 ;------------------------------------------------------------------------------------- 1410 1411 AreaPalette: 1412 .db $01, $02, $03, $04 1413 1414 GetAreaPalette: 1415 ldy AreaType ;select appropriate palette to load 1416 ldx AreaPalette,y ;based on area type 1417 SetVRAMAddr_A: stx VRAM_Buffer_AddrCtrl ;store offset into buffer control 1418 NextSubtask: jmp IncSubtask ;move onto next task 1419 1420 ;------------------------------------------------------------------------------------- 1421 ;$00 - used as temp counter in GetPlayerColors 1422 1423 BGColorCtrl_Addr: 1424 .db $00, $09, $0a, $04 1425 1426 BackgroundColors: 1427 .db $22, $22, $0f, $0f ;used by area type if bg color ctrl not set 1428 .db $0f, $22, $0f, $0f ;used by background color control if set 1429 1430 PlayerColors: 1431 .db $22, $16, $27, $18 ;mario's colors 1432 .db $22, $30, $27, $19 ;luigi's colors 1433 .db $22, $37, $27, $16 ;fiery (used by both) 1434 1435 GetBackgroundColor: 1436 ldy BackgroundColorCtrl ;check background color control 1437 beq NoBGColor ;if not set, increment task and fetch palette 1438 lda BGColorCtrl_Addr-4,y ;put appropriate palette into vram 1439 sta VRAM_Buffer_AddrCtrl ;note that if set to 5-7, $0301 will not be read 1440 NoBGColor: inc ScreenRoutineTask ;increment to next subtask and plod on through 1441 1442 GetPlayerColors: 1443 ldx VRAM_Buffer1_Offset ;get current buffer offset 1444 ldy #$00 1445 lda CurrentPlayer ;check which player is on the screen 1446 beq ChkFiery 1447 ldy #$04 ;load offset for luigi 1448 ChkFiery: lda PlayerStatus ;check player status 1449 cmp #$02 1450 bne StartClrGet ;if fiery, load alternate offset for fiery player 1451 ldy #$08 1452 StartClrGet: lda #$03 ;do four colors 1453 sta $00 1454 ClrGetLoop: lda PlayerColors,y ;fetch player colors and store them 1455 sta VRAM_Buffer1+3,x ;in the buffer 1456 iny 1457 inx 1458 dec $00 1459 bpl ClrGetLoop 1460 ldx VRAM_Buffer1_Offset ;load original offset from before 1461 ldy BackgroundColorCtrl ;if this value is four or greater, it will be set 1462 bne SetBGColor ;therefore use it as offset to background color 1463 ldy AreaType ;otherwise use area type bits from area offset as offset 1464 SetBGColor: lda BackgroundColors,y ;to background color instead 1465 sta VRAM_Buffer1+3,x 1466 lda #$3f ;set for sprite palette address 1467 sta VRAM_Buffer1,x ;save to buffer 1468 lda #$10 1469 sta VRAM_Buffer1+1,x 1470 lda #$04 ;write length byte to buffer 1471 sta VRAM_Buffer1+2,x 1472 lda #$00 ;now the null terminator 1473 sta VRAM_Buffer1+7,x 1474 txa ;move the buffer pointer ahead 7 bytes 1475 clc ;in case we want to write anything else later 1476 adc #$07 1477 SetVRAMOffset: sta VRAM_Buffer1_Offset ;store as new vram buffer offset 1478 rts 1479 1480 ;------------------------------------------------------------------------------------- 1481 1482 GetAlternatePalette1: 1483 lda AreaStyle ;check for mushroom level style 1484 cmp #$01 1485 bne NoAltPal 1486 lda #$0b ;if found, load appropriate palette 1487 SetVRAMAddr_B: sta VRAM_Buffer_AddrCtrl 1488 NoAltPal: jmp IncSubtask ;now onto the next task 1489 1490 ;------------------------------------------------------------------------------------- 1491 1492 WriteTopStatusLine: 1493 lda #$00 ;select main status bar 1494 jsr WriteGameText ;output it 1495 jmp IncSubtask ;onto the next task 1496 1497 ;------------------------------------------------------------------------------------- 1498 1499 WriteBottomStatusLine: 1500 jsr GetSBNybbles ;write player's score and coin tally to screen 1501 ldx VRAM_Buffer1_Offset 1502 lda #$20 ;write address for world-area number on screen 1503 sta VRAM_Buffer1,x 1504 lda #$73 1505 sta VRAM_Buffer1+1,x 1506 lda #$03 ;write length for it 1507 sta VRAM_Buffer1+2,x 1508 ldy WorldNumber ;first the world number 1509 iny 1510 tya 1511 sta VRAM_Buffer1+3,x 1512 lda #$28 ;next the dash 1513 sta VRAM_Buffer1+4,x 1514 ldy LevelNumber ;next the level number 1515 iny ;increment for proper number display 1516 tya 1517 sta VRAM_Buffer1+5,x 1518 lda #$00 ;put null terminator on 1519 sta VRAM_Buffer1+6,x 1520 txa ;move the buffer offset up by 6 bytes 1521 clc 1522 adc #$06 1523 sta VRAM_Buffer1_Offset 1524 jmp IncSubtask 1525 1526 ;------------------------------------------------------------------------------------- 1527 1528 DisplayTimeUp: 1529 lda GameTimerExpiredFlag ;if game timer not expired, increment task 1530 beq NoTimeUp ;control 2 tasks forward, otherwise, stay here 1531 lda #$00 1532 sta GameTimerExpiredFlag ;reset timer expiration flag 1533 lda #$02 ;output time-up screen to buffer 1534 jmp OutputInter 1535 NoTimeUp: inc ScreenRoutineTask ;increment control task 2 tasks forward 1536 jmp IncSubtask 1537 1538 ;------------------------------------------------------------------------------------- 1539 1540 DisplayIntermediate: 1541 lda OperMode ;check primary mode of operation 1542 beq NoInter ;if in title screen mode, skip this 1543 cmp #GameOverModeValue ;are we in game over mode? 1544 beq GameOverInter ;if so, proceed to display game over screen 1545 lda AltEntranceControl ;otherwise check for mode of alternate entry 1546 bne NoInter ;and branch if found 1547 ldy AreaType ;check if we are on castle level 1548 cpy #$03 ;and if so, branch (possibly residual) 1549 beq PlayerInter 1550 lda DisableIntermediate ;if this flag is set, skip intermediate lives display 1551 bne NoInter ;and jump to specific task, otherwise 1552 PlayerInter: jsr DrawPlayer_Intermediate ;put player in appropriate place for 1553 lda #$01 ;lives display, then output lives display to buffer 1554 OutputInter: jsr WriteGameText 1555 jsr ResetScreenTimer 1556 lda #$00 1557 sta DisableScreenFlag ;reenable screen output 1558 rts 1559 GameOverInter: lda #$12 ;set screen timer 1560 sta ScreenTimer 1561 lda #$03 ;output game over screen to buffer 1562 jsr WriteGameText 1563 jmp IncModeTask_B 1564 NoInter: lda #$08 ;set for specific task and leave 1565 sta ScreenRoutineTask 1566 rts 1567 1568 ;------------------------------------------------------------------------------------- 1569 1570 AreaParserTaskControl: 1571 inc DisableScreenFlag ;turn off screen 1572 TaskLoop: jsr AreaParserTaskHandler ;render column set of current area 1573 lda AreaParserTaskNum ;check number of tasks 1574 bne TaskLoop ;if tasks still not all done, do another one 1575 dec ColumnSets ;do we need to render more column sets? 1576 bpl OutputCol 1577 inc ScreenRoutineTask ;if not, move on to the next task 1578 OutputCol: lda #$06 ;set vram buffer to output rendered column set 1579 sta VRAM_Buffer_AddrCtrl ;on next NMI 1580 rts 1581 1582 ;------------------------------------------------------------------------------------- 1583 1584 ;$00 - vram buffer address table low 1585 ;$01 - vram buffer address table high 1586 1587 DrawTitleScreen: 1588 lda OperMode ;are we in title screen mode? 1589 bne IncModeTask_B ;if not, exit 1590 lda #>TitleScreenDataOffset ;load address $1ec0 into 1591 sta PPU_ADDRESS ;the vram address register 1592 lda #<TitleScreenDataOffset 1593 sta PPU_ADDRESS 1594 lda #$03 ;put address $0300 into 1595 sta $01 ;the indirect at $00 1596 ldy #$00 1597 sty $00 1598 lda PPU_DATA ;do one garbage read 1599 OutputTScr: lda PPU_DATA ;get title screen from chr-rom 1600 sta ($00),y ;store 256 bytes into buffer 1601 iny 1602 bne ChkHiByte ;if not past 256 bytes, do not increment 1603 inc $01 ;otherwise increment high byte of indirect 1604 ChkHiByte: lda $01 ;check high byte? 1605 cmp #$04 ;at $0400? 1606 bne OutputTScr ;if not, loop back and do another 1607 cpy #$3a ;check if offset points past end of data 1608 bcc OutputTScr ;if not, loop back and do another 1609 lda #$05 ;set buffer transfer control to $0300, 1610 jmp SetVRAMAddr_B ;increment task and exit 1611 1612 ;------------------------------------------------------------------------------------- 1613 1614 ClearBuffersDrawIcon: 1615 lda OperMode ;check game mode 1616 bne IncModeTask_B ;if not title screen mode, leave 1617 ldx #$00 ;otherwise, clear buffer space 1618 TScrClear: sta VRAM_Buffer1-1,x 1619 sta VRAM_Buffer1-1+$100,x 1620 dex 1621 bne TScrClear 1622 jsr DrawMushroomIcon ;draw player select icon 1623 IncSubtask: inc ScreenRoutineTask ;move onto next task 1624 rts 1625 1626 ;------------------------------------------------------------------------------------- 1627 1628 WriteTopScore: 1629 lda #$fa ;run display routine to display top score on title 1630 jsr UpdateNumber 1631 IncModeTask_B: inc OperMode_Task ;move onto next mode 1632 rts 1633 1634 ;------------------------------------------------------------------------------------- 1635 1636 GameText: 1637 TopStatusBarLine: 1638 .db $20, $43, $05, $16, $0a, $1b, $12, $18 ; "MARIO" 1639 .db $20, $52, $0b, $20, $18, $1b, $15, $0d ; "WORLD TIME" 1640 .db $24, $24, $1d, $12, $16, $0e 1641 .db $20, $68, $05, $00, $24, $24, $2e, $29 ; score trailing digit and coin display 1642 .db $23, $c0, $7f, $aa ; attribute table data, clears name table 0 to palette 2 1643 .db $23, $c2, $01, $ea ; attribute table data, used for coin icon in status bar 1644 .db $ff ; end of data block 1645 1646 WorldLivesDisplay: 1647 .db $21, $cd, $07, $24, $24 ; cross with spaces used on 1648 .db $29, $24, $24, $24, $24 ; lives display 1649 .db $21, $4b, $09, $20, $18 ; "WORLD - " used on lives display 1650 .db $1b, $15, $0d, $24, $24, $28, $24 1651 .db $22, $0c, $47, $24 ; possibly used to clear time up 1652 .db $23, $dc, $01, $ba ; attribute table data for crown if more than 9 lives 1653 .db $ff 1654 1655 TwoPlayerTimeUp: 1656 .db $21, $cd, $05, $16, $0a, $1b, $12, $18 ; "MARIO" 1657 OnePlayerTimeUp: 1658 .db $22, $0c, $07, $1d, $12, $16, $0e, $24, $1e, $19 ; "TIME UP" 1659 .db $ff 1660 1661 TwoPlayerGameOver: 1662 .db $21, $cd, $05, $16, $0a, $1b, $12, $18 ; "MARIO" 1663 OnePlayerGameOver: 1664 .db $22, $0b, $09, $10, $0a, $16, $0e, $24 ; "GAME OVER" 1665 .db $18, $1f, $0e, $1b 1666 .db $ff 1667 1668 WarpZoneWelcome: 1669 .db $25, $84, $15, $20, $0e, $15, $0c, $18, $16 ; "WELCOME TO WARP ZONE!" 1670 .db $0e, $24, $1d, $18, $24, $20, $0a, $1b, $19 1671 .db $24, $23, $18, $17, $0e, $2b 1672 .db $26, $25, $01, $24 ; placeholder for left pipe 1673 .db $26, $2d, $01, $24 ; placeholder for middle pipe 1674 .db $26, $35, $01, $24 ; placeholder for right pipe 1675 .db $27, $d9, $46, $aa ; attribute data 1676 .db $27, $e1, $45, $aa 1677 .db $ff 1678 1679 LuigiName: 1680 .db $15, $1e, $12, $10, $12 ; "LUIGI", no address or length 1681 1682 WarpZoneNumbers: 1683 .db $04, $03, $02, $00 ; warp zone numbers, note spaces on middle 1684 .db $24, $05, $24, $00 ; zone, partly responsible for 1685 .db $08, $07, $06, $00 ; the minus world 1686 1687 GameTextOffsets: 1688 .db TopStatusBarLine-GameText, TopStatusBarLine-GameText 1689 .db WorldLivesDisplay-GameText, WorldLivesDisplay-GameText 1690 .db TwoPlayerTimeUp-GameText, OnePlayerTimeUp-GameText 1691 .db TwoPlayerGameOver-GameText, OnePlayerGameOver-GameText 1692 .db WarpZoneWelcome-GameText, WarpZoneWelcome-GameText 1693 1694 WriteGameText: 1695 pha ;save text number to stack 1696 asl 1697 tay ;multiply by 2 and use as offset 1698 cpy #$04 ;if set to do top status bar or world/lives display, 1699 bcc LdGameText ;branch to use current offset as-is 1700 cpy #$08 ;if set to do time-up or game over, 1701 bcc Chk2Players ;branch to check players 1702 ldy #$08 ;otherwise warp zone, therefore set offset 1703 Chk2Players: lda NumberOfPlayers ;check for number of players 1704 bne LdGameText ;if there are two, use current offset to also print name 1705 iny ;otherwise increment offset by one to not print name 1706 LdGameText: ldx GameTextOffsets,y ;get offset to message we want to print 1707 ldy #$00 1708 GameTextLoop: lda GameText,x ;load message data 1709 cmp #$ff ;check for terminator 1710 beq EndGameText ;branch to end text if found 1711 sta VRAM_Buffer1,y ;otherwise write data to buffer 1712 inx ;and increment increment 1713 iny 1714 bne GameTextLoop ;do this for 256 bytes if no terminator found 1715 EndGameText: lda #$00 ;put null terminator at end 1716 sta VRAM_Buffer1,y 1717 pla ;pull original text number from stack 1718 tax 1719 cmp #$04 ;are we printing warp zone? 1720 bcs PrintWarpZoneNumbers 1721 dex ;are we printing the world/lives display? 1722 bne CheckPlayerName ;if not, branch to check player's name 1723 lda NumberofLives ;otherwise, check number of lives 1724 clc ;and increment by one for display 1725 adc #$01 1726 cmp #10 ;more than 9 lives? 1727 bcc PutLives 1728 sbc #10 ;if so, subtract 10 and put a crown tile 1729 ldy #$9f ;next to the difference...strange things happen if 1730 sty VRAM_Buffer1+7 ;the number of lives exceeds 19 1731 PutLives: sta VRAM_Buffer1+8 1732 ldy WorldNumber ;write world and level numbers (incremented for display) 1733 iny ;to the buffer in the spaces surrounding the dash 1734 sty VRAM_Buffer1+19 1735 ldy LevelNumber 1736 iny 1737 sty VRAM_Buffer1+21 ;we're done here 1738 rts 1739 1740 CheckPlayerName: 1741 lda NumberOfPlayers ;check number of players 1742 beq ExitChkName ;if only 1 player, leave 1743 lda CurrentPlayer ;load current player 1744 dex ;check to see if current message number is for time up 1745 bne ChkLuigi 1746 ldy OperMode ;check for game over mode 1747 cpy #GameOverModeValue 1748 beq ChkLuigi 1749 eor #%00000001 ;if not, must be time up, invert d0 to do other player 1750 ChkLuigi: lsr 1751 bcc ExitChkName ;if mario is current player, do not change the name 1752 ldy #$04 1753 NameLoop: lda LuigiName,y ;otherwise, replace "MARIO" with "LUIGI" 1754 sta VRAM_Buffer1+3,y 1755 dey 1756 bpl NameLoop ;do this until each letter is replaced 1757 ExitChkName: rts 1758 1759 PrintWarpZoneNumbers: 1760 sbc #$04 ;subtract 4 and then shift to the left 1761 asl ;twice to get proper warp zone number 1762 asl ;offset 1763 tax 1764 ldy #$00 1765 WarpNumLoop: lda WarpZoneNumbers,x ;print warp zone numbers into the 1766 sta VRAM_Buffer1+27,y ;placeholders from earlier 1767 inx 1768 iny ;put a number in every fourth space 1769 iny 1770 iny 1771 iny 1772 cpy #$0c 1773 bcc WarpNumLoop 1774 lda #$2c ;load new buffer pointer at end of message 1775 jmp SetVRAMOffset 1776 1777 ;------------------------------------------------------------------------------------- 1778 1779 ResetSpritesAndScreenTimer: 1780 lda ScreenTimer ;check if screen timer has expired 1781 bne NoReset ;if not, branch to leave 1782 jsr MoveAllSpritesOffscreen ;otherwise reset sprites now 1783 1784 ResetScreenTimer: 1785 lda #$07 ;reset timer again 1786 sta ScreenTimer 1787 inc ScreenRoutineTask ;move onto next task 1788 NoReset: rts 1789 1790 ;------------------------------------------------------------------------------------- 1791 ;$00 - temp vram buffer offset 1792 ;$01 - temp metatile buffer offset 1793 ;$02 - temp metatile graphics table offset 1794 ;$03 - used to store attribute bits 1795 ;$04 - used to determine attribute table row 1796 ;$05 - used to determine attribute table column 1797 ;$06 - metatile graphics table address low 1798 ;$07 - metatile graphics table address high 1799 1800 RenderAreaGraphics: 1801 lda CurrentColumnPos ;store LSB of where we're at 1802 and #$01 1803 sta $05 1804 ldy VRAM_Buffer2_Offset ;store vram buffer offset 1805 sty $00 1806 lda CurrentNTAddr_Low ;get current name table address we're supposed to render 1807 sta VRAM_Buffer2+1,y 1808 lda CurrentNTAddr_High 1809 sta VRAM_Buffer2,y 1810 lda #$9a ;store length byte of 26 here with d7 set 1811 sta VRAM_Buffer2+2,y ;to increment by 32 (in columns) 1812 lda #$00 ;init attribute row 1813 sta $04 1814 tax 1815 DrawMTLoop: stx $01 ;store init value of 0 or incremented offset for buffer 1816 lda MetatileBuffer,x ;get first metatile number, and mask out all but 2 MSB 1817 and #%11000000 1818 sta $03 ;store attribute table bits here 1819 asl ;note that metatile format is: 1820 rol ;%xx000000 - attribute table bits, 1821 rol ;%00xxxxxx - metatile number 1822 tay ;rotate bits to d1-d0 and use as offset here 1823 lda MetatileGraphics_Low,y ;get address to graphics table from here 1824 sta $06 1825 lda MetatileGraphics_High,y 1826 sta $07 1827 lda MetatileBuffer,x ;get metatile number again 1828 asl ;multiply by 4 and use as tile offset 1829 asl 1830 sta $02 1831 lda AreaParserTaskNum ;get current task number for level processing and 1832 and #%00000001 ;mask out all but LSB, then invert LSB, multiply by 2 1833 eor #%00000001 ;to get the correct column position in the metatile, 1834 asl ;then add to the tile offset so we can draw either side 1835 adc $02 ;of the metatiles 1836 tay 1837 ldx $00 ;use vram buffer offset from before as X 1838 lda ($06),y 1839 sta VRAM_Buffer2+3,x ;get first tile number (top left or top right) and store 1840 iny 1841 lda ($06),y ;now get the second (bottom left or bottom right) and store 1842 sta VRAM_Buffer2+4,x 1843 ldy $04 ;get current attribute row 1844 lda $05 ;get LSB of current column where we're at, and 1845 bne RightCheck ;branch if set (clear = left attrib, set = right) 1846 lda $01 ;get current row we're rendering 1847 lsr ;branch if LSB set (clear = top left, set = bottom left) 1848 bcs LLeft 1849 rol $03 ;rotate attribute bits 3 to the left 1850 rol $03 ;thus in d1-d0, for upper left square 1851 rol $03 1852 jmp SetAttrib 1853 RightCheck: lda $01 ;get LSB of current row we're rendering 1854 lsr ;branch if set (clear = top right, set = bottom right) 1855 bcs NextMTRow 1856 lsr $03 ;shift attribute bits 4 to the right 1857 lsr $03 ;thus in d3-d2, for upper right square 1858 lsr $03 1859 lsr $03 1860 jmp SetAttrib 1861 LLeft: lsr $03 ;shift attribute bits 2 to the right 1862 lsr $03 ;thus in d5-d4 for lower left square 1863 NextMTRow: inc $04 ;move onto next attribute row 1864 SetAttrib: lda AttributeBuffer,y ;get previously saved bits from before 1865 ora $03 ;if any, and put new bits, if any, onto 1866 sta AttributeBuffer,y ;the old, and store 1867 inc $00 ;increment vram buffer offset by 2 1868 inc $00 1869 ldx $01 ;get current gfx buffer row, and check for 1870 inx ;the bottom of the screen 1871 cpx #$0d 1872 bcc DrawMTLoop ;if not there yet, loop back 1873 ldy $00 ;get current vram buffer offset, increment by 3 1874 iny ;(for name table address and length bytes) 1875 iny 1876 iny 1877 lda #$00 1878 sta VRAM_Buffer2,y ;put null terminator at end of data for name table 1879 sty VRAM_Buffer2_Offset ;store new buffer offset 1880 inc CurrentNTAddr_Low ;increment name table address low 1881 lda CurrentNTAddr_Low ;check current low byte 1882 and #%00011111 ;if no wraparound, just skip this part 1883 bne ExitDrawM 1884 lda #$80 ;if wraparound occurs, make sure low byte stays 1885 sta CurrentNTAddr_Low ;just under the status bar 1886 lda CurrentNTAddr_High ;and then invert d2 of the name table address high 1887 eor #%00000100 ;to move onto the next appropriate name table 1888 sta CurrentNTAddr_High 1889 ExitDrawM: jmp SetVRAMCtrl ;jump to set buffer to $0341 and leave 1890 1891 ;------------------------------------------------------------------------------------- 1892 ;$00 - temp attribute table address high (big endian order this time!) 1893 ;$01 - temp attribute table address low 1894 1895 RenderAttributeTables: 1896 lda CurrentNTAddr_Low ;get low byte of next name table address 1897 and #%00011111 ;to be written to, mask out all but 5 LSB, 1898 sec ;subtract four 1899 sbc #$04 1900 and #%00011111 ;mask out bits again and store 1901 sta $01 1902 lda CurrentNTAddr_High ;get high byte and branch if borrow not set 1903 bcs SetATHigh 1904 eor #%00000100 ;otherwise invert d2 1905 SetATHigh: and #%00000100 ;mask out all other bits 1906 ora #$23 ;add $2300 to the high byte and store 1907 sta $00 1908 lda $01 ;get low byte - 4, divide by 4, add offset for 1909 lsr ;attribute table and store 1910 lsr 1911 adc #$c0 ;we should now have the appropriate block of 1912 sta $01 ;attribute table in our temp address 1913 ldx #$00 1914 ldy VRAM_Buffer2_Offset ;get buffer offset 1915 AttribLoop: lda $00 1916 sta VRAM_Buffer2,y ;store high byte of attribute table address 1917 lda $01 1918 clc ;get low byte, add 8 because we want to start 1919 adc #$08 ;below the status bar, and store 1920 sta VRAM_Buffer2+1,y 1921 sta $01 ;also store in temp again 1922 lda AttributeBuffer,x ;fetch current attribute table byte and store 1923 sta VRAM_Buffer2+3,y ;in the buffer 1924 lda #$01 1925 sta VRAM_Buffer2+2,y ;store length of 1 in buffer 1926 lsr 1927 sta AttributeBuffer,x ;clear current byte in attribute buffer 1928 iny ;increment buffer offset by 4 bytes 1929 iny 1930 iny 1931 iny 1932 inx ;increment attribute offset and check to see 1933 cpx #$07 ;if we're at the end yet 1934 bcc AttribLoop 1935 sta VRAM_Buffer2,y ;put null terminator at the end 1936 sty VRAM_Buffer2_Offset ;store offset in case we want to do any more 1937 SetVRAMCtrl: lda #$06 1938 sta VRAM_Buffer_AddrCtrl ;set buffer to $0341 and leave 1939 rts 1940 1941 ;------------------------------------------------------------------------------------- 1942 1943 ;$00 - used as temporary counter in ColorRotation 1944 1945 ColorRotatePalette: 1946 .db $27, $27, $27, $17, $07, $17 1947 1948 BlankPalette: 1949 .db $3f, $0c, $04, $ff, $ff, $ff, $ff, $00 1950 1951 ;used based on area type 1952 Palette3Data: 1953 .db $0f, $07, $12, $0f 1954 .db $0f, $07, $17, $0f 1955 .db $0f, $07, $17, $1c 1956 .db $0f, $07, $17, $00 1957 1958 ColorRotation: 1959 lda FrameCounter ;get frame counter 1960 and #$07 ;mask out all but three LSB 1961 bne ExitColorRot ;branch if not set to zero to do this every eighth frame 1962 ldx VRAM_Buffer1_Offset ;check vram buffer offset 1963 cpx #$31 1964 bcs ExitColorRot ;if offset over 48 bytes, branch to leave 1965 tay ;otherwise use frame counter's 3 LSB as offset here 1966 GetBlankPal: lda BlankPalette,y ;get blank palette for palette 3 1967 sta VRAM_Buffer1,x ;store it in the vram buffer 1968 inx ;increment offsets 1969 iny 1970 cpy #$08 1971 bcc GetBlankPal ;do this until all bytes are copied 1972 ldx VRAM_Buffer1_Offset ;get current vram buffer offset 1973 lda #$03 1974 sta $00 ;set counter here 1975 lda AreaType ;get area type 1976 asl ;multiply by 4 to get proper offset 1977 asl 1978 tay ;save as offset here 1979 GetAreaPal: lda Palette3Data,y ;fetch palette to be written based on area type 1980 sta VRAM_Buffer1+3,x ;store it to overwrite blank palette in vram buffer 1981 iny 1982 inx 1983 dec $00 ;decrement counter 1984 bpl GetAreaPal ;do this until the palette is all copied 1985 ldx VRAM_Buffer1_Offset ;get current vram buffer offset 1986 ldy ColorRotateOffset ;get color cycling offset 1987 lda ColorRotatePalette,y 1988 sta VRAM_Buffer1+4,x ;get and store current color in second slot of palette 1989 lda VRAM_Buffer1_Offset 1990 clc ;add seven bytes to vram buffer offset 1991 adc #$07 1992 sta VRAM_Buffer1_Offset 1993 inc ColorRotateOffset ;increment color cycling offset 1994 lda ColorRotateOffset 1995 cmp #$06 ;check to see if it's still in range 1996 bcc ExitColorRot ;if so, branch to leave 1997 lda #$00 1998 sta ColorRotateOffset ;otherwise, init to keep it in range 1999 ExitColorRot: rts ;leave 2000 2001 ;------------------------------------------------------------------------------------- 2002 ;$00 - temp store for offset control bit 2003 ;$01 - temp vram buffer offset 2004 ;$02 - temp store for vertical high nybble in block buffer routine 2005 ;$03 - temp adder for high byte of name table address 2006 ;$04, $05 - name table address low/high 2007 ;$06, $07 - block buffer address low/high 2008 2009 BlockGfxData: 2010 .db $45, $45, $47, $47 2011 .db $47, $47, $47, $47 2012 .db $57, $58, $59, $5a 2013 .db $24, $24, $24, $24 2014 .db $26, $26, $26, $26 2015 2016 RemoveCoin_Axe: 2017 ldy #$41 ;set low byte so offset points to $0341 2018 lda #$03 ;load offset for default blank metatile 2019 ldx AreaType ;check area type 2020 bne WriteBlankMT ;if not water type, use offset 2021 lda #$04 ;otherwise load offset for blank metatile used in water 2022 WriteBlankMT: jsr PutBlockMetatile ;do a sub to write blank metatile to vram buffer 2023 lda #$06 2024 sta VRAM_Buffer_AddrCtrl ;set vram address controller to $0341 and leave 2025 rts 2026 2027 ReplaceBlockMetatile: 2028 jsr WriteBlockMetatile ;write metatile to vram buffer to replace block object 2029 inc Block_ResidualCounter ;increment unused counter (residual code) 2030 dec Block_RepFlag,x ;decrement flag (residual code) 2031 rts ;leave 2032 2033 DestroyBlockMetatile: 2034 lda #$00 ;force blank metatile if branched/jumped to this point 2035 2036 WriteBlockMetatile: 2037 ldy #$03 ;load offset for blank metatile 2038 cmp #$00 ;check contents of A for blank metatile 2039 beq UseBOffset ;branch if found (unconditional if branched from 8a6b) 2040 ldy #$00 ;load offset for brick metatile w/ line 2041 cmp #$58 2042 beq UseBOffset ;use offset if metatile is brick with coins (w/ line) 2043 cmp #$51 2044 beq UseBOffset ;use offset if metatile is breakable brick w/ line 2045 iny ;increment offset for brick metatile w/o line 2046 cmp #$5d 2047 beq UseBOffset ;use offset if metatile is brick with coins (w/o line) 2048 cmp #$52 2049 beq UseBOffset ;use offset if metatile is breakable brick w/o line 2050 iny ;if any other metatile, increment offset for empty block 2051 UseBOffset: tya ;put Y in A 2052 ldy VRAM_Buffer1_Offset ;get vram buffer offset 2053 iny ;move onto next byte 2054 jsr PutBlockMetatile ;get appropriate block data and write to vram buffer 2055 MoveVOffset: dey ;decrement vram buffer offset 2056 tya ;add 10 bytes to it 2057 clc 2058 adc #10 2059 jmp SetVRAMOffset ;branch to store as new vram buffer offset 2060 2061 PutBlockMetatile: 2062 stx $00 ;store control bit from SprDataOffset_Ctrl 2063 sty $01 ;store vram buffer offset for next byte 2064 asl 2065 asl ;multiply A by four and use as X 2066 tax 2067 ldy #$20 ;load high byte for name table 0 2068 lda $06 ;get low byte of block buffer pointer 2069 cmp #$d0 ;check to see if we're on odd-page block buffer 2070 bcc SaveHAdder ;if not, use current high byte 2071 ldy #$24 ;otherwise load high byte for name table 1 2072 SaveHAdder: sty $03 ;save high byte here 2073 and #$0f ;mask out high nybble of block buffer pointer 2074 asl ;multiply by 2 to get appropriate name table low byte 2075 sta $04 ;and then store it here 2076 lda #$00 2077 sta $05 ;initialize temp high byte 2078 lda $02 ;get vertical high nybble offset used in block buffer routine 2079 clc 2080 adc #$20 ;add 32 pixels for the status bar 2081 asl 2082 rol $05 ;shift and rotate d7 onto d0 and d6 into carry 2083 asl 2084 rol $05 ;shift and rotate d6 onto d0 and d5 into carry 2085 adc $04 ;add low byte of name table and carry to vertical high nybble 2086 sta $04 ;and store here 2087 lda $05 ;get whatever was in d7 and d6 of vertical high nybble 2088 adc #$00 ;add carry 2089 clc 2090 adc $03 ;then add high byte of name table 2091 sta $05 ;store here 2092 ldy $01 ;get vram buffer offset to be used 2093 RemBridge: lda BlockGfxData,x ;write top left and top right 2094 sta VRAM_Buffer1+2,y ;tile numbers into first spot 2095 lda BlockGfxData+1,x 2096 sta VRAM_Buffer1+3,y 2097 lda BlockGfxData+2,x ;write bottom left and bottom 2098 sta VRAM_Buffer1+7,y ;right tiles numbers into 2099 lda BlockGfxData+3,x ;second spot 2100 sta VRAM_Buffer1+8,y 2101 lda $04 2102 sta VRAM_Buffer1,y ;write low byte of name table 2103 clc ;into first slot as read 2104 adc #$20 ;add 32 bytes to value 2105 sta VRAM_Buffer1+5,y ;write low byte of name table 2106 lda $05 ;plus 32 bytes into second slot 2107 sta VRAM_Buffer1-1,y ;write high byte of name 2108 sta VRAM_Buffer1+4,y ;table address to both slots 2109 lda #$02 2110 sta VRAM_Buffer1+1,y ;put length of 2 in 2111 sta VRAM_Buffer1+6,y ;both slots 2112 lda #$00 2113 sta VRAM_Buffer1+9,y ;put null terminator at end 2114 ldx $00 ;get offset control bit here 2115 rts ;and leave 2116 2117 ;------------------------------------------------------------------------------------- 2118 ;METATILE GRAPHICS TABLE 2119 2120 MetatileGraphics_Low: 2121 .db <Palette0_MTiles, <Palette1_MTiles, <Palette2_MTiles, <Palette3_MTiles 2122 2123 MetatileGraphics_High: 2124 .db >Palette0_MTiles, >Palette1_MTiles, >Palette2_MTiles, >Palette3_MTiles 2125 2126 Palette0_MTiles: 2127 .db $24, $24, $24, $24 ;blank 2128 .db $27, $27, $27, $27 ;black metatile 2129 .db $24, $24, $24, $35 ;bush left 2130 .db $36, $25, $37, $25 ;bush middle 2131 .db $24, $38, $24, $24 ;bush right 2132 .db $24, $30, $30, $26 ;mountain left 2133 .db $26, $26, $34, $26 ;mountain left bottom/middle center 2134 .db $24, $31, $24, $32 ;mountain middle top 2135 .db $33, $26, $24, $33 ;mountain right 2136 .db $34, $26, $26, $26 ;mountain right bottom 2137 .db $26, $26, $26, $26 ;mountain middle bottom 2138 .db $24, $c0, $24, $c0 ;bridge guardrail 2139 .db $24, $7f, $7f, $24 ;chain 2140 .db $b8, $ba, $b9, $bb ;tall tree top, top half 2141 .db $b8, $bc, $b9, $bd ;short tree top 2142 .db $ba, $bc, $bb, $bd ;tall tree top, bottom half 2143 .db $60, $64, $61, $65 ;warp pipe end left, points up 2144 .db $62, $66, $63, $67 ;warp pipe end right, points up 2145 .db $60, $64, $61, $65 ;decoration pipe end left, points up 2146 .db $62, $66, $63, $67 ;decoration pipe end right, points up 2147 .db $68, $68, $69, $69 ;pipe shaft left 2148 .db $26, $26, $6a, $6a ;pipe shaft right 2149 .db $4b, $4c, $4d, $4e ;tree ledge left edge 2150 .db $4d, $4f, $4d, $4f ;tree ledge middle 2151 .db $4d, $4e, $50, $51 ;tree ledge right edge 2152 .db $6b, $70, $2c, $2d ;mushroom left edge 2153 .db $6c, $71, $6d, $72 ;mushroom middle 2154 .db $6e, $73, $6f, $74 ;mushroom right edge 2155 .db $86, $8a, $87, $8b ;sideways pipe end top 2156 .db $88, $8c, $88, $8c ;sideways pipe shaft top 2157 .db $89, $8d, $69, $69 ;sideways pipe joint top 2158 .db $8e, $91, $8f, $92 ;sideways pipe end bottom 2159 .db $26, $93, $26, $93 ;sideways pipe shaft bottom 2160 .db $90, $94, $69, $69 ;sideways pipe joint bottom 2161 .db $a4, $e9, $ea, $eb ;seaplant 2162 .db $24, $24, $24, $24 ;blank, used on bricks or blocks that are hit 2163 .db $24, $2f, $24, $3d ;flagpole ball 2164 .db $a2, $a2, $a3, $a3 ;flagpole shaft 2165 .db $24, $24, $24, $24 ;blank, used in conjunction with vines 2166 2167 Palette1_MTiles: 2168 .db $a2, $a2, $a3, $a3 ;vertical rope 2169 .db $99, $24, $99, $24 ;horizontal rope 2170 .db $24, $a2, $3e, $3f ;left pulley 2171 .db $5b, $5c, $24, $a3 ;right pulley 2172 .db $24, $24, $24, $24 ;blank used for balance rope 2173 .db $9d, $47, $9e, $47 ;castle top 2174 .db $47, $47, $27, $27 ;castle window left 2175 .db $47, $47, $47, $47 ;castle brick wall 2176 .db $27, $27, $47, $47 ;castle window right 2177 .db $a9, $47, $aa, $47 ;castle top w/ brick 2178 .db $9b, $27, $9c, $27 ;entrance top 2179 .db $27, $27, $27, $27 ;entrance bottom 2180 .db $52, $52, $52, $52 ;green ledge stump 2181 .db $80, $a0, $81, $a1 ;fence 2182 .db $be, $be, $bf, $bf ;tree trunk 2183 .db $75, $ba, $76, $bb ;mushroom stump top 2184 .db $ba, $ba, $bb, $bb ;mushroom stump bottom 2185 .db $45, $47, $45, $47 ;breakable brick w/ line 2186 .db $47, $47, $47, $47 ;breakable brick 2187 .db $45, $47, $45, $47 ;breakable brick (not used) 2188 .db $b4, $b6, $b5, $b7 ;cracked rock terrain 2189 .db $45, $47, $45, $47 ;brick with line (power-up) 2190 .db $45, $47, $45, $47 ;brick with line (vine) 2191 .db $45, $47, $45, $47 ;brick with line (star) 2192 .db $45, $47, $45, $47 ;brick with line (coins) 2193 .db $45, $47, $45, $47 ;brick with line (1-up) 2194 .db $47, $47, $47, $47 ;brick (power-up) 2195 .db $47, $47, $47, $47 ;brick (vine) 2196 .db $47, $47, $47, $47 ;brick (star) 2197 .db $47, $47, $47, $47 ;brick (coins) 2198 .db $47, $47, $47, $47 ;brick (1-up) 2199 .db $24, $24, $24, $24 ;hidden block (1 coin) 2200 .db $24, $24, $24, $24 ;hidden block (1-up) 2201 .db $ab, $ac, $ad, $ae ;solid block (3-d block) 2202 .db $5d, $5e, $5d, $5e ;solid block (white wall) 2203 .db $c1, $24, $c1, $24 ;bridge 2204 .db $c6, $c8, $c7, $c9 ;bullet bill cannon barrel 2205 .db $ca, $cc, $cb, $cd ;bullet bill cannon top 2206 .db $2a, $2a, $40, $40 ;bullet bill cannon bottom 2207 .db $24, $24, $24, $24 ;blank used for jumpspring 2208 .db $24, $47, $24, $47 ;half brick used for jumpspring 2209 .db $82, $83, $84, $85 ;solid block (water level, green rock) 2210 .db $24, $47, $24, $47 ;half brick (???) 2211 .db $86, $8a, $87, $8b ;water pipe top 2212 .db $8e, $91, $8f, $92 ;water pipe bottom 2213 .db $24, $2f, $24, $3d ;flag ball (residual object) 2214 2215 Palette2_MTiles: 2216 .db $24, $24, $24, $35 ;cloud left 2217 .db $36, $25, $37, $25 ;cloud middle 2218 .db $24, $38, $24, $24 ;cloud right 2219 .db $24, $24, $39, $24 ;cloud bottom left 2220 .db $3a, $24, $3b, $24 ;cloud bottom middle 2221 .db $3c, $24, $24, $24 ;cloud bottom right 2222 .db $41, $26, $41, $26 ;water/lava top 2223 .db $26, $26, $26, $26 ;water/lava 2224 .db $b0, $b1, $b2, $b3 ;cloud level terrain 2225 .db $77, $79, $77, $79 ;bowser's bridge 2226 2227 Palette3_MTiles: 2228 .db $53, $55, $54, $56 ;question block (coin) 2229 .db $53, $55, $54, $56 ;question block (power-up) 2230 .db $a5, $a7, $a6, $a8 ;coin 2231 .db $c2, $c4, $c3, $c5 ;underwater coin 2232 .db $57, $59, $58, $5a ;empty block 2233 .db $7b, $7d, $7c, $7e ;axe 2234 2235 ;------------------------------------------------------------------------------------- 2236 ;VRAM BUFFER DATA FOR LOCATIONS IN PRG-ROM 2237 2238 WaterPaletteData: 2239 .db $3f, $00, $20 2240 .db $0f, $15, $12, $25 2241 .db $0f, $3a, $1a, $0f 2242 .db $0f, $30, $12, $0f 2243 .db $0f, $27, $12, $0f 2244 .db $22, $16, $27, $18 2245 .db $0f, $10, $30, $27 2246 .db $0f, $16, $30, $27 2247 .db $0f, $0f, $30, $10 2248 .db $00 2249 2250 GroundPaletteData: 2251 .db $3f, $00, $20 2252 .db $0f, $29, $1a, $0f 2253 .db $0f, $36, $17, $0f 2254 .db $0f, $30, $21, $0f 2255 .db $0f, $27, $17, $0f 2256 .db $0f, $16, $27, $18 2257 .db $0f, $1a, $30, $27 2258 .db $0f, $16, $30, $27 2259 .db $0f, $0f, $36, $17 2260 .db $00 2261 2262 UndergroundPaletteData: 2263 .db $3f, $00, $20 2264 .db $0f, $29, $1a, $09 2265 .db $0f, $3c, $1c, $0f 2266 .db $0f, $30, $21, $1c 2267 .db $0f, $27, $17, $1c 2268 .db $0f, $16, $27, $18 2269 .db $0f, $1c, $36, $17 2270 .db $0f, $16, $30, $27 2271 .db $0f, $0c, $3c, $1c 2272 .db $00 2273 2274 CastlePaletteData: 2275 .db $3f, $00, $20 2276 .db $0f, $30, $10, $00 2277 .db $0f, $30, $10, $00 2278 .db $0f, $30, $16, $00 2279 .db $0f, $27, $17, $00 2280 .db $0f, $16, $27, $18 2281 .db $0f, $1c, $36, $17 2282 .db $0f, $16, $30, $27 2283 .db $0f, $00, $30, $10 2284 .db $00 2285 2286 DaySnowPaletteData: 2287 .db $3f, $00, $04 2288 .db $22, $30, $00, $10 2289 .db $00 2290 2291 NightSnowPaletteData: 2292 .db $3f, $00, $04 2293 .db $0f, $30, $00, $10 2294 .db $00 2295 2296 MushroomPaletteData: 2297 .db $3f, $00, $04 2298 .db $22, $27, $16, $0f 2299 .db $00 2300 2301 BowserPaletteData: 2302 .db $3f, $14, $04 2303 .db $0f, $1a, $30, $27 2304 .db $00 2305 2306 MarioThanksMessage: 2307 ;"THANK YOU MARIO!" 2308 .db $25, $48, $10 2309 .db $1d, $11, $0a, $17, $14, $24 2310 .db $22, $18, $1e, $24 2311 .db $16, $0a, $1b, $12, $18, $2b 2312 .db $00 2313 2314 LuigiThanksMessage: 2315 ;"THANK YOU LUIGI!" 2316 .db $25, $48, $10 2317 .db $1d, $11, $0a, $17, $14, $24 2318 .db $22, $18, $1e, $24 2319 .db $15, $1e, $12, $10, $12, $2b 2320 .db $00 2321 2322 MushroomRetainerSaved: 2323 ;"BUT OUR PRINCESS IS IN" 2324 .db $25, $c5, $16 2325 .db $0b, $1e, $1d, $24, $18, $1e, $1b, $24 2326 .db $19, $1b, $12, $17, $0c, $0e, $1c, $1c, $24 2327 .db $12, $1c, $24, $12, $17 2328 ;"ANOTHER CASTLE!" 2329 .db $26, $05, $0f 2330 .db $0a, $17, $18, $1d, $11, $0e, $1b, $24 2331 .db $0c, $0a, $1c, $1d, $15, $0e, $2b, $00 2332 2333 PrincessSaved1: 2334 ;"YOUR QUEST IS OVER." 2335 .db $25, $a7, $13 2336 .db $22, $18, $1e, $1b, $24 2337 .db $1a, $1e, $0e, $1c, $1d, $24 2338 .db $12, $1c, $24, $18, $1f, $0e, $1b, $af 2339 .db $00 2340 2341 PrincessSaved2: 2342 ;"WE PRESENT YOU A NEW QUEST." 2343 .db $25, $e3, $1b 2344 .db $20, $0e, $24 2345 .db $19, $1b, $0e, $1c, $0e, $17, $1d, $24 2346 .db $22, $18, $1e, $24, $0a, $24, $17, $0e, $20, $24 2347 .db $1a, $1e, $0e, $1c, $1d, $af 2348 .db $00 2349 2350 WorldSelectMessage1: 2351 ;"PUSH BUTTON B" 2352 .db $26, $4a, $0d 2353 .db $19, $1e, $1c, $11, $24 2354 .db $0b, $1e, $1d, $1d, $18, $17, $24, $0b 2355 .db $00 2356 2357 WorldSelectMessage2: 2358 ;"TO SELECT A WORLD" 2359 .db $26, $88, $11 2360 .db $1d, $18, $24, $1c, $0e, $15, $0e, $0c, $1d, $24 2361 .db $0a, $24, $20, $18, $1b, $15, $0d 2362 .db $00 2363 2364 ;------------------------------------------------------------------------------------- 2365 ;$04 - address low to jump address 2366 ;$05 - address high to jump address 2367 ;$06 - jump address low 2368 ;$07 - jump address high 2369 2370 JumpEngine: 2371 asl ;shift bit from contents of A 2372 tay 2373 pla ;pull saved return address from stack 2374 sta $04 ;save to indirect 2375 pla 2376 sta $05 2377 iny 2378 lda ($04),y ;load pointer from indirect 2379 sta $06 ;note that if an RTS is performed in next routine 2380 iny ;it will return to the execution before the sub 2381 lda ($04),y ;that called this routine 2382 sta $07 2383 jmp ($06) ;jump to the address we loaded 2384 2385 ;------------------------------------------------------------------------------------- 2386 2387 InitializeNameTables: 2388 lda PPU_STATUS ;reset flip-flop 2389 lda Mirror_PPU_CTRL_REG1 ;load mirror of ppu reg $2000 2390 ora #%00010000 ;set sprites for first 4k and background for second 4k 2391 and #%11110000 ;clear rest of lower nybble, leave higher alone 2392 jsr WritePPUReg1 2393 lda #$24 ;set vram address to start of name table 1 2394 jsr WriteNTAddr 2395 lda #$20 ;and then set it to name table 0 2396 WriteNTAddr: sta PPU_ADDRESS 2397 lda #$00 2398 sta PPU_ADDRESS 2399 ldx #$04 ;clear name table with blank tile #24 2400 ldy #$c0 2401 lda #$24 2402 InitNTLoop: sta PPU_DATA ;count out exactly 768 tiles 2403 dey 2404 bne InitNTLoop 2405 dex 2406 bne InitNTLoop 2407 ldy #64 ;now to clear the attribute table (with zero this time) 2408 txa 2409 sta VRAM_Buffer1_Offset ;init vram buffer 1 offset 2410 sta VRAM_Buffer1 ;init vram buffer 1 2411 InitATLoop: sta PPU_DATA 2412 dey 2413 bne InitATLoop 2414 sta HorizontalScroll ;reset scroll variables 2415 sta VerticalScroll 2416 jmp InitScroll ;initialize scroll registers to zero 2417 2418 ;------------------------------------------------------------------------------------- 2419 ;$00 - temp joypad bit 2420 2421 ReadJoypads: 2422 lda #$01 ;reset and clear strobe of joypad ports 2423 sta JOYPAD_PORT 2424 lsr 2425 tax ;start with joypad 1's port 2426 sta JOYPAD_PORT 2427 jsr ReadPortBits 2428 inx ;increment for joypad 2's port 2429 ReadPortBits: ldy #$08 2430 PortLoop: pha ;push previous bit onto stack 2431 lda JOYPAD_PORT,x ;read current bit on joypad port 2432 sta $00 ;check d1 and d0 of port output 2433 lsr ;this is necessary on the old 2434 ora $00 ;famicom systems in japan 2435 lsr 2436 pla ;read bits from stack 2437 rol ;rotate bit from carry flag 2438 dey 2439 bne PortLoop ;count down bits left 2440 sta SavedJoypadBits,x ;save controller status here always 2441 pha 2442 and #%00110000 ;check for select or start 2443 and JoypadBitMask,x ;if neither saved state nor current state 2444 beq Save8Bits ;have any of these two set, branch 2445 pla 2446 and #%11001111 ;otherwise store without select 2447 sta SavedJoypadBits,x ;or start bits and leave 2448 rts 2449 Save8Bits: pla 2450 sta JoypadBitMask,x ;save with all bits in another place and leave 2451 rts 2452 2453 ;------------------------------------------------------------------------------------- 2454 ;$00 - vram buffer address table low 2455 ;$01 - vram buffer address table high 2456 2457 WriteBufferToScreen: 2458 sta PPU_ADDRESS ;store high byte of vram address 2459 iny 2460 lda ($00),y ;load next byte (second) 2461 sta PPU_ADDRESS ;store low byte of vram address 2462 iny 2463 lda ($00),y ;load next byte (third) 2464 asl ;shift to left and save in stack 2465 pha 2466 lda Mirror_PPU_CTRL_REG1 ;load mirror of $2000, 2467 ora #%00000100 ;set ppu to increment by 32 by default 2468 bcs SetupWrites ;if d7 of third byte was clear, ppu will 2469 and #%11111011 ;only increment by 1 2470 SetupWrites: jsr WritePPUReg1 ;write to register 2471 pla ;pull from stack and shift to left again 2472 asl 2473 bcc GetLength ;if d6 of third byte was clear, do not repeat byte 2474 ora #%00000010 ;otherwise set d1 and increment Y 2475 iny 2476 GetLength: lsr ;shift back to the right to get proper length 2477 lsr ;note that d1 will now be in carry 2478 tax 2479 OutputToVRAM: bcs RepeatByte ;if carry set, repeat loading the same byte 2480 iny ;otherwise increment Y to load next byte 2481 RepeatByte: lda ($00),y ;load more data from buffer and write to vram 2482 sta PPU_DATA 2483 dex ;done writing? 2484 bne OutputToVRAM 2485 sec 2486 tya 2487 adc $00 ;add end length plus one to the indirect at $00 2488 sta $00 ;to allow this routine to read another set of updates 2489 lda #$00 2490 adc $01 2491 sta $01 2492 lda #$3f ;sets vram address to $3f00 2493 sta PPU_ADDRESS 2494 lda #$00 2495 sta PPU_ADDRESS 2496 sta PPU_ADDRESS ;then reinitializes it for some reason 2497 sta PPU_ADDRESS 2498 UpdateScreen: ldx PPU_STATUS ;reset flip-flop 2499 ldy #$00 ;load first byte from indirect as a pointer 2500 lda ($00),y 2501 bne WriteBufferToScreen ;if byte is zero we have no further updates to make here 2502 InitScroll: sta PPU_SCROLL_REG ;store contents of A into scroll registers 2503 sta PPU_SCROLL_REG ;and end whatever subroutine led us here 2504 rts 2505 2506 ;------------------------------------------------------------------------------------- 2507 2508 WritePPUReg1: 2509 sta PPU_CTRL_REG1 ;write contents of A to PPU register 1 2510 sta Mirror_PPU_CTRL_REG1 ;and its mirror 2511 rts 2512 2513 ;------------------------------------------------------------------------------------- 2514 ;$00 - used to store status bar nybbles 2515 ;$02 - used as temp vram offset 2516 ;$03 - used to store length of status bar number 2517 2518 ;status bar name table offset and length data 2519 StatusBarData: 2520 .db $f0, $06 ; top score display on title screen 2521 .db $62, $06 ; player score 2522 .db $62, $06 2523 .db $6d, $02 ; coin tally 2524 .db $6d, $02 2525 .db $7a, $03 ; game timer 2526 2527 StatusBarOffset: 2528 .db $06, $0c, $12, $18, $1e, $24 2529 2530 PrintStatusBarNumbers: 2531 sta $00 ;store player-specific offset 2532 jsr OutputNumbers ;use first nybble to print the coin display 2533 lda $00 ;move high nybble to low 2534 lsr ;and print to score display 2535 lsr 2536 lsr 2537 lsr 2538 2539 OutputNumbers: 2540 clc ;add 1 to low nybble 2541 adc #$01 2542 and #%00001111 ;mask out high nybble 2543 cmp #$06 2544 bcs ExitOutputN 2545 pha ;save incremented value to stack for now and 2546 asl ;shift to left and use as offset 2547 tay 2548 ldx VRAM_Buffer1_Offset ;get current buffer pointer 2549 lda #$20 ;put at top of screen by default 2550 cpy #$00 ;are we writing top score on title screen? 2551 bne SetupNums 2552 lda #$22 ;if so, put further down on the screen 2553 SetupNums: sta VRAM_Buffer1,x 2554 lda StatusBarData,y ;write low vram address and length of thing 2555 sta VRAM_Buffer1+1,x ;we're printing to the buffer 2556 lda StatusBarData+1,y 2557 sta VRAM_Buffer1+2,x 2558 sta $03 ;save length byte in counter 2559 stx $02 ;and buffer pointer elsewhere for now 2560 pla ;pull original incremented value from stack 2561 tax 2562 lda StatusBarOffset,x ;load offset to value we want to write 2563 sec 2564 sbc StatusBarData+1,y ;subtract from length byte we read before 2565 tay ;use value as offset to display digits 2566 ldx $02 2567 DigitPLoop: lda DisplayDigits,y ;write digits to the buffer 2568 sta VRAM_Buffer1+3,x 2569 inx 2570 iny 2571 dec $03 ;do this until all the digits are written 2572 bne DigitPLoop 2573 lda #$00 ;put null terminator at end 2574 sta VRAM_Buffer1+3,x 2575 inx ;increment buffer pointer by 3 2576 inx 2577 inx 2578 stx VRAM_Buffer1_Offset ;store it in case we want to use it again 2579 ExitOutputN: rts 2580 2581 ;------------------------------------------------------------------------------------- 2582 2583 DigitsMathRoutine: 2584 lda OperMode ;check mode of operation 2585 cmp #TitleScreenModeValue 2586 beq EraseDMods ;if in title screen mode, branch to lock score 2587 ldx #$05 2588 AddModLoop: lda DigitModifier,x ;load digit amount to increment 2589 clc 2590 adc DisplayDigits,y ;add to current digit 2591 bmi BorrowOne ;if result is a negative number, branch to subtract 2592 cmp #10 2593 bcs CarryOne ;if digit greater than $09, branch to add 2594 StoreNewD: sta DisplayDigits,y ;store as new score or game timer digit 2595 dey ;move onto next digits in score or game timer 2596 dex ;and digit amounts to increment 2597 bpl AddModLoop ;loop back if we're not done yet 2598 EraseDMods: lda #$00 ;store zero here 2599 ldx #$06 ;start with the last digit 2600 EraseMLoop: sta DigitModifier-1,x ;initialize the digit amounts to increment 2601 dex 2602 bpl EraseMLoop ;do this until they're all reset, then leave 2603 rts 2604 BorrowOne: dec DigitModifier-1,x ;decrement the previous digit, then put $09 in 2605 lda #$09 ;the game timer digit we're currently on to "borrow 2606 bne StoreNewD ;the one", then do an unconditional branch back 2607 CarryOne: sec ;subtract ten from our digit to make it a 2608 sbc #10 ;proper BCD number, then increment the digit 2609 inc DigitModifier-1,x ;preceding current digit to "carry the one" properly 2610 jmp StoreNewD ;go back to just after we branched here 2611 2612 ;------------------------------------------------------------------------------------- 2613 2614 UpdateTopScore: 2615 ldx #$05 ;start with mario's score 2616 jsr TopScoreCheck 2617 ldx #$0b ;now do luigi's score 2618 2619 TopScoreCheck: 2620 ldy #$05 ;start with the lowest digit 2621 sec 2622 GetScoreDiff: lda PlayerScoreDisplay,x ;subtract each player digit from each high score digit 2623 sbc TopScoreDisplay,y ;from lowest to highest, if any top score digit exceeds 2624 dex ;any player digit, borrow will be set until a subsequent 2625 dey ;subtraction clears it (player digit is higher than top) 2626 bpl GetScoreDiff 2627 bcc NoTopSc ;check to see if borrow is still set, if so, no new high score 2628 inx ;increment X and Y once to the start of the score 2629 iny 2630 CopyScore: lda PlayerScoreDisplay,x ;store player's score digits into high score memory area 2631 sta TopScoreDisplay,y 2632 inx 2633 iny 2634 cpy #$06 ;do this until we have stored them all 2635 bcc CopyScore 2636 NoTopSc: rts 2637 2638 ;------------------------------------------------------------------------------------- 2639 2640 DefaultSprOffsets: 2641 .db $04, $30, $48, $60, $78, $90, $a8, $c0 2642 .db $d8, $e8, $24, $f8, $fc, $28, $2c 2643 2644 Sprite0Data: 2645 .db $18, $ff, $23, $58 2646 2647 ;------------------------------------------------------------------------------------- 2648 2649 InitializeGame: 2650 ldy #$6f ;clear all memory as in initialization procedure, 2651 jsr InitializeMemory ;but this time, clear only as far as $076f 2652 ldy #$1f 2653 ClrSndLoop: sta SoundMemory,y ;clear out memory used 2654 dey ;by the sound engines 2655 bpl ClrSndLoop 2656 lda #$18 ;set demo timer 2657 sta DemoTimer 2658 jsr LoadAreaPointer 2659 2660 InitializeArea: 2661 ldy #$4b ;clear all memory again, only as far as $074b 2662 jsr InitializeMemory ;this is only necessary if branching from 2663 ldx #$21 2664 lda #$00 2665 ClrTimersLoop: sta Timers,x ;clear out memory between 2666 dex ;$0780 and $07a1 2667 bpl ClrTimersLoop 2668 lda HalfwayPage 2669 ldy AltEntranceControl ;if AltEntranceControl not set, use halfway page, if any found 2670 beq StartPage 2671 lda EntrancePage ;otherwise use saved entry page number here 2672 StartPage: sta ScreenLeft_PageLoc ;set as value here 2673 sta CurrentPageLoc ;also set as current page 2674 sta BackloadingFlag ;set flag here if halfway page or saved entry page number found 2675 jsr GetScreenPosition ;get pixel coordinates for screen borders 2676 ldy #$20 ;if on odd numbered page, use $2480 as start of rendering 2677 and #%00000001 ;otherwise use $2080, this address used later as name table 2678 beq SetInitNTHigh ;address for rendering of game area 2679 ldy #$24 2680 SetInitNTHigh: sty CurrentNTAddr_High ;store name table address 2681 ldy #$80 2682 sty CurrentNTAddr_Low 2683 asl ;store LSB of page number in high nybble 2684 asl ;of block buffer column position 2685 asl 2686 asl 2687 sta BlockBufferColumnPos 2688 dec AreaObjectLength ;set area object lengths for all empty 2689 dec AreaObjectLength+1 2690 dec AreaObjectLength+2 2691 lda #$0b ;set value for renderer to update 12 column sets 2692 sta ColumnSets ;12 column sets = 24 metatile columns = 1 1/2 screens 2693 jsr GetAreaDataAddrs ;get enemy and level addresses and load header 2694 lda PrimaryHardMode ;check to see if primary hard mode has been activated 2695 bne SetSecHard ;if so, activate the secondary no matter where we're at 2696 lda WorldNumber ;otherwise check world number 2697 cmp #World5 ;if less than 5, do not activate secondary 2698 bcc CheckHalfway 2699 bne SetSecHard ;if not equal to, then world > 5, thus activate 2700 lda LevelNumber ;otherwise, world 5, so check level number 2701 cmp #Level3 ;if 1 or 2, do not set secondary hard mode flag 2702 bcc CheckHalfway 2703 SetSecHard: inc SecondaryHardMode ;set secondary hard mode flag for areas 5-3 and beyond 2704 CheckHalfway: lda HalfwayPage 2705 beq DoneInitArea 2706 lda #$02 ;if halfway page set, overwrite start position from header 2707 sta PlayerEntranceCtrl 2708 DoneInitArea: lda #Silence ;silence music 2709 sta AreaMusicQueue 2710 lda #$01 ;disable screen output 2711 sta DisableScreenFlag 2712 inc OperMode_Task ;increment one of the modes 2713 rts 2714 2715 ;------------------------------------------------------------------------------------- 2716 2717 PrimaryGameSetup: 2718 lda #$01 2719 sta FetchNewGameTimerFlag ;set flag to load game timer from header 2720 sta PlayerSize ;set player's size to small 2721 lda #$02 2722 sta NumberofLives ;give each player three lives 2723 sta OffScr_NumberofLives 2724 2725 SecondaryGameSetup: 2726 lda #$00 2727 sta DisableScreenFlag ;enable screen output 2728 tay 2729 ClearVRLoop: sta VRAM_Buffer1-1,y ;clear buffer at $0300-$03ff 2730 iny 2731 bne ClearVRLoop 2732 sta GameTimerExpiredFlag ;clear game timer exp flag 2733 sta DisableIntermediate ;clear skip lives display flag 2734 sta BackloadingFlag ;clear value here 2735 lda #$ff 2736 sta BalPlatformAlignment ;initialize balance platform assignment flag 2737 lda ScreenLeft_PageLoc ;get left side page location 2738 lsr Mirror_PPU_CTRL_REG1 ;shift LSB of ppu register #1 mirror out 2739 and #$01 ;mask out all but LSB of page location 2740 ror ;rotate LSB of page location into carry then onto mirror 2741 rol Mirror_PPU_CTRL_REG1 ;this is to set the proper PPU name table 2742 jsr GetAreaMusic ;load proper music into queue 2743 lda #$38 ;load sprite shuffle amounts to be used later 2744 sta SprShuffleAmt+2 2745 lda #$48 2746 sta SprShuffleAmt+1 2747 lda #$58 2748 sta SprShuffleAmt 2749 ldx #$0e ;load default OAM offsets into $06e4-$06f2 2750 ShufAmtLoop: lda DefaultSprOffsets,x 2751 sta SprDataOffset,x 2752 dex ;do this until they're all set 2753 bpl ShufAmtLoop 2754 ldy #$03 ;set up sprite #0 2755 ISpr0Loop: lda Sprite0Data,y 2756 sta Sprite_Data,y 2757 dey 2758 bpl ISpr0Loop 2759 jsr DoNothing2 ;these jsrs doesn't do anything useful 2760 jsr DoNothing1 2761 inc Sprite0HitDetectFlag ;set sprite #0 check flag 2762 inc OperMode_Task ;increment to next task 2763 rts 2764 2765 ;------------------------------------------------------------------------------------- 2766 2767 ;$06 - RAM address low 2768 ;$07 - RAM address high 2769 2770 InitializeMemory: 2771 ldx #$07 ;set initial high byte to $0700-$07ff 2772 lda #$00 ;set initial low byte to start of page (at $00 of page) 2773 sta $06 2774 InitPageLoop: stx $07 2775 InitByteLoop: cpx #$01 ;check to see if we're on the stack ($0100-$01ff) 2776 bne InitByte ;if not, go ahead anyway 2777 cpy #$60 ;otherwise, check to see if we're at $0160-$01ff 2778 bcs SkipByte ;if so, skip write 2779 InitByte: sta ($06),y ;otherwise, initialize byte with current low byte in Y 2780 SkipByte: dey 2781 cpy #$ff ;do this until all bytes in page have been erased 2782 bne InitByteLoop 2783 dex ;go onto the next page 2784 bpl InitPageLoop ;do this until all pages of memory have been erased 2785 rts 2786 2787 ;------------------------------------------------------------------------------------- 2788 2789 MusicSelectData: 2790 .db WaterMusic, GroundMusic, UndergroundMusic, CastleMusic 2791 .db CloudMusic, PipeIntroMusic 2792 2793 GetAreaMusic: 2794 lda OperMode ;if in title screen mode, leave 2795 beq ExitGetM 2796 lda AltEntranceControl ;check for specific alternate mode of entry 2797 cmp #$02 ;if found, branch without checking starting position 2798 beq ChkAreaType ;from area object data header 2799 ldy #$05 ;select music for pipe intro scene by default 2800 lda PlayerEntranceCtrl ;check value from level header for certain values 2801 cmp #$06 2802 beq StoreMusic ;load music for pipe intro scene if header 2803 cmp #$07 ;start position either value $06 or $07 2804 beq StoreMusic 2805 ChkAreaType: ldy AreaType ;load area type as offset for music bit 2806 lda CloudTypeOverride 2807 beq StoreMusic ;check for cloud type override 2808 ldy #$04 ;select music for cloud type level if found 2809 StoreMusic: lda MusicSelectData,y ;otherwise select appropriate music for level type 2810 sta AreaMusicQueue ;store in queue and leave 2811 ExitGetM: rts 2812 2813 ;------------------------------------------------------------------------------------- 2814 2815 PlayerStarting_X_Pos: 2816 .db $28, $18 2817 .db $38, $28 2818 2819 AltYPosOffset: 2820 .db $08, $00 2821 2822 PlayerStarting_Y_Pos: 2823 .db $00, $20, $b0, $50, $00, $00, $b0, $b0 2824 .db $f0 2825 2826 PlayerBGPriorityData: 2827 .db $00, $20, $00, $00, $00, $00, $00, $00 2828 2829 GameTimerData: 2830 .db $20 ;dummy byte, used as part of bg priority data 2831 .db $04, $03, $02 2832 2833 Entrance_GameTimerSetup: 2834 lda ScreenLeft_PageLoc ;set current page for area objects 2835 sta Player_PageLoc ;as page location for player 2836 lda #$28 ;store value here 2837 sta VerticalForceDown ;for fractional movement downwards if necessary 2838 lda #$01 ;set high byte of player position and 2839 sta PlayerFacingDir ;set facing direction so that player faces right 2840 sta Player_Y_HighPos 2841 lda #$00 ;set player state to on the ground by default 2842 sta Player_State 2843 dec Player_CollisionBits ;initialize player's collision bits 2844 ldy #$00 ;initialize halfway page 2845 sty HalfwayPage 2846 lda AreaType ;check area type 2847 bne ChkStPos ;if water type, set swimming flag, otherwise do not set 2848 iny 2849 ChkStPos: sty SwimmingFlag 2850 ldx PlayerEntranceCtrl ;get starting position loaded from header 2851 ldy AltEntranceControl ;check alternate mode of entry flag for 0 or 1 2852 beq SetStPos 2853 cpy #$01 2854 beq SetStPos 2855 ldx AltYPosOffset-2,y ;if not 0 or 1, override $0710 with new offset in X 2856 SetStPos: lda PlayerStarting_X_Pos,y ;load appropriate horizontal position 2857 sta Player_X_Position ;and vertical positions for the player, using 2858 lda PlayerStarting_Y_Pos,x ;AltEntranceControl as offset for horizontal and either $0710 2859 sta Player_Y_Position ;or value that overwrote $0710 as offset for vertical 2860 lda PlayerBGPriorityData,x 2861 sta Player_SprAttrib ;set player sprite attributes using offset in X 2862 jsr GetPlayerColors ;get appropriate player palette 2863 ldy GameTimerSetting ;get timer control value from header 2864 beq ChkOverR ;if set to zero, branch (do not use dummy byte for this) 2865 lda FetchNewGameTimerFlag ;do we need to set the game timer? if not, use 2866 beq ChkOverR ;old game timer setting 2867 lda GameTimerData,y ;if game timer is set and game timer flag is also set, 2868 sta GameTimerDisplay ;use value of game timer control for first digit of game timer 2869 lda #$01 2870 sta GameTimerDisplay+2 ;set last digit of game timer to 1 2871 lsr 2872 sta GameTimerDisplay+1 ;set second digit of game timer 2873 sta FetchNewGameTimerFlag ;clear flag for game timer reset 2874 sta StarInvincibleTimer ;clear star mario timer 2875 ChkOverR: ldy JoypadOverride ;if controller bits not set, branch to skip this part 2876 beq ChkSwimE 2877 lda #$03 ;set player state to climbing 2878 sta Player_State 2879 ldx #$00 ;set offset for first slot, for block object 2880 jsr InitBlock_XY_Pos 2881 lda #$f0 ;set vertical coordinate for block object 2882 sta Block_Y_Position 2883 ldx #$05 ;set offset in X for last enemy object buffer slot 2884 ldy #$00 ;set offset in Y for object coordinates used earlier 2885 jsr Setup_Vine ;do a sub to grow vine 2886 ChkSwimE: ldy AreaType ;if level not water-type, 2887 bne SetPESub ;skip this subroutine 2888 jsr SetupBubble ;otherwise, execute sub to set up air bubbles 2889 SetPESub: lda #$07 ;set to run player entrance subroutine 2890 sta GameEngineSubroutine ;on the next frame of game engine 2891 rts 2892 2893 ;------------------------------------------------------------------------------------- 2894 2895 ;page numbers are in order from -1 to -4 2896 HalfwayPageNybbles: 2897 .db $56, $40 2898 .db $65, $70 2899 .db $66, $40 2900 .db $66, $40 2901 .db $66, $40 2902 .db $66, $60 2903 .db $65, $70 2904 .db $00, $00 2905 2906 PlayerLoseLife: 2907 inc DisableScreenFlag ;disable screen and sprite 0 check 2908 lda #$00 2909 sta Sprite0HitDetectFlag 2910 lda #Silence ;silence music 2911 sta EventMusicQueue 2912 dec NumberofLives ;take one life from player 2913 bpl StillInGame ;if player still has lives, branch 2914 lda #$00 2915 sta OperMode_Task ;initialize mode task, 2916 lda #GameOverModeValue ;switch to game over mode 2917 sta OperMode ;and leave 2918 rts 2919 StillInGame: lda WorldNumber ;multiply world number by 2 and use 2920 asl ;as offset 2921 tax 2922 lda LevelNumber ;if in area -3 or -4, increment 2923 and #$02 ;offset by one byte, otherwise 2924 beq GetHalfway ;leave offset alone 2925 inx 2926 GetHalfway: ldy HalfwayPageNybbles,x ;get halfway page number with offset 2927 lda LevelNumber ;check area number's LSB 2928 lsr 2929 tya ;if in area -2 or -4, use lower nybble 2930 bcs MaskHPNyb 2931 lsr ;move higher nybble to lower if area 2932 lsr ;number is -1 or -3 2933 lsr 2934 lsr 2935 MaskHPNyb: and #%00001111 ;mask out all but lower nybble 2936 cmp ScreenLeft_PageLoc 2937 beq SetHalfway ;left side of screen must be at the halfway page, 2938 bcc SetHalfway ;otherwise player must start at the 2939 lda #$00 ;beginning of the level 2940 SetHalfway: sta HalfwayPage ;store as halfway page for player 2941 jsr TransposePlayers ;switch players around if 2-player game 2942 jmp ContinueGame ;continue the game 2943 2944 ;------------------------------------------------------------------------------------- 2945 2946 GameOverMode: 2947 lda OperMode_Task 2948 jsr JumpEngine 2949 2950 .dw SetupGameOver 2951 .dw ScreenRoutines 2952 .dw RunGameOver 2953 2954 ;------------------------------------------------------------------------------------- 2955 2956 SetupGameOver: 2957 lda #$00 ;reset screen routine task control for title screen, game, 2958 sta ScreenRoutineTask ;and game over modes 2959 sta Sprite0HitDetectFlag ;disable sprite 0 check 2960 lda #GameOverMusic 2961 sta EventMusicQueue ;put game over music in secondary queue 2962 inc DisableScreenFlag ;disable screen output 2963 inc OperMode_Task ;set secondary mode to 1 2964 rts 2965 2966 ;------------------------------------------------------------------------------------- 2967 2968 RunGameOver: 2969 lda #$00 ;reenable screen 2970 sta DisableScreenFlag 2971 lda SavedJoypad1Bits ;check controller for start pressed 2972 and #Start_Button 2973 bne TerminateGame 2974 lda ScreenTimer ;if not pressed, wait for 2975 bne GameIsOn ;screen timer to expire 2976 TerminateGame: 2977 lda #Silence ;silence music 2978 sta EventMusicQueue 2979 jsr TransposePlayers ;check if other player can keep 2980 bcc ContinueGame ;going, and do so if possible 2981 lda WorldNumber ;otherwise put world number of current 2982 sta ContinueWorld ;player into secret continue function variable 2983 lda #$00 2984 asl ;residual ASL instruction 2985 sta OperMode_Task ;reset all modes to title screen and 2986 sta ScreenTimer ;leave 2987 sta OperMode 2988 rts 2989 2990 ContinueGame: 2991 jsr LoadAreaPointer ;update level pointer with 2992 lda #$01 ;actual world and area numbers, then 2993 sta PlayerSize ;reset player's size, status, and 2994 inc FetchNewGameTimerFlag ;set game timer flag to reload 2995 lda #$00 ;game timer from header 2996 sta TimerControl ;also set flag for timers to count again 2997 sta PlayerStatus 2998 sta GameEngineSubroutine ;reset task for game core 2999 sta OperMode_Task ;set modes and leave 3000 lda #$01 ;if in game over mode, switch back to 3001 sta OperMode ;game mode, because game is still on 3002 GameIsOn: rts 3003 3004 TransposePlayers: 3005 sec ;set carry flag by default to end game 3006 lda NumberOfPlayers ;if only a 1 player game, leave 3007 beq ExTrans 3008 lda OffScr_NumberofLives ;does offscreen player have any lives left? 3009 bmi ExTrans ;branch if not 3010 lda CurrentPlayer ;invert bit to update 3011 eor #%00000001 ;which player is on the screen 3012 sta CurrentPlayer 3013 ldx #$06 3014 TransLoop: lda OnscreenPlayerInfo,x ;transpose the information 3015 pha ;of the onscreen player 3016 lda OffscreenPlayerInfo,x ;with that of the offscreen player 3017 sta OnscreenPlayerInfo,x 3018 pla 3019 sta OffscreenPlayerInfo,x 3020 dex 3021 bpl TransLoop 3022 clc ;clear carry flag to get game going 3023 ExTrans: rts 3024 3025 ;------------------------------------------------------------------------------------- 3026 3027 DoNothing1: 3028 lda #$ff ;this is residual code, this value is 3029 sta $06c9 ;not used anywhere in the program 3030 DoNothing2: 3031 rts 3032 3033 ;------------------------------------------------------------------------------------- 3034 3035 AreaParserTaskHandler: 3036 ldy AreaParserTaskNum ;check number of tasks here 3037 bne DoAPTasks ;if already set, go ahead 3038 ldy #$08 3039 sty AreaParserTaskNum ;otherwise, set eight by default 3040 DoAPTasks: dey 3041 tya 3042 jsr AreaParserTasks 3043 dec AreaParserTaskNum ;if all tasks not complete do not 3044 bne SkipATRender ;render attribute table yet 3045 jsr RenderAttributeTables 3046 SkipATRender: rts 3047 3048 AreaParserTasks: 3049 jsr JumpEngine 3050 3051 .dw IncrementColumnPos 3052 .dw RenderAreaGraphics 3053 .dw RenderAreaGraphics 3054 .dw AreaParserCore 3055 .dw IncrementColumnPos 3056 .dw RenderAreaGraphics 3057 .dw RenderAreaGraphics 3058 .dw AreaParserCore 3059 3060 ;------------------------------------------------------------------------------------- 3061 3062 IncrementColumnPos: 3063 inc CurrentColumnPos ;increment column where we're at 3064 lda CurrentColumnPos 3065 and #%00001111 ;mask out higher nybble 3066 bne NoColWrap 3067 sta CurrentColumnPos ;if no bits left set, wrap back to zero (0-f) 3068 inc CurrentPageLoc ;and increment page number where we're at 3069 NoColWrap: inc BlockBufferColumnPos ;increment column offset where we're at 3070 lda BlockBufferColumnPos 3071 and #%00011111 ;mask out all but 5 LSB (0-1f) 3072 sta BlockBufferColumnPos ;and save 3073 rts 3074 3075 ;------------------------------------------------------------------------------------- 3076 ;$00 - used as counter, store for low nybble for background, ceiling byte for terrain 3077 ;$01 - used to store floor byte for terrain 3078 ;$07 - used to store terrain metatile 3079 ;$06-$07 - used to store block buffer address 3080 3081 BSceneDataOffsets: 3082 .db $00, $30, $60 3083 3084 BackSceneryData: 3085 .db $93, $00, $00, $11, $12, $12, $13, $00 ;clouds 3086 .db $00, $51, $52, $53, $00, $00, $00, $00 3087 .db $00, $00, $01, $02, $02, $03, $00, $00 3088 .db $00, $00, $00, $00, $91, $92, $93, $00 3089 .db $00, $00, $00, $51, $52, $53, $41, $42 3090 .db $43, $00, $00, $00, $00, $00, $91, $92 3091 3092 .db $97, $87, $88, $89, $99, $00, $00, $00 ;mountains and bushes 3093 .db $11, $12, $13, $a4, $a5, $a5, $a5, $a6 3094 .db $97, $98, $99, $01, $02, $03, $00, $a4 3095 .db $a5, $a6, $00, $11, $12, $12, $12, $13 3096 .db $00, $00, $00, $00, $01, $02, $02, $03 3097 .db $00, $a4, $a5, $a5, $a6, $00, $00, $00 3098 3099 .db $11, $12, $12, $13, $00, $00, $00, $00 ;trees and fences 3100 .db $00, $00, $00, $9c, $00, $8b, $aa, $aa 3101 .db $aa, $aa, $11, $12, $13, $8b, $00, $9c 3102 .db $9c, $00, $00, $01, $02, $03, $11, $12 3103 .db $12, $13, $00, $00, $00, $00, $aa, $aa 3104 .db $9c, $aa, $00, $8b, $00, $01, $02, $03 3105 3106 BackSceneryMetatiles: 3107 .db $80, $83, $00 ;cloud left 3108 .db $81, $84, $00 ;cloud middle 3109 .db $82, $85, $00 ;cloud right 3110 .db $02, $00, $00 ;bush left 3111 .db $03, $00, $00 ;bush middle 3112 .db $04, $00, $00 ;bush right 3113 .db $00, $05, $06 ;mountain left 3114 .db $07, $06, $0a ;mountain middle 3115 .db $00, $08, $09 ;mountain right 3116 .db $4d, $00, $00 ;fence 3117 .db $0d, $0f, $4e ;tall tree 3118 .db $0e, $4e, $4e ;short tree 3119 3120 FSceneDataOffsets: 3121 .db $00, $0d, $1a 3122 3123 ForeSceneryData: 3124 .db $86, $87, $87, $87, $87, $87, $87 ;in water 3125 .db $87, $87, $87, $87, $69, $69 3126 3127 .db $00, $00, $00, $00, $00, $45, $47 ;wall 3128 .db $47, $47, $47, $47, $00, $00 3129 3130 .db $00, $00, $00, $00, $00, $00, $00 ;over water 3131 .db $00, $00, $00, $00, $86, $87 3132 3133 TerrainMetatiles: 3134 .db $69, $54, $52, $62 3135 3136 TerrainRenderBits: 3137 .db %00000000, %00000000 ;no ceiling or floor 3138 .db %00000000, %00011000 ;no ceiling, floor 2 3139 .db %00000001, %00011000 ;ceiling 1, floor 2 3140 .db %00000111, %00011000 ;ceiling 3, floor 2 3141 .db %00001111, %00011000 ;ceiling 4, floor 2 3142 .db %11111111, %00011000 ;ceiling 8, floor 2 3143 .db %00000001, %00011111 ;ceiling 1, floor 5 3144 .db %00000111, %00011111 ;ceiling 3, floor 5 3145 .db %00001111, %00011111 ;ceiling 4, floor 5 3146 .db %10000001, %00011111 ;ceiling 1, floor 6 3147 .db %00000001, %00000000 ;ceiling 1, no floor 3148 .db %10001111, %00011111 ;ceiling 4, floor 6 3149 .db %11110001, %00011111 ;ceiling 1, floor 9 3150 .db %11111001, %00011000 ;ceiling 1, middle 5, floor 2 3151 .db %11110001, %00011000 ;ceiling 1, middle 4, floor 2 3152 .db %11111111, %00011111 ;completely solid top to bottom 3153 3154 AreaParserCore: 3155 lda BackloadingFlag ;check to see if we are starting right of start 3156 beq RenderSceneryTerrain ;if not, go ahead and render background, foreground and terrain 3157 jsr ProcessAreaData ;otherwise skip ahead and load level data 3158 3159 RenderSceneryTerrain: 3160 ldx #$0c 3161 lda #$00 3162 ClrMTBuf: sta MetatileBuffer,x ;clear out metatile buffer 3163 dex 3164 bpl ClrMTBuf 3165 ldy BackgroundScenery ;do we need to render the background scenery? 3166 beq RendFore ;if not, skip to check the foreground 3167 lda CurrentPageLoc ;otherwise check for every third page 3168 ThirdP: cmp #$03 3169 bmi RendBack ;if less than three we're there 3170 sec 3171 sbc #$03 ;if 3 or more, subtract 3 and 3172 bpl ThirdP ;do an unconditional branch 3173 RendBack: asl ;move results to higher nybble 3174 asl 3175 asl 3176 asl 3177 adc BSceneDataOffsets-1,y ;add to it offset loaded from here 3178 adc CurrentColumnPos ;add to the result our current column position 3179 tax 3180 lda BackSceneryData,x ;load data from sum of offsets 3181 beq RendFore ;if zero, no scenery for that part 3182 pha 3183 and #$0f ;save to stack and clear high nybble 3184 sec 3185 sbc #$01 ;subtract one (because low nybble is $01-$0c) 3186 sta $00 ;save low nybble 3187 asl ;multiply by three (shift to left and add result to old one) 3188 adc $00 ;note that since d7 was nulled, the carry flag is always clear 3189 tax ;save as offset for background scenery metatile data 3190 pla ;get high nybble from stack, move low 3191 lsr 3192 lsr 3193 lsr 3194 lsr 3195 tay ;use as second offset (used to determine height) 3196 lda #$03 ;use previously saved memory location for counter 3197 sta $00 3198 SceLoop1: lda BackSceneryMetatiles,x ;load metatile data from offset of (lsb - 1) * 3 3199 sta MetatileBuffer,y ;store into buffer from offset of (msb / 16) 3200 inx 3201 iny 3202 cpy #$0b ;if at this location, leave loop 3203 beq RendFore 3204 dec $00 ;decrement until counter expires, barring exception 3205 bne SceLoop1 3206 RendFore: ldx ForegroundScenery ;check for foreground data needed or not 3207 beq RendTerr ;if not, skip this part 3208 ldy FSceneDataOffsets-1,x ;load offset from location offset by header value, then 3209 ldx #$00 ;reinit X 3210 SceLoop2: lda ForeSceneryData,y ;load data until counter expires 3211 beq NoFore ;do not store if zero found 3212 sta MetatileBuffer,x 3213 NoFore: iny 3214 inx 3215 cpx #$0d ;store up to end of metatile buffer 3216 bne SceLoop2 3217 RendTerr: ldy AreaType ;check world type for water level 3218 bne TerMTile ;if not water level, skip this part 3219 lda WorldNumber ;check world number, if not world number eight 3220 cmp #World8 ;then skip this part 3221 bne TerMTile 3222 lda #$62 ;if set as water level and world number eight, 3223 jmp StoreMT ;use castle wall metatile as terrain type 3224 TerMTile: lda TerrainMetatiles,y ;otherwise get appropriate metatile for area type 3225 ldy CloudTypeOverride ;check for cloud type override 3226 beq StoreMT ;if not set, keep value otherwise 3227 lda #$88 ;use cloud block terrain 3228 StoreMT: sta $07 ;store value here 3229 ldx #$00 ;initialize X, use as metatile buffer offset 3230 lda TerrainControl ;use yet another value from the header 3231 asl ;multiply by 2 and use as yet another offset 3232 tay 3233 TerrLoop: lda TerrainRenderBits,y ;get one of the terrain rendering bit data 3234 sta $00 3235 iny ;increment Y and use as offset next time around 3236 sty $01 3237 lda CloudTypeOverride ;skip if value here is zero 3238 beq NoCloud2 3239 cpx #$00 ;otherwise, check if we're doing the ceiling byte 3240 beq NoCloud2 3241 lda $00 ;if not, mask out all but d3 3242 and #%00001000 3243 sta $00 3244 NoCloud2: ldy #$00 ;start at beginning of bitmasks 3245 TerrBChk: lda Bitmasks,y ;load bitmask, then perform AND on contents of first byte 3246 bit $00 3247 beq NextTBit ;if not set, skip this part (do not write terrain to buffer) 3248 lda $07 3249 sta MetatileBuffer,x ;load terrain type metatile number and store into buffer here 3250 NextTBit: inx ;continue until end of buffer 3251 cpx #$0d 3252 beq RendBBuf ;if we're at the end, break out of this loop 3253 lda AreaType ;check world type for underground area 3254 cmp #$02 3255 bne EndUChk ;if not underground, skip this part 3256 cpx #$0b 3257 bne EndUChk ;if we're at the bottom of the screen, override 3258 lda #$54 ;old terrain type with ground level terrain type 3259 sta $07 3260 EndUChk: iny ;increment bitmasks offset in Y 3261 cpy #$08 3262 bne TerrBChk ;if not all bits checked, loop back 3263 ldy $01 3264 bne TerrLoop ;unconditional branch, use Y to load next byte 3265 RendBBuf: jsr ProcessAreaData ;do the area data loading routine now 3266 lda BlockBufferColumnPos 3267 jsr GetBlockBufferAddr ;get block buffer address from where we're at 3268 ldx #$00 3269 ldy #$00 ;init index regs and start at beginning of smaller buffer 3270 ChkMTLow: sty $00 3271 lda MetatileBuffer,x ;load stored metatile number 3272 and #%11000000 ;mask out all but 2 MSB 3273 asl 3274 rol ;make %xx000000 into %000000xx 3275 rol 3276 tay ;use as offset in Y 3277 lda MetatileBuffer,x ;reload original unmasked value here 3278 cmp BlockBuffLowBounds,y ;check for certain values depending on bits set 3279 bcs StrBlock ;if equal or greater, branch 3280 lda #$00 ;if less, init value before storing 3281 StrBlock: ldy $00 ;get offset for block buffer 3282 sta ($06),y ;store value into block buffer 3283 tya 3284 clc ;add 16 (move down one row) to offset 3285 adc #$10 3286 tay 3287 inx ;increment column value 3288 cpx #$0d 3289 bcc ChkMTLow ;continue until we pass last row, then leave 3290 rts 3291 3292 ;numbers lower than these with the same attribute bits 3293 ;will not be stored in the block buffer 3294 BlockBuffLowBounds: 3295 .db $10, $51, $88, $c0 3296 3297 ;------------------------------------------------------------------------------------- 3298 ;$00 - used to store area object identifier 3299 ;$07 - used as adder to find proper area object code 3300 3301 ProcessAreaData: 3302 ldx #$02 ;start at the end of area object buffer 3303 ProcADLoop: stx ObjectOffset 3304 lda #$00 ;reset flag 3305 sta BehindAreaParserFlag 3306 ldy AreaDataOffset ;get offset of area data pointer 3307 lda (AreaData),y ;get first byte of area object 3308 cmp #$fd ;if end-of-area, skip all this crap 3309 beq RdyDecode 3310 lda AreaObjectLength,x ;check area object buffer flag 3311 bpl RdyDecode ;if buffer not negative, branch, otherwise 3312 iny 3313 lda (AreaData),y ;get second byte of area object 3314 asl ;check for page select bit (d7), branch if not set 3315 bcc Chk1Row13 3316 lda AreaObjectPageSel ;check page select 3317 bne Chk1Row13 3318 inc AreaObjectPageSel ;if not already set, set it now 3319 inc AreaObjectPageLoc ;and increment page location 3320 Chk1Row13: dey 3321 lda (AreaData),y ;reread first byte of level object 3322 and #$0f ;mask out high nybble 3323 cmp #$0d ;row 13? 3324 bne Chk1Row14 3325 iny ;if so, reread second byte of level object 3326 lda (AreaData),y 3327 dey ;decrement to get ready to read first byte 3328 and #%01000000 ;check for d6 set (if not, object is page control) 3329 bne CheckRear 3330 lda AreaObjectPageSel ;if page select is set, do not reread 3331 bne CheckRear 3332 iny ;if d6 not set, reread second byte 3333 lda (AreaData),y 3334 and #%00011111 ;mask out all but 5 LSB and store in page control 3335 sta AreaObjectPageLoc 3336 inc AreaObjectPageSel ;increment page select 3337 jmp NextAObj 3338 Chk1Row14: cmp #$0e ;row 14? 3339 bne CheckRear 3340 lda BackloadingFlag ;check flag for saved page number and branch if set 3341 bne RdyDecode ;to render the object (otherwise bg might not look right) 3342 CheckRear: lda AreaObjectPageLoc ;check to see if current page of level object is 3343 cmp CurrentPageLoc ;behind current page of renderer 3344 bcc SetBehind ;if so branch 3345 RdyDecode: jsr DecodeAreaData ;do sub and do not turn on flag 3346 jmp ChkLength 3347 SetBehind: inc BehindAreaParserFlag ;turn on flag if object is behind renderer 3348 NextAObj: jsr IncAreaObjOffset ;increment buffer offset and move on 3349 ChkLength: ldx ObjectOffset ;get buffer offset 3350 lda AreaObjectLength,x ;check object length for anything stored here 3351 bmi ProcLoopb ;if not, branch to handle loopback 3352 dec AreaObjectLength,x ;otherwise decrement length or get rid of it 3353 ProcLoopb: dex ;decrement buffer offset 3354 bpl ProcADLoop ;and loopback unless exceeded buffer 3355 lda BehindAreaParserFlag ;check for flag set if objects were behind renderer 3356 bne ProcessAreaData ;branch if true to load more level data, otherwise 3357 lda BackloadingFlag ;check for flag set if starting right of page $00 3358 bne ProcessAreaData ;branch if true to load more level data, otherwise leave 3359 EndAParse: rts 3360 3361 IncAreaObjOffset: 3362 inc AreaDataOffset ;increment offset of level pointer 3363 inc AreaDataOffset 3364 lda #$00 ;reset page select 3365 sta AreaObjectPageSel 3366 rts 3367 3368 DecodeAreaData: 3369 lda AreaObjectLength,x ;check current buffer flag 3370 bmi Chk1stB 3371 ldy AreaObjOffsetBuffer,x ;if not, get offset from buffer 3372 Chk1stB: ldx #$10 ;load offset of 16 for special row 15 3373 lda (AreaData),y ;get first byte of level object again 3374 cmp #$fd 3375 beq EndAParse ;if end of level, leave this routine 3376 and #$0f ;otherwise, mask out low nybble 3377 cmp #$0f ;row 15? 3378 beq ChkRow14 ;if so, keep the offset of 16 3379 ldx #$08 ;otherwise load offset of 8 for special row 12 3380 cmp #$0c ;row 12? 3381 beq ChkRow14 ;if so, keep the offset value of 8 3382 ldx #$00 ;otherwise nullify value by default 3383 ChkRow14: stx $07 ;store whatever value we just loaded here 3384 ldx ObjectOffset ;get object offset again 3385 cmp #$0e ;row 14? 3386 bne ChkRow13 3387 lda #$00 ;if so, load offset with $00 3388 sta $07 3389 lda #$2e ;and load A with another value 3390 bne NormObj ;unconditional branch 3391 ChkRow13: cmp #$0d ;row 13? 3392 bne ChkSRows 3393 lda #$22 ;if so, load offset with 34 3394 sta $07 3395 iny ;get next byte 3396 lda (AreaData),y 3397 and #%01000000 ;mask out all but d6 (page control obj bit) 3398 beq LeavePar ;if d6 clear, branch to leave (we handled this earlier) 3399 lda (AreaData),y ;otherwise, get byte again 3400 and #%01111111 ;mask out d7 3401 cmp #$4b ;check for loop command in low nybble 3402 bne Mask2MSB ;(plus d6 set for object other than page control) 3403 inc LoopCommand ;if loop command, set loop command flag 3404 Mask2MSB: and #%00111111 ;mask out d7 and d6 3405 jmp NormObj ;and jump 3406 ChkSRows: cmp #$0c ;row 12-15? 3407 bcs SpecObj 3408 iny ;if not, get second byte of level object 3409 lda (AreaData),y 3410 and #%01110000 ;mask out all but d6-d4 3411 bne LrgObj ;if any bits set, branch to handle large object 3412 lda #$16 3413 sta $07 ;otherwise set offset of 24 for small object 3414 lda (AreaData),y ;reload second byte of level object 3415 and #%00001111 ;mask out higher nybble and jump 3416 jmp NormObj 3417 LrgObj: sta $00 ;store value here (branch for large objects) 3418 cmp #$70 ;check for vertical pipe object 3419 bne NotWPipe 3420 lda (AreaData),y ;if not, reload second byte 3421 and #%00001000 ;mask out all but d3 (usage control bit) 3422 beq NotWPipe ;if d3 clear, branch to get original value 3423 lda #$00 ;otherwise, nullify value for warp pipe 3424 sta $00 3425 NotWPipe: lda $00 ;get value and jump ahead 3426 jmp MoveAOId 3427 SpecObj: iny ;branch here for rows 12-15 3428 lda (AreaData),y 3429 and #%01110000 ;get next byte and mask out all but d6-d4 3430 MoveAOId: lsr ;move d6-d4 to lower nybble 3431 lsr 3432 lsr 3433 lsr 3434 NormObj: sta $00 ;store value here (branch for small objects and rows 13 and 14) 3435 lda AreaObjectLength,x ;is there something stored here already? 3436 bpl RunAObj ;if so, branch to do its particular sub 3437 lda AreaObjectPageLoc ;otherwise check to see if the object we've loaded is on the 3438 cmp CurrentPageLoc ;same page as the renderer, and if so, branch 3439 beq InitRear 3440 ldy AreaDataOffset ;if not, get old offset of level pointer 3441 lda (AreaData),y ;and reload first byte 3442 and #%00001111 3443 cmp #$0e ;row 14? 3444 bne LeavePar 3445 lda BackloadingFlag ;if so, check backloading flag 3446 bne StrAObj ;if set, branch to render object, else leave 3447 LeavePar: rts 3448 InitRear: lda BackloadingFlag ;check backloading flag to see if it's been initialized 3449 beq BackColC ;branch to column-wise check 3450 lda #$00 ;if not, initialize both backloading and 3451 sta BackloadingFlag ;behind-renderer flags and leave 3452 sta BehindAreaParserFlag 3453 sta ObjectOffset 3454 LoopCmdE: rts 3455 BackColC: ldy AreaDataOffset ;get first byte again 3456 lda (AreaData),y 3457 and #%11110000 ;mask out low nybble and move high to low 3458 lsr 3459 lsr 3460 lsr 3461 lsr 3462 cmp CurrentColumnPos ;is this where we're at? 3463 bne LeavePar ;if not, branch to leave 3464 StrAObj: lda AreaDataOffset ;if so, load area obj offset and store in buffer 3465 sta AreaObjOffsetBuffer,x 3466 jsr IncAreaObjOffset ;do sub to increment to next object data 3467 RunAObj: lda $00 ;get stored value and add offset to it 3468 clc ;then use the jump engine with current contents of A 3469 adc $07 3470 jsr JumpEngine 3471 3472 ;large objects (rows $00-$0b or 00-11, d6-d4 set) 3473 .dw VerticalPipe ;used by warp pipes 3474 .dw AreaStyleObject 3475 .dw RowOfBricks 3476 .dw RowOfSolidBlocks 3477 .dw RowOfCoins 3478 .dw ColumnOfBricks 3479 .dw ColumnOfSolidBlocks 3480 .dw VerticalPipe ;used by decoration pipes 3481 3482 ;objects for special row $0c or 12 3483 .dw Hole_Empty 3484 .dw PulleyRopeObject 3485 .dw Bridge_High 3486 .dw Bridge_Middle 3487 .dw Bridge_Low 3488 .dw Hole_Water 3489 .dw QuestionBlockRow_High 3490 .dw QuestionBlockRow_Low 3491 3492 ;objects for special row $0f or 15 3493 .dw EndlessRope 3494 .dw BalancePlatRope 3495 .dw CastleObject 3496 .dw StaircaseObject 3497 .dw ExitPipe 3498 .dw FlagBalls_Residual 3499 3500 ;small objects (rows $00-$0b or 00-11, d6-d4 all clear) 3501 .dw QuestionBlock ;power-up 3502 .dw QuestionBlock ;coin 3503 .dw QuestionBlock ;hidden, coin 3504 .dw Hidden1UpBlock ;hidden, 1-up 3505 .dw BrickWithItem ;brick, power-up 3506 .dw BrickWithItem ;brick, vine 3507 .dw BrickWithItem ;brick, star 3508 .dw BrickWithCoins ;brick, coins 3509 .dw BrickWithItem ;brick, 1-up 3510 .dw WaterPipe 3511 .dw EmptyBlock 3512 .dw Jumpspring 3513 3514 ;objects for special row $0d or 13 (d6 set) 3515 .dw IntroPipe 3516 .dw FlagpoleObject 3517 .dw AxeObj 3518 .dw ChainObj 3519 .dw CastleBridgeObj 3520 .dw ScrollLockObject_Warp 3521 .dw ScrollLockObject 3522 .dw ScrollLockObject 3523 .dw AreaFrenzy ;flying cheep-cheeps 3524 .dw AreaFrenzy ;bullet bills or swimming cheep-cheeps 3525 .dw AreaFrenzy ;stop frenzy 3526 .dw LoopCmdE 3527 3528 ;object for special row $0e or 14 3529 .dw AlterAreaAttributes 3530 3531 ;------------------------------------------------------------------------------------- 3532 ;(these apply to all area object subroutines in this section unless otherwise stated) 3533 ;$00 - used to store offset used to find object code 3534 ;$07 - starts with adder from area parser, used to store row offset 3535 3536 AlterAreaAttributes: 3537 ldy AreaObjOffsetBuffer,x ;load offset for level object data saved in buffer 3538 iny ;load second byte 3539 lda (AreaData),y 3540 pha ;save in stack for now 3541 and #%01000000 3542 bne Alter2 ;branch if d6 is set 3543 pla 3544 pha ;pull and push offset to copy to A 3545 and #%00001111 ;mask out high nybble and store as 3546 sta TerrainControl ;new terrain height type bits 3547 pla 3548 and #%00110000 ;pull and mask out all but d5 and d4 3549 lsr ;move bits to lower nybble and store 3550 lsr ;as new background scenery bits 3551 lsr 3552 lsr 3553 sta BackgroundScenery ;then leave 3554 rts 3555 Alter2: pla 3556 and #%00000111 ;mask out all but 3 LSB 3557 cmp #$04 ;if four or greater, set color control bits 3558 bcc SetFore ;and nullify foreground scenery bits 3559 sta BackgroundColorCtrl 3560 lda #$00 3561 SetFore: sta ForegroundScenery ;otherwise set new foreground scenery bits 3562 rts 3563 3564 ;-------------------------------- 3565 3566 ScrollLockObject_Warp: 3567 ldx #$04 ;load value of 4 for game text routine as default 3568 lda WorldNumber ;warp zone (4-3-2), then check world number 3569 beq WarpNum 3570 inx ;if world number > 1, increment for next warp zone (5) 3571 ldy AreaType ;check area type 3572 dey 3573 bne WarpNum ;if ground area type, increment for last warp zone 3574 inx ;(8-7-6) and move on 3575 WarpNum: txa 3576 sta WarpZoneControl ;store number here to be used by warp zone routine 3577 jsr WriteGameText ;print text and warp zone numbers 3578 lda #PiranhaPlant 3579 jsr KillEnemies ;load identifier for piranha plants and do sub 3580 3581 ScrollLockObject: 3582 lda ScrollLock ;invert scroll lock to turn it on 3583 eor #%00000001 3584 sta ScrollLock 3585 rts 3586 3587 ;-------------------------------- 3588 ;$00 - used to store enemy identifier in KillEnemies 3589 3590 KillEnemies: 3591 sta $00 ;store identifier here 3592 lda #$00 3593 ldx #$04 ;check for identifier in enemy object buffer 3594 KillELoop: ldy Enemy_ID,x 3595 cpy $00 ;if not found, branch 3596 bne NoKillE 3597 sta Enemy_Flag,x ;if found, deactivate enemy object flag 3598 NoKillE: dex ;do this until all slots are checked 3599 bpl KillELoop 3600 rts 3601 3602 ;-------------------------------- 3603 3604 FrenzyIDData: 3605 .db FlyCheepCheepFrenzy, BBill_CCheep_Frenzy, Stop_Frenzy 3606 3607 AreaFrenzy: ldx $00 ;use area object identifier bit as offset 3608 lda FrenzyIDData-8,x ;note that it starts at 8, thus weird address here 3609 ldy #$05 3610 FreCompLoop: dey ;check regular slots of enemy object buffer 3611 bmi ExitAFrenzy ;if all slots checked and enemy object not found, branch to store 3612 cmp Enemy_ID,y ;check for enemy object in buffer versus frenzy object 3613 bne FreCompLoop 3614 lda #$00 ;if enemy object already present, nullify queue and leave 3615 ExitAFrenzy: sta EnemyFrenzyQueue ;store enemy into frenzy queue 3616 rts 3617 3618 ;-------------------------------- 3619 ;$06 - used by MushroomLedge to store length 3620 3621 AreaStyleObject: 3622 lda AreaStyle ;load level object style and jump to the right sub 3623 jsr JumpEngine 3624 .dw TreeLedge ;also used for cloud type levels 3625 .dw MushroomLedge 3626 .dw BulletBillCannon 3627 3628 TreeLedge: 3629 jsr GetLrgObjAttrib ;get row and length of green ledge 3630 lda AreaObjectLength,x ;check length counter for expiration 3631 beq EndTreeL 3632 bpl MidTreeL 3633 tya 3634 sta AreaObjectLength,x ;store lower nybble into buffer flag as length of ledge 3635 lda CurrentPageLoc 3636 ora CurrentColumnPos ;are we at the start of the level? 3637 beq MidTreeL 3638 lda #$16 ;render start of tree ledge 3639 jmp NoUnder 3640 MidTreeL: ldx $07 3641 lda #$17 ;render middle of tree ledge 3642 sta MetatileBuffer,x ;note that this is also used if ledge position is 3643 lda #$4c ;at the start of level for continuous effect 3644 jmp AllUnder ;now render the part underneath 3645 EndTreeL: lda #$18 ;render end of tree ledge 3646 jmp NoUnder 3647 3648 MushroomLedge: 3649 jsr ChkLrgObjLength ;get shroom dimensions 3650 sty $06 ;store length here for now 3651 bcc EndMushL 3652 lda AreaObjectLength,x ;divide length by 2 and store elsewhere 3653 lsr 3654 sta MushroomLedgeHalfLen,x 3655 lda #$19 ;render start of mushroom 3656 jmp NoUnder 3657 EndMushL: lda #$1b ;if at the end, render end of mushroom 3658 ldy AreaObjectLength,x 3659 beq NoUnder 3660 lda MushroomLedgeHalfLen,x ;get divided length and store where length 3661 sta $06 ;was stored originally 3662 ldx $07 3663 lda #$1a 3664 sta MetatileBuffer,x ;render middle of mushroom 3665 cpy $06 ;are we smack dab in the center? 3666 bne MushLExit ;if not, branch to leave 3667 inx 3668 lda #$4f 3669 sta MetatileBuffer,x ;render stem top of mushroom underneath the middle 3670 lda #$50 3671 AllUnder: inx 3672 ldy #$0f ;set $0f to render all way down 3673 jmp RenderUnderPart ;now render the stem of mushroom 3674 NoUnder: ldx $07 ;load row of ledge 3675 ldy #$00 ;set 0 for no bottom on this part 3676 jmp RenderUnderPart 3677 3678 ;-------------------------------- 3679 3680 ;tiles used by pulleys and rope object 3681 PulleyRopeMetatiles: 3682 .db $42, $41, $43 3683 3684 PulleyRopeObject: 3685 jsr ChkLrgObjLength ;get length of pulley/rope object 3686 ldy #$00 ;initialize metatile offset 3687 bcs RenderPul ;if starting, render left pulley 3688 iny 3689 lda AreaObjectLength,x ;if not at the end, render rope 3690 bne RenderPul 3691 iny ;otherwise render right pulley 3692 RenderPul: lda PulleyRopeMetatiles,y 3693 sta MetatileBuffer ;render at the top of the screen 3694 MushLExit: rts ;and leave 3695 3696 ;-------------------------------- 3697 ;$06 - used to store upper limit of rows for CastleObject 3698 3699 CastleMetatiles: 3700 .db $00, $45, $45, $45, $00 3701 .db $00, $48, $47, $46, $00 3702 .db $45, $49, $49, $49, $45 3703 .db $47, $47, $4a, $47, $47 3704 .db $47, $47, $4b, $47, $47 3705 .db $49, $49, $49, $49, $49 3706 .db $47, $4a, $47, $4a, $47 3707 .db $47, $4b, $47, $4b, $47 3708 .db $47, $47, $47, $47, $47 3709 .db $4a, $47, $4a, $47, $4a 3710 .db $4b, $47, $4b, $47, $4b 3711 3712 CastleObject: 3713 jsr GetLrgObjAttrib ;save lower nybble as starting row 3714 sty $07 ;if starting row is above $0a, game will crash!!! 3715 ldy #$04 3716 jsr ChkLrgObjFixedLength ;load length of castle if not already loaded 3717 txa 3718 pha ;save obj buffer offset to stack 3719 ldy AreaObjectLength,x ;use current length as offset for castle data 3720 ldx $07 ;begin at starting row 3721 lda #$0b 3722 sta $06 ;load upper limit of number of rows to print 3723 CRendLoop: lda CastleMetatiles,y ;load current byte using offset 3724 sta MetatileBuffer,x 3725 inx ;store in buffer and increment buffer offset 3726 lda $06 3727 beq ChkCFloor ;have we reached upper limit yet? 3728 iny ;if not, increment column-wise 3729 iny ;to byte in next row 3730 iny 3731 iny 3732 iny 3733 dec $06 ;move closer to upper limit 3734 ChkCFloor: cpx #$0b ;have we reached the row just before floor? 3735 bne CRendLoop ;if not, go back and do another row 3736 pla 3737 tax ;get obj buffer offset from before 3738 lda CurrentPageLoc 3739 beq ExitCastle ;if we're at page 0, we do not need to do anything else 3740 lda AreaObjectLength,x ;check length 3741 cmp #$01 ;if length almost about to expire, put brick at floor 3742 beq PlayerStop 3743 ldy $07 ;check starting row for tall castle ($00) 3744 bne NotTall 3745 cmp #$03 ;if found, then check to see if we're at the second column 3746 beq PlayerStop 3747 NotTall: cmp #$02 ;if not tall castle, check to see if we're at the third column 3748 bne ExitCastle ;if we aren't and the castle is tall, don't create flag yet 3749 jsr GetAreaObjXPosition ;otherwise, obtain and save horizontal pixel coordinate 3750 pha 3751 jsr FindEmptyEnemySlot ;find an empty place on the enemy object buffer 3752 pla 3753 sta Enemy_X_Position,x ;then write horizontal coordinate for star flag 3754 lda CurrentPageLoc 3755 sta Enemy_PageLoc,x ;set page location for star flag 3756 lda #$01 3757 sta Enemy_Y_HighPos,x ;set vertical high byte 3758 sta Enemy_Flag,x ;set flag for buffer 3759 lda #$90 3760 sta Enemy_Y_Position,x ;set vertical coordinate 3761 lda #StarFlagObject ;set star flag value in buffer itself 3762 sta Enemy_ID,x 3763 rts 3764 PlayerStop: ldy #$52 ;put brick at floor to stop player at end of level 3765 sty MetatileBuffer+10 ;this is only done if we're on the second column 3766 ExitCastle: rts 3767 3768 ;-------------------------------- 3769 3770 WaterPipe: 3771 jsr GetLrgObjAttrib ;get row and lower nybble 3772 ldy AreaObjectLength,x ;get length (residual code, water pipe is 1 col thick) 3773 ldx $07 ;get row 3774 lda #$6b 3775 sta MetatileBuffer,x ;draw something here and below it 3776 lda #$6c 3777 sta MetatileBuffer+1,x 3778 rts 3779 3780 ;-------------------------------- 3781 ;$05 - used to store length of vertical shaft in RenderSidewaysPipe 3782 ;$06 - used to store leftover horizontal length in RenderSidewaysPipe 3783 ; and vertical length in VerticalPipe and GetPipeHeight 3784 3785 IntroPipe: 3786 ldy #$03 ;check if length set, if not set, set it 3787 jsr ChkLrgObjFixedLength 3788 ldy #$0a ;set fixed value and render the sideways part 3789 jsr RenderSidewaysPipe 3790 bcs NoBlankP ;if carry flag set, not time to draw vertical pipe part 3791 ldx #$06 ;blank everything above the vertical pipe part 3792 VPipeSectLoop: lda #$00 ;all the way to the top of the screen 3793 sta MetatileBuffer,x ;because otherwise it will look like exit pipe 3794 dex 3795 bpl VPipeSectLoop 3796 lda VerticalPipeData,y ;draw the end of the vertical pipe part 3797 sta MetatileBuffer+7 3798 NoBlankP: rts 3799 3800 SidePipeShaftData: 3801 .db $15, $14 ;used to control whether or not vertical pipe shaft 3802 .db $00, $00 ;is drawn, and if so, controls the metatile number 3803 SidePipeTopPart: 3804 .db $15, $1e ;top part of sideways part of pipe 3805 .db $1d, $1c 3806 SidePipeBottomPart: 3807 .db $15, $21 ;bottom part of sideways part of pipe 3808 .db $20, $1f 3809 3810 ExitPipe: 3811 ldy #$03 ;check if length set, if not set, set it 3812 jsr ChkLrgObjFixedLength 3813 jsr GetLrgObjAttrib ;get vertical length, then plow on through RenderSidewaysPipe 3814 3815 RenderSidewaysPipe: 3816 dey ;decrement twice to make room for shaft at bottom 3817 dey ;and store here for now as vertical length 3818 sty $05 3819 ldy AreaObjectLength,x ;get length left over and store here 3820 sty $06 3821 ldx $05 ;get vertical length plus one, use as buffer offset 3822 inx 3823 lda SidePipeShaftData,y ;check for value $00 based on horizontal offset 3824 cmp #$00 3825 beq DrawSidePart ;if found, do not draw the vertical pipe shaft 3826 ldx #$00 3827 ldy $05 ;init buffer offset and get vertical length 3828 jsr RenderUnderPart ;and render vertical shaft using tile number in A 3829 clc ;clear carry flag to be used by IntroPipe 3830 DrawSidePart: ldy $06 ;render side pipe part at the bottom 3831 lda SidePipeTopPart,y 3832 sta MetatileBuffer,x ;note that the pipe parts are stored 3833 lda SidePipeBottomPart,y ;backwards horizontally 3834 sta MetatileBuffer+1,x 3835 rts 3836 3837 VerticalPipeData: 3838 .db $11, $10 ;used by pipes that lead somewhere 3839 .db $15, $14 3840 .db $13, $12 ;used by decoration pipes 3841 .db $15, $14 3842 3843 VerticalPipe: 3844 jsr GetPipeHeight 3845 lda $00 ;check to see if value was nullified earlier 3846 beq WarpPipe ;(if d3, the usage control bit of second byte, was set) 3847 iny 3848 iny 3849 iny 3850 iny ;add four if usage control bit was not set 3851 WarpPipe: tya ;save value in stack 3852 pha 3853 lda AreaNumber 3854 ora WorldNumber ;if at world 1-1, do not add piranha plant ever 3855 beq DrawPipe 3856 ldy AreaObjectLength,x ;if on second column of pipe, branch 3857 beq DrawPipe ;(because we only need to do this once) 3858 jsr FindEmptyEnemySlot ;check for an empty moving data buffer space 3859 bcs DrawPipe ;if not found, too many enemies, thus skip 3860 jsr GetAreaObjXPosition ;get horizontal pixel coordinate 3861 clc 3862 adc #$08 ;add eight to put the piranha plant in the center 3863 sta Enemy_X_Position,x ;store as enemy's horizontal coordinate 3864 lda CurrentPageLoc ;add carry to current page number 3865 adc #$00 3866 sta Enemy_PageLoc,x ;store as enemy's page coordinate 3867 lda #$01 3868 sta Enemy_Y_HighPos,x 3869 sta Enemy_Flag,x ;activate enemy flag 3870 jsr GetAreaObjYPosition ;get piranha plant's vertical coordinate and store here 3871 sta Enemy_Y_Position,x 3872 lda #PiranhaPlant ;write piranha plant's value into buffer 3873 sta Enemy_ID,x 3874 jsr InitPiranhaPlant 3875 DrawPipe: pla ;get value saved earlier and use as Y 3876 tay 3877 ldx $07 ;get buffer offset 3878 lda VerticalPipeData,y ;draw the appropriate pipe with the Y we loaded earlier 3879 sta MetatileBuffer,x ;render the top of the pipe 3880 inx 3881 lda VerticalPipeData+2,y ;render the rest of the pipe 3882 ldy $06 ;subtract one from length and render the part underneath 3883 dey 3884 jmp RenderUnderPart 3885 3886 GetPipeHeight: 3887 ldy #$01 ;check for length loaded, if not, load 3888 jsr ChkLrgObjFixedLength ;pipe length of 2 (horizontal) 3889 jsr GetLrgObjAttrib 3890 tya ;get saved lower nybble as height 3891 and #$07 ;save only the three lower bits as 3892 sta $06 ;vertical length, then load Y with 3893 ldy AreaObjectLength,x ;length left over 3894 rts 3895 3896 FindEmptyEnemySlot: 3897 ldx #$00 ;start at first enemy slot 3898 EmptyChkLoop: clc ;clear carry flag by default 3899 lda Enemy_Flag,x ;check enemy buffer for nonzero 3900 beq ExitEmptyChk ;if zero, leave 3901 inx 3902 cpx #$05 ;if nonzero, check next value 3903 bne EmptyChkLoop 3904 ExitEmptyChk: rts ;if all values nonzero, carry flag is set 3905 3906 ;-------------------------------- 3907 3908 Hole_Water: 3909 jsr ChkLrgObjLength ;get low nybble and save as length 3910 lda #$86 ;render waves 3911 sta MetatileBuffer+10 3912 ldx #$0b 3913 ldy #$01 ;now render the water underneath 3914 lda #$87 3915 jmp RenderUnderPart 3916 3917 ;-------------------------------- 3918 3919 QuestionBlockRow_High: 3920 lda #$03 ;start on the fourth row 3921 .db $2c ;BIT instruction opcode 3922 3923 QuestionBlockRow_Low: 3924 lda #$07 ;start on the eighth row 3925 pha ;save whatever row to the stack for now 3926 jsr ChkLrgObjLength ;get low nybble and save as length 3927 pla 3928 tax ;render question boxes with coins 3929 lda #$c0 3930 sta MetatileBuffer,x 3931 rts 3932 3933 ;-------------------------------- 3934 3935 Bridge_High: 3936 lda #$06 ;start on the seventh row from top of screen 3937 .db $2c ;BIT instruction opcode 3938 3939 Bridge_Middle: 3940 lda #$07 ;start on the eighth row 3941 .db $2c ;BIT instruction opcode 3942 3943 Bridge_Low: 3944 lda #$09 ;start on the tenth row 3945 pha ;save whatever row to the stack for now 3946 jsr ChkLrgObjLength ;get low nybble and save as length 3947 pla 3948 tax ;render bridge railing 3949 lda #$0b 3950 sta MetatileBuffer,x 3951 inx 3952 ldy #$00 ;now render the bridge itself 3953 lda #$63 3954 jmp RenderUnderPart 3955 3956 ;-------------------------------- 3957 3958 FlagBalls_Residual: 3959 jsr GetLrgObjAttrib ;get low nybble from object byte 3960 ldx #$02 ;render flag balls on third row from top 3961 lda #$6d ;of screen downwards based on low nybble 3962 jmp RenderUnderPart 3963 3964 ;-------------------------------- 3965 3966 FlagpoleObject: 3967 lda #$24 ;render flagpole ball on top 3968 sta MetatileBuffer 3969 ldx #$01 ;now render the flagpole shaft 3970 ldy #$08 3971 lda #$25 3972 jsr RenderUnderPart 3973 lda #$61 ;render solid block at the bottom 3974 sta MetatileBuffer+10 3975 jsr GetAreaObjXPosition 3976 sec ;get pixel coordinate of where the flagpole is, 3977 sbc #$08 ;subtract eight pixels and use as horizontal 3978 sta Enemy_X_Position+5 ;coordinate for the flag 3979 lda CurrentPageLoc 3980 sbc #$00 ;subtract borrow from page location and use as 3981 sta Enemy_PageLoc+5 ;page location for the flag 3982 lda #$30 3983 sta Enemy_Y_Position+5 ;set vertical coordinate for flag 3984 lda #$b0 3985 sta FlagpoleFNum_Y_Pos ;set initial vertical coordinate for flagpole's floatey number 3986 lda #FlagpoleFlagObject 3987 sta Enemy_ID+5 ;set flag identifier, note that identifier and coordinates 3988 inc Enemy_Flag+5 ;use last space in enemy object buffer 3989 rts 3990 3991 ;-------------------------------- 3992 3993 EndlessRope: 3994 ldx #$00 ;render rope from the top to the bottom of screen 3995 ldy #$0f 3996 jmp DrawRope 3997 3998 BalancePlatRope: 3999 txa ;save object buffer offset for now 4000 pha 4001 ldx #$01 ;blank out all from second row to the bottom 4002 ldy #$0f ;with blank used for balance platform rope 4003 lda #$44 4004 jsr RenderUnderPart 4005 pla ;get back object buffer offset 4006 tax 4007 jsr GetLrgObjAttrib ;get vertical length from lower nybble 4008 ldx #$01 4009 DrawRope: lda #$40 ;render the actual rope 4010 jmp RenderUnderPart 4011 4012 ;-------------------------------- 4013 4014 CoinMetatileData: 4015 .db $c3, $c2, $c2, $c2 4016 4017 RowOfCoins: 4018 ldy AreaType ;get area type 4019 lda CoinMetatileData,y ;load appropriate coin metatile 4020 jmp GetRow 4021 4022 ;-------------------------------- 4023 4024 C_ObjectRow: 4025 .db $06, $07, $08 4026 4027 C_ObjectMetatile: 4028 .db $c5, $0c, $89 4029 4030 CastleBridgeObj: 4031 ldy #$0c ;load length of 13 columns 4032 jsr ChkLrgObjFixedLength 4033 jmp ChainObj 4034 4035 AxeObj: 4036 lda #$08 ;load bowser's palette into sprite portion of palette 4037 sta VRAM_Buffer_AddrCtrl 4038 4039 ChainObj: 4040 ldy $00 ;get value loaded earlier from decoder 4041 ldx C_ObjectRow-2,y ;get appropriate row and metatile for object 4042 lda C_ObjectMetatile-2,y 4043 jmp ColObj 4044 4045 EmptyBlock: 4046 jsr GetLrgObjAttrib ;get row location 4047 ldx $07 4048 lda #$c4 4049 ColObj: ldy #$00 ;column length of 1 4050 jmp RenderUnderPart 4051 4052 ;-------------------------------- 4053 4054 SolidBlockMetatiles: 4055 .db $69, $61, $61, $62 4056 4057 BrickMetatiles: 4058 .db $22, $51, $52, $52 4059 .db $88 ;used only by row of bricks object 4060 4061 RowOfBricks: 4062 ldy AreaType ;load area type obtained from area offset pointer 4063 lda CloudTypeOverride ;check for cloud type override 4064 beq DrawBricks 4065 ldy #$04 ;if cloud type, override area type 4066 DrawBricks: lda BrickMetatiles,y ;get appropriate metatile 4067 jmp GetRow ;and go render it 4068 4069 RowOfSolidBlocks: 4070 ldy AreaType ;load area type obtained from area offset pointer 4071 lda SolidBlockMetatiles,y ;get metatile 4072 GetRow: pha ;store metatile here 4073 jsr ChkLrgObjLength ;get row number, load length 4074 DrawRow: ldx $07 4075 ldy #$00 ;set vertical height of 1 4076 pla 4077 jmp RenderUnderPart ;render object 4078 4079 ColumnOfBricks: 4080 ldy AreaType ;load area type obtained from area offset 4081 lda BrickMetatiles,y ;get metatile (no cloud override as for row) 4082 jmp GetRow2 4083 4084 ColumnOfSolidBlocks: 4085 ldy AreaType ;load area type obtained from area offset 4086 lda SolidBlockMetatiles,y ;get metatile 4087 GetRow2: pha ;save metatile to stack for now 4088 jsr GetLrgObjAttrib ;get length and row 4089 pla ;restore metatile 4090 ldx $07 ;get starting row 4091 jmp RenderUnderPart ;now render the column 4092 4093 ;-------------------------------- 4094 4095 BulletBillCannon: 4096 jsr GetLrgObjAttrib ;get row and length of bullet bill cannon 4097 ldx $07 ;start at first row 4098 lda #$64 ;render bullet bill cannon 4099 sta MetatileBuffer,x 4100 inx 4101 dey ;done yet? 4102 bmi SetupCannon 4103 lda #$65 ;if not, render middle part 4104 sta MetatileBuffer,x 4105 inx 4106 dey ;done yet? 4107 bmi SetupCannon 4108 lda #$66 ;if not, render bottom until length expires 4109 jsr RenderUnderPart 4110 SetupCannon: ldx Cannon_Offset ;get offset for data used by cannons and whirlpools 4111 jsr GetAreaObjYPosition ;get proper vertical coordinate for cannon 4112 sta Cannon_Y_Position,x ;and store it here 4113 lda CurrentPageLoc 4114 sta Cannon_PageLoc,x ;store page number for cannon here 4115 jsr GetAreaObjXPosition ;get proper horizontal coordinate for cannon 4116 sta Cannon_X_Position,x ;and store it here 4117 inx 4118 cpx #$06 ;increment and check offset 4119 bcc StrCOffset ;if not yet reached sixth cannon, branch to save offset 4120 ldx #$00 ;otherwise initialize it 4121 StrCOffset: stx Cannon_Offset ;save new offset and leave 4122 rts 4123 4124 ;-------------------------------- 4125 4126 StaircaseHeightData: 4127 .db $07, $07, $06, $05, $04, $03, $02, $01, $00 4128 4129 StaircaseRowData: 4130 .db $03, $03, $04, $05, $06, $07, $08, $09, $0a 4131 4132 StaircaseObject: 4133 jsr ChkLrgObjLength ;check and load length 4134 bcc NextStair ;if length already loaded, skip init part 4135 lda #$09 ;start past the end for the bottom 4136 sta StaircaseControl ;of the staircase 4137 NextStair: dec StaircaseControl ;move onto next step (or first if starting) 4138 ldy StaircaseControl 4139 ldx StaircaseRowData,y ;get starting row and height to render 4140 lda StaircaseHeightData,y 4141 tay 4142 lda #$61 ;now render solid block staircase 4143 jmp RenderUnderPart 4144 4145 ;-------------------------------- 4146 4147 Jumpspring: 4148 jsr GetLrgObjAttrib 4149 jsr FindEmptyEnemySlot ;find empty space in enemy object buffer 4150 jsr GetAreaObjXPosition ;get horizontal coordinate for jumpspring 4151 sta Enemy_X_Position,x ;and store 4152 lda CurrentPageLoc ;store page location of jumpspring 4153 sta Enemy_PageLoc,x 4154 jsr GetAreaObjYPosition ;get vertical coordinate for jumpspring 4155 sta Enemy_Y_Position,x ;and store 4156 sta Jumpspring_FixedYPos,x ;store as permanent coordinate here 4157 lda #JumpspringObject 4158 sta Enemy_ID,x ;write jumpspring object to enemy object buffer 4159 ldy #$01 4160 sty Enemy_Y_HighPos,x ;store vertical high byte 4161 inc Enemy_Flag,x ;set flag for enemy object buffer 4162 ldx $07 4163 lda #$67 ;draw metatiles in two rows where jumpspring is 4164 sta MetatileBuffer,x 4165 lda #$68 4166 sta MetatileBuffer+1,x 4167 rts 4168 4169 ;-------------------------------- 4170 ;$07 - used to save ID of brick object 4171 4172 Hidden1UpBlock: 4173 lda Hidden1UpFlag ;if flag not set, do not render object 4174 beq ExitDecBlock 4175 lda #$00 ;if set, init for the next one 4176 sta Hidden1UpFlag 4177 jmp BrickWithItem ;jump to code shared with unbreakable bricks 4178 4179 QuestionBlock: 4180 jsr GetAreaObjectID ;get value from level decoder routine 4181 jmp DrawQBlk ;go to render it 4182 4183 BrickWithCoins: 4184 lda #$00 ;initialize multi-coin timer flag 4185 sta BrickCoinTimerFlag 4186 4187 BrickWithItem: 4188 jsr GetAreaObjectID ;save area object ID 4189 sty $07 4190 lda #$00 ;load default adder for bricks with lines 4191 ldy AreaType ;check level type for ground level 4192 dey 4193 beq BWithL ;if ground type, do not start with 5 4194 lda #$05 ;otherwise use adder for bricks without lines 4195 BWithL: clc ;add object ID to adder 4196 adc $07 4197 tay ;use as offset for metatile 4198 DrawQBlk: lda BrickQBlockMetatiles,y ;get appropriate metatile for brick (question block 4199 pha ;if branched to here from question block routine) 4200 jsr GetLrgObjAttrib ;get row from location byte 4201 jmp DrawRow ;now render the object 4202 4203 GetAreaObjectID: 4204 lda $00 ;get value saved from area parser routine 4205 sec 4206 sbc #$00 ;possibly residual code 4207 tay ;save to Y 4208 ExitDecBlock: rts 4209 4210 ;-------------------------------- 4211 4212 HoleMetatiles: 4213 .db $87, $00, $00, $00 4214 4215 Hole_Empty: 4216 jsr ChkLrgObjLength ;get lower nybble and save as length 4217 bcc NoWhirlP ;skip this part if length already loaded 4218 lda AreaType ;check for water type level 4219 bne NoWhirlP ;if not water type, skip this part 4220 ldx Whirlpool_Offset ;get offset for data used by cannons and whirlpools 4221 jsr GetAreaObjXPosition ;get proper vertical coordinate of where we're at 4222 sec 4223 sbc #$10 ;subtract 16 pixels 4224 sta Whirlpool_LeftExtent,x ;store as left extent of whirlpool 4225 lda CurrentPageLoc ;get page location of where we're at 4226 sbc #$00 ;subtract borrow 4227 sta Whirlpool_PageLoc,x ;save as page location of whirlpool 4228 iny 4229 iny ;increment length by 2 4230 tya 4231 asl ;multiply by 16 to get size of whirlpool 4232 asl ;note that whirlpool will always be 4233 asl ;two blocks bigger than actual size of hole 4234 asl ;and extend one block beyond each edge 4235 sta Whirlpool_Length,x ;save size of whirlpool here 4236 inx 4237 cpx #$05 ;increment and check offset 4238 bcc StrWOffset ;if not yet reached fifth whirlpool, branch to save offset 4239 ldx #$00 ;otherwise initialize it 4240 StrWOffset: stx Whirlpool_Offset ;save new offset here 4241 NoWhirlP: ldx AreaType ;get appropriate metatile, then 4242 lda HoleMetatiles,x ;render the hole proper 4243 ldx #$08 4244 ldy #$0f ;start at ninth row and go to bottom, run RenderUnderPart 4245 4246 ;-------------------------------- 4247 4248 RenderUnderPart: 4249 sty AreaObjectHeight ;store vertical length to render 4250 ldy MetatileBuffer,x ;check current spot to see if there's something 4251 beq DrawThisRow ;we need to keep, if nothing, go ahead 4252 cpy #$17 4253 beq WaitOneRow ;if middle part (tree ledge), wait until next row 4254 cpy #$1a 4255 beq WaitOneRow ;if middle part (mushroom ledge), wait until next row 4256 cpy #$c0 4257 beq DrawThisRow ;if question block w/ coin, overwrite 4258 cpy #$c0 4259 bcs WaitOneRow ;if any other metatile with palette 3, wait until next row 4260 cpy #$54 4261 bne DrawThisRow ;if cracked rock terrain, overwrite 4262 cmp #$50 4263 beq WaitOneRow ;if stem top of mushroom, wait until next row 4264 DrawThisRow: sta MetatileBuffer,x ;render contents of A from routine that called this 4265 WaitOneRow: inx 4266 cpx #$0d ;stop rendering if we're at the bottom of the screen 4267 bcs ExitUPartR 4268 ldy AreaObjectHeight ;decrement, and stop rendering if there is no more length 4269 dey 4270 bpl RenderUnderPart 4271 ExitUPartR: rts 4272 4273 ;-------------------------------- 4274 4275 ChkLrgObjLength: 4276 jsr GetLrgObjAttrib ;get row location and size (length if branched to from here) 4277 4278 ChkLrgObjFixedLength: 4279 lda AreaObjectLength,x ;check for set length counter 4280 clc ;clear carry flag for not just starting 4281 bpl LenSet ;if counter not set, load it, otherwise leave alone 4282 tya ;save length into length counter 4283 sta AreaObjectLength,x 4284 sec ;set carry flag if just starting 4285 LenSet: rts 4286 4287 4288 GetLrgObjAttrib: 4289 ldy AreaObjOffsetBuffer,x ;get offset saved from area obj decoding routine 4290 lda (AreaData),y ;get first byte of level object 4291 and #%00001111 4292 sta $07 ;save row location 4293 iny 4294 lda (AreaData),y ;get next byte, save lower nybble (length or height) 4295 and #%00001111 ;as Y, then leave 4296 tay 4297 rts 4298 4299 ;-------------------------------- 4300 4301 GetAreaObjXPosition: 4302 lda CurrentColumnPos ;multiply current offset where we're at by 16 4303 asl ;to obtain horizontal pixel coordinate 4304 asl 4305 asl 4306 asl 4307 rts 4308 4309 ;-------------------------------- 4310 4311 GetAreaObjYPosition: 4312 lda $07 ;multiply value by 16 4313 asl 4314 asl ;this will give us the proper vertical pixel coordinate 4315 asl 4316 asl 4317 clc 4318 adc #32 ;add 32 pixels for the status bar 4319 rts 4320 4321 ;------------------------------------------------------------------------------------- 4322 ;$06-$07 - used to store block buffer address used as indirect 4323 4324 BlockBufferAddr: 4325 .db <Block_Buffer_1, <Block_Buffer_2 4326 .db >Block_Buffer_1, >Block_Buffer_2 4327 4328 GetBlockBufferAddr: 4329 pha ;take value of A, save 4330 lsr ;move high nybble to low 4331 lsr 4332 lsr 4333 lsr 4334 tay ;use nybble as pointer to high byte 4335 lda BlockBufferAddr+2,y ;of indirect here 4336 sta $07 4337 pla 4338 and #%00001111 ;pull from stack, mask out high nybble 4339 clc 4340 adc BlockBufferAddr,y ;add to low byte 4341 sta $06 ;store here and leave 4342 rts 4343 4344 ;------------------------------------------------------------------------------------- 4345 4346 ;unused space 4347 .db $ff, $ff 4348 4349 ;------------------------------------------------------------------------------------- 4350 4351 AreaDataOfsLoopback: 4352 .db $12, $36, $0e, $0e, $0e, $32, $32, $32, $0a, $26, $40 4353 4354 ;------------------------------------------------------------------------------------- 4355 4356 LoadAreaPointer: 4357 jsr FindAreaPointer ;find it and store it here 4358 sta AreaPointer 4359 GetAreaType: and #%01100000 ;mask out all but d6 and d5 4360 asl 4361 rol 4362 rol 4363 rol ;make %0xx00000 into %000000xx 4364 sta AreaType ;save 2 MSB as area type 4365 rts 4366 4367 FindAreaPointer: 4368 ldy WorldNumber ;load offset from world variable 4369 lda WorldAddrOffsets,y 4370 clc ;add area number used to find data 4371 adc AreaNumber 4372 tay 4373 lda AreaAddrOffsets,y ;from there we have our area pointer 4374 rts 4375 4376 4377 GetAreaDataAddrs: 4378 lda AreaPointer ;use 2 MSB for Y 4379 jsr GetAreaType 4380 tay 4381 lda AreaPointer ;mask out all but 5 LSB 4382 and #%00011111 4383 sta AreaAddrsLOffset ;save as low offset 4384 lda EnemyAddrHOffsets,y ;load base value with 2 altered MSB, 4385 clc ;then add base value to 5 LSB, result 4386 adc AreaAddrsLOffset ;becomes offset for level data 4387 tay 4388 lda EnemyDataAddrLow,y ;use offset to load pointer 4389 sta EnemyDataLow 4390 lda EnemyDataAddrHigh,y 4391 sta EnemyDataHigh 4392 ldy AreaType ;use area type as offset 4393 lda AreaDataHOffsets,y ;do the same thing but with different base value 4394 clc 4395 adc AreaAddrsLOffset 4396 tay 4397 lda AreaDataAddrLow,y ;use this offset to load another pointer 4398 sta AreaDataLow 4399 lda AreaDataAddrHigh,y 4400 sta AreaDataHigh 4401 ldy #$00 ;load first byte of header 4402 lda (AreaData),y 4403 pha ;save it to the stack for now 4404 and #%00000111 ;save 3 LSB for foreground scenery or bg color control 4405 cmp #$04 4406 bcc StoreFore 4407 sta BackgroundColorCtrl ;if 4 or greater, save value here as bg color control 4408 lda #$00 4409 StoreFore: sta ForegroundScenery ;if less, save value here as foreground scenery 4410 pla ;pull byte from stack and push it back 4411 pha 4412 and #%00111000 ;save player entrance control bits 4413 lsr ;shift bits over to LSBs 4414 lsr 4415 lsr 4416 sta PlayerEntranceCtrl ;save value here as player entrance control 4417 pla ;pull byte again but do not push it back 4418 and #%11000000 ;save 2 MSB for game timer setting 4419 clc 4420 rol ;rotate bits over to LSBs 4421 rol 4422 rol 4423 sta GameTimerSetting ;save value here as game timer setting 4424 iny 4425 lda (AreaData),y ;load second byte of header 4426 pha ;save to stack 4427 and #%00001111 ;mask out all but lower nybble 4428 sta TerrainControl 4429 pla ;pull and push byte to copy it to A 4430 pha 4431 and #%00110000 ;save 2 MSB for background scenery type 4432 lsr 4433 lsr ;shift bits to LSBs 4434 lsr 4435 lsr 4436 sta BackgroundScenery ;save as background scenery 4437 pla 4438 and #%11000000 4439 clc 4440 rol ;rotate bits over to LSBs 4441 rol 4442 rol 4443 cmp #%00000011 ;if set to 3, store here 4444 bne StoreStyle ;and nullify other value 4445 sta CloudTypeOverride ;otherwise store value in other place 4446 lda #$00 4447 StoreStyle: sta AreaStyle 4448 lda AreaDataLow ;increment area data address by 2 bytes 4449 clc 4450 adc #$02 4451 sta AreaDataLow 4452 lda AreaDataHigh 4453 adc #$00 4454 sta AreaDataHigh 4455 rts 4456 4457 ;------------------------------------------------------------------------------------- 4458 ;GAME LEVELS DATA 4459 4460 WorldAddrOffsets: 4461 .db World1Areas-AreaAddrOffsets, World2Areas-AreaAddrOffsets 4462 .db World3Areas-AreaAddrOffsets, World4Areas-AreaAddrOffsets 4463 .db World5Areas-AreaAddrOffsets, World6Areas-AreaAddrOffsets 4464 .db World7Areas-AreaAddrOffsets, World8Areas-AreaAddrOffsets 4465 4466 AreaAddrOffsets: 4467 World1Areas: .db $25, $29, $c0, $26, $60 4468 World2Areas: .db $28, $29, $01, $27, $62 4469 World3Areas: .db $24, $35, $20, $63 4470 World4Areas: .db $22, $29, $41, $2c, $61 4471 World5Areas: .db $2a, $31, $26, $62 4472 World6Areas: .db $2e, $23, $2d, $60 4473 World7Areas: .db $33, $29, $01, $27, $64 4474 World8Areas: .db $30, $32, $21, $65 4475 4476 ;bonus area data offsets, included here for comparison purposes 4477 ;underground bonus area - c2 4478 ;cloud area 1 (day) - 2b 4479 ;cloud area 2 (night) - 34 4480 ;water area (5-2/6-2) - 00 4481 ;water area (8-4) - 02 4482 ;warp zone area (4-2) - 2f 4483 4484 EnemyAddrHOffsets: 4485 .db $1f, $06, $1c, $00 4486 4487 EnemyDataAddrLow: 4488 .db <E_CastleArea1, <E_CastleArea2, <E_CastleArea3, <E_CastleArea4, <E_CastleArea5, <E_CastleArea6 4489 .db <E_GroundArea1, <E_GroundArea2, <E_GroundArea3, <E_GroundArea4, <E_GroundArea5, <E_GroundArea6 4490 .db <E_GroundArea7, <E_GroundArea8, <E_GroundArea9, <E_GroundArea10, <E_GroundArea11, <E_GroundArea12 4491 .db <E_GroundArea13, <E_GroundArea14, <E_GroundArea15, <E_GroundArea16, <E_GroundArea17, <E_GroundArea18 4492 .db <E_GroundArea19, <E_GroundArea20, <E_GroundArea21, <E_GroundArea22, <E_UndergroundArea1 4493 .db <E_UndergroundArea2, <E_UndergroundArea3, <E_WaterArea1, <E_WaterArea2, <E_WaterArea3 4494 4495 EnemyDataAddrHigh: 4496 .db >E_CastleArea1, >E_CastleArea2, >E_CastleArea3, >E_CastleArea4, >E_CastleArea5, >E_CastleArea6 4497 .db >E_GroundArea1, >E_GroundArea2, >E_GroundArea3, >E_GroundArea4, >E_GroundArea5, >E_GroundArea6 4498 .db >E_GroundArea7, >E_GroundArea8, >E_GroundArea9, >E_GroundArea10, >E_GroundArea11, >E_GroundArea12 4499 .db >E_GroundArea13, >E_GroundArea14, >E_GroundArea15, >E_GroundArea16, >E_GroundArea17, >E_GroundArea18 4500 .db >E_GroundArea19, >E_GroundArea20, >E_GroundArea21, >E_GroundArea22, >E_UndergroundArea1 4501 .db >E_UndergroundArea2, >E_UndergroundArea3, >E_WaterArea1, >E_WaterArea2, >E_WaterArea3 4502 4503 AreaDataHOffsets: 4504 .db $00, $03, $19, $1c 4505 4506 AreaDataAddrLow: 4507 .db <L_WaterArea1, <L_WaterArea2, <L_WaterArea3, <L_GroundArea1, <L_GroundArea2, <L_GroundArea3 4508 .db <L_GroundArea4, <L_GroundArea5, <L_GroundArea6, <L_GroundArea7, <L_GroundArea8, <L_GroundArea9 4509 .db <L_GroundArea10, <L_GroundArea11, <L_GroundArea12, <L_GroundArea13, <L_GroundArea14, <L_GroundArea15 4510 .db <L_GroundArea16, <L_GroundArea17, <L_GroundArea18, <L_GroundArea19, <L_GroundArea20, <L_GroundArea21 4511 .db <L_GroundArea22, <L_UndergroundArea1, <L_UndergroundArea2, <L_UndergroundArea3, <L_CastleArea1 4512 .db <L_CastleArea2, <L_CastleArea3, <L_CastleArea4, <L_CastleArea5, <L_CastleArea6 4513 4514 AreaDataAddrHigh: 4515 .db >L_WaterArea1, >L_WaterArea2, >L_WaterArea3, >L_GroundArea1, >L_GroundArea2, >L_GroundArea3 4516 .db >L_GroundArea4, >L_GroundArea5, >L_GroundArea6, >L_GroundArea7, >L_GroundArea8, >L_GroundArea9 4517 .db >L_GroundArea10, >L_GroundArea11, >L_GroundArea12, >L_GroundArea13, >L_GroundArea14, >L_GroundArea15 4518 .db >L_GroundArea16, >L_GroundArea17, >L_GroundArea18, >L_GroundArea19, >L_GroundArea20, >L_GroundArea21 4519 .db >L_GroundArea22, >L_UndergroundArea1, >L_UndergroundArea2, >L_UndergroundArea3, >L_CastleArea1 4520 .db >L_CastleArea2, >L_CastleArea3, >L_CastleArea4, >L_CastleArea5, >L_CastleArea6 4521 4522 ;ENEMY OBJECT DATA 4523 4524 ;level 1-4/6-4 4525 E_CastleArea1: 4526 .db $76, $dd, $bb, $4c, $ea, $1d, $1b, $cc, $56, $5d 4527 .db $16, $9d, $c6, $1d, $36, $9d, $c9, $1d, $04, $db 4528 .db $49, $1d, $84, $1b, $c9, $5d, $88, $95, $0f, $08 4529 .db $30, $4c, $78, $2d, $a6, $28, $90, $b5 4530 .db $ff 4531 4532 ;level 4-4 4533 E_CastleArea2: 4534 .db $0f, $03, $56, $1b, $c9, $1b, $0f, $07, $36, $1b 4535 .db $aa, $1b, $48, $95, $0f, $0a, $2a, $1b, $5b, $0c 4536 .db $78, $2d, $90, $b5 4537 .db $ff 4538 4539 ;level 2-4/5-4 4540 E_CastleArea3: 4541 .db $0b, $8c, $4b, $4c, $77, $5f, $eb, $0c, $bd, $db 4542 .db $19, $9d, $75, $1d, $7d, $5b, $d9, $1d, $3d, $dd 4543 .db $99, $1d, $26, $9d, $5a, $2b, $8a, $2c, $ca, $1b 4544 .db $20, $95, $7b, $5c, $db, $4c, $1b, $cc, $3b, $cc 4545 .db $78, $2d, $a6, $28, $90, $b5 4546 .db $ff 4547 4548 ;level 3-4 4549 E_CastleArea4: 4550 .db $0b, $8c, $3b, $1d, $8b, $1d, $ab, $0c, $db, $1d 4551 .db $0f, $03, $65, $1d, $6b, $1b, $05, $9d, $0b, $1b 4552 .db $05, $9b, $0b, $1d, $8b, $0c, $1b, $8c, $70, $15 4553 .db $7b, $0c, $db, $0c, $0f, $08, $78, $2d, $a6, $28 4554 .db $90, $b5 4555 .db $ff 4556 4557 ;level 7-4 4558 E_CastleArea5: 4559 .db $27, $a9, $4b, $0c, $68, $29, $0f, $06, $77, $1b 4560 .db $0f, $0b, $60, $15, $4b, $8c, $78, $2d, $90, $b5 4561 .db $ff 4562 4563 ;level 8-4 4564 E_CastleArea6: 4565 .db $0f, $03, $8e, $65, $e1, $bb, $38, $6d, $a8, $3e, $e5, $e7 4566 .db $0f, $08, $0b, $02, $2b, $02, $5e, $65, $e1, $bb, $0e 4567 .db $db, $0e, $bb, $8e, $db, $0e, $fe, $65, $ec, $0f, $0d 4568 .db $4e, $65, $e1, $0f, $0e, $4e, $02, $e0, $0f, $10, $fe, $e5, $e1 4569 .db $1b, $85, $7b, $0c, $5b, $95, $78, $2d, $90, $b5 4570 .db $ff 4571 4572 ;level 3-3 4573 E_GroundArea1: 4574 .db $a5, $86, $e4, $28, $18, $a8, $45, $83, $69, $03 4575 .db $c6, $29, $9b, $83, $16, $a4, $88, $24, $e9, $28 4576 .db $05, $a8, $7b, $28, $24, $8f, $c8, $03, $e8, $03 4577 .db $46, $a8, $85, $24, $c8, $24 4578 .db $ff 4579 4580 ;level 8-3 4581 E_GroundArea2: 4582 .db $eb, $8e, $0f, $03, $fb, $05, $17, $85, $db, $8e 4583 .db $0f, $07, $57, $05, $7b, $05, $9b, $80, $2b, $85 4584 .db $fb, $05, $0f, $0b, $1b, $05, $9b, $05 4585 .db $ff 4586 4587 ;level 4-1 4588 E_GroundArea3: 4589 .db $2e, $c2, $66, $e2, $11, $0f, $07, $02, $11, $0f, $0c 4590 .db $12, $11 4591 .db $ff 4592 4593 ;level 6-2 4594 E_GroundArea4: 4595 .db $0e, $c2, $a8, $ab, $00, $bb, $8e, $6b, $82, $de, $00, $a0 4596 .db $33, $86, $43, $06, $3e, $b4, $a0, $cb, $02, $0f, $07 4597 .db $7e, $42, $a6, $83, $02, $0f, $0a, $3b, $02, $cb, $37 4598 .db $0f, $0c, $e3, $0e 4599 .db $ff 4600 4601 ;level 3-1 4602 E_GroundArea5: 4603 .db $9b, $8e, $ca, $0e, $ee, $42, $44, $5b, $86, $80, $b8 4604 .db $1b, $80, $50, $ba, $10, $b7, $5b, $00, $17, $85 4605 .db $4b, $05, $fe, $34, $40, $b7, $86, $c6, $06, $5b, $80 4606 .db $83, $00, $d0, $38, $5b, $8e, $8a, $0e, $a6, $00 4607 .db $bb, $0e, $c5, $80, $f3, $00 4608 .db $ff 4609 4610 ;level 1-1 4611 E_GroundArea6: 4612 .db $1e, $c2, $00, $6b, $06, $8b, $86, $63, $b7, $0f, $05 4613 .db $03, $06, $23, $06, $4b, $b7, $bb, $00, $5b, $b7 4614 .db $fb, $37, $3b, $b7, $0f, $0b, $1b, $37 4615 .db $ff 4616 4617 ;level 1-3/5-3 4618 E_GroundArea7: 4619 .db $2b, $d7, $e3, $03, $c2, $86, $e2, $06, $76, $a5 4620 .db $a3, $8f, $03, $86, $2b, $57, $68, $28, $e9, $28 4621 .db $e5, $83, $24, $8f, $36, $a8, $5b, $03 4622 .db $ff 4623 4624 ;level 2-3/7-3 4625 E_GroundArea8: 4626 .db $0f, $02, $78, $40, $48, $ce, $f8, $c3, $f8, $c3 4627 .db $0f, $07, $7b, $43, $c6, $d0, $0f, $8a, $c8, $50 4628 .db $ff 4629 4630 ;level 2-1 4631 E_GroundArea9: 4632 .db $85, $86, $0b, $80, $1b, $00, $db, $37, $77, $80 4633 .db $eb, $37, $fe, $2b, $20, $2b, $80, $7b, $38, $ab, $b8 4634 .db $77, $86, $fe, $42, $20, $49, $86, $8b, $06, $9b, $80 4635 .db $7b, $8e, $5b, $b7, $9b, $0e, $bb, $0e, $9b, $80 4636 ;end of data terminator here is also used by pipe intro area 4637 E_GroundArea10: 4638 .db $ff 4639 4640 ;level 5-1 4641 E_GroundArea11: 4642 .db $0b, $80, $60, $38, $10, $b8, $c0, $3b, $db, $8e 4643 .db $40, $b8, $f0, $38, $7b, $8e, $a0, $b8, $c0, $b8 4644 .db $fb, $00, $a0, $b8, $30, $bb, $ee, $42, $88, $0f, $0b 4645 .db $2b, $0e, $67, $0e 4646 .db $ff 4647 4648 ;cloud level used in levels 2-1 and 5-2 4649 E_GroundArea12: 4650 .db $0a, $aa, $0e, $28, $2a, $0e, $31, $88 4651 .db $ff 4652 4653 ;level 4-3 4654 E_GroundArea13: 4655 .db $c7, $83, $d7, $03, $42, $8f, $7a, $03, $05, $a4 4656 .db $78, $24, $a6, $25, $e4, $25, $4b, $83, $e3, $03 4657 .db $05, $a4, $89, $24, $b5, $24, $09, $a4, $65, $24 4658 .db $c9, $24, $0f, $08, $85, $25 4659 .db $ff 4660 4661 ;level 6-3 4662 E_GroundArea14: 4663 .db $cd, $a5, $b5, $a8, $07, $a8, $76, $28, $cc, $25 4664 .db $65, $a4, $a9, $24, $e5, $24, $19, $a4, $0f, $07 4665 .db $95, $28, $e6, $24, $19, $a4, $d7, $29, $16, $a9 4666 .db $58, $29, $97, $29 4667 .db $ff 4668 4669 ;level 6-1 4670 E_GroundArea15: 4671 .db $0f, $02, $02, $11, $0f, $07, $02, $11 4672 .db $ff 4673 4674 ;warp zone area used in level 4-2 4675 E_GroundArea16: 4676 .db $ff 4677 4678 ;level 8-1 4679 E_GroundArea17: 4680 .db $2b, $82, $ab, $38, $de, $42, $e2, $1b, $b8, $eb 4681 .db $3b, $db, $80, $8b, $b8, $1b, $82, $fb, $b8, $7b 4682 .db $80, $fb, $3c, $5b, $bc, $7b, $b8, $1b, $8e, $cb 4683 .db $0e, $1b, $8e, $0f, $0d, $2b, $3b, $bb, $b8, $eb, $82 4684 .db $4b, $b8, $bb, $38, $3b, $b7, $bb, $02, $0f, $13 4685 .db $1b, $00, $cb, $80, $6b, $bc 4686 .db $ff 4687 4688 ;level 5-2 4689 E_GroundArea18: 4690 .db $7b, $80, $ae, $00, $80, $8b, $8e, $e8, $05, $f9, $86 4691 .db $17, $86, $16, $85, $4e, $2b, $80, $ab, $8e, $87, $85 4692 .db $c3, $05, $8b, $82, $9b, $02, $ab, $02, $bb, $86 4693 .db $cb, $06, $d3, $03, $3b, $8e, $6b, $0e, $a7, $8e 4694 .db $ff 4695 4696 ;level 8-2 4697 E_GroundArea19: 4698 .db $29, $8e, $52, $11, $83, $0e, $0f, $03, $9b, $0e 4699 .db $2b, $8e, $5b, $0e, $cb, $8e, $fb, $0e, $fb, $82 4700 .db $9b, $82, $bb, $02, $fe, $42, $e8, $bb, $8e, $0f, $0a 4701 .db $ab, $0e, $cb, $0e, $f9, $0e, $88, $86, $a6, $06 4702 .db $db, $02, $b6, $8e 4703 .db $ff 4704 4705 ;level 7-1 4706 E_GroundArea20: 4707 .db $ab, $ce, $de, $42, $c0, $cb, $ce, $5b, $8e, $1b, $ce 4708 .db $4b, $85, $67, $45, $0f, $07, $2b, $00, $7b, $85 4709 .db $97, $05, $0f, $0a, $92, $02 4710 .db $ff 4711 4712 ;cloud level used in levels 3-1 and 6-2 4713 E_GroundArea21: 4714 .db $0a, $aa, $0e, $24, $4a, $1e, $23, $aa 4715 .db $ff 4716 4717 ;level 3-2 4718 E_GroundArea22: 4719 .db $1b, $80, $bb, $38, $4b, $bc, $eb, $3b, $0f, $04 4720 .db $2b, $00, $ab, $38, $eb, $00, $cb, $8e, $fb, $80 4721 .db $ab, $b8, $6b, $80, $fb, $3c, $9b, $bb, $5b, $bc 4722 .db $fb, $00, $6b, $b8, $fb, $38 4723 .db $ff 4724 4725 ;level 1-2 4726 E_UndergroundArea1: 4727 .db $0b, $86, $1a, $06, $db, $06, $de, $c2, $02, $f0, $3b 4728 .db $bb, $80, $eb, $06, $0b, $86, $93, $06, $f0, $39 4729 .db $0f, $06, $60, $b8, $1b, $86, $a0, $b9, $b7, $27 4730 .db $bd, $27, $2b, $83, $a1, $26, $a9, $26, $ee, $25, $0b 4731 .db $27, $b4 4732 .db $ff 4733 4734 ;level 4-2 4735 E_UndergroundArea2: 4736 .db $0f, $02, $1e, $2f, $60, $e0, $3a, $a5, $a7, $db, $80 4737 .db $3b, $82, $8b, $02, $fe, $42, $68, $70, $bb, $25, $a7 4738 .db $2c, $27, $b2, $26, $b9, $26, $9b, $80, $a8, $82 4739 .db $b5, $27, $bc, $27, $b0, $bb, $3b, $82, $87, $34 4740 .db $ee, $25, $6b 4741 .db $ff 4742 4743 ;underground bonus rooms area used in many levels 4744 E_UndergroundArea3: 4745 .db $1e, $a5, $0a, $2e, $28, $27, $2e, $33, $c7, $0f, $03, $1e, $40, $07 4746 .db $2e, $30, $e7, $0f, $05, $1e, $24, $44, $0f, $07, $1e, $22, $6a 4747 .db $2e, $23, $ab, $0f, $09, $1e, $41, $68, $1e, $2a, $8a, $2e, $23, $a2 4748 .db $2e, $32, $ea 4749 .db $ff 4750 4751 ;water area used in levels 5-2 and 6-2 4752 E_WaterArea1: 4753 .db $3b, $87, $66, $27, $cc, $27, $ee, $31, $87, $ee, $23, $a7 4754 .db $3b, $87, $db, $07 4755 .db $ff 4756 4757 ;level 2-2/7-2 4758 E_WaterArea2: 4759 .db $0f, $01, $2e, $25, $2b, $2e, $25, $4b, $4e, $25, $cb, $6b, $07 4760 .db $97, $47, $e9, $87, $47, $c7, $7a, $07, $d6, $c7 4761 .db $78, $07, $38, $87, $ab, $47, $e3, $07, $9b, $87 4762 .db $0f, $09, $68, $47, $db, $c7, $3b, $c7 4763 .db $ff 4764 4765 ;water area used in level 8-4 4766 E_WaterArea3: 4767 .db $47, $9b, $cb, $07, $fa, $1d, $86, $9b, $3a, $87 4768 .db $56, $07, $88, $1b, $07, $9d, $2e, $65, $f0 4769 .db $ff 4770 4771 ;AREA OBJECT DATA 4772 4773 ;level 1-4/6-4 4774 L_CastleArea1: 4775 .db $9b, $07 4776 .db $05, $32, $06, $33, $07, $34, $ce, $03, $dc, $51 4777 .db $ee, $07, $73, $e0, $74, $0a, $7e, $06, $9e, $0a 4778 .db $ce, $06, $e4, $00, $e8, $0a, $fe, $0a, $2e, $89 4779 .db $4e, $0b, $54, $0a, $14, $8a, $c4, $0a, $34, $8a 4780 .db $7e, $06, $c7, $0a, $01, $e0, $02, $0a, $47, $0a 4781 .db $81, $60, $82, $0a, $c7, $0a, $0e, $87, $7e, $02 4782 .db $a7, $02, $b3, $02, $d7, $02, $e3, $02, $07, $82 4783 .db $13, $02, $3e, $06, $7e, $02, $ae, $07, $fe, $0a 4784 .db $0d, $c4, $cd, $43, $ce, $09, $de, $0b, $dd, $42 4785 .db $fe, $02, $5d, $c7 4786 .db $fd 4787 4788 ;level 4-4 4789 L_CastleArea2: 4790 .db $5b, $07 4791 .db $05, $32, $06, $33, $07, $34, $5e, $0a, $68, $64 4792 .db $98, $64, $a8, $64, $ce, $06, $fe, $02, $0d, $01 4793 .db $1e, $0e, $7e, $02, $94, $63, $b4, $63, $d4, $63 4794 .db $f4, $63, $14, $e3, $2e, $0e, $5e, $02, $64, $35 4795 .db $88, $72, $be, $0e, $0d, $04, $ae, $02, $ce, $08 4796 .db $cd, $4b, $fe, $02, $0d, $05, $68, $31, $7e, $0a 4797 .db $96, $31, $a9, $63, $a8, $33, $d5, $30, $ee, $02 4798 .db $e6, $62, $f4, $61, $04, $b1, $08, $3f, $44, $33 4799 .db $94, $63, $a4, $31, $e4, $31, $04, $bf, $08, $3f 4800 .db $04, $bf, $08, $3f, $cd, $4b, $03, $e4, $0e, $03 4801 .db $2e, $01, $7e, $06, $be, $02, $de, $06, $fe, $0a 4802 .db $0d, $c4, $cd, $43, $ce, $09, $de, $0b, $dd, $42 4803 .db $fe, $02, $5d, $c7 4804 .db $fd 4805 4806 ;level 2-4/5-4 4807 L_CastleArea3: 4808 .db $9b, $07 4809 .db $05, $32, $06, $33, $07, $34, $fe, $00, $27, $b1 4810 .db $65, $32, $75, $0a, $71, $00, $b7, $31, $08, $e4 4811 .db $18, $64, $1e, $04, $57, $3b, $bb, $0a, $17, $8a 4812 .db $27, $3a, $73, $0a, $7b, $0a, $d7, $0a, $e7, $3a 4813 .db $3b, $8a, $97, $0a, $fe, $08, $24, $8a, $2e, $00 4814 .db $3e, $40, $38, $64, $6f, $00, $9f, $00, $be, $43 4815 .db $c8, $0a, $c9, $63, $ce, $07, $fe, $07, $2e, $81 4816 .db $66, $42, $6a, $42, $79, $0a, $be, $00, $c8, $64 4817 .db $f8, $64, $08, $e4, $2e, $07, $7e, $03, $9e, $07 4818 .db $be, $03, $de, $07, $fe, $0a, $03, $a5, $0d, $44 4819 .db $cd, $43, $ce, $09, $dd, $42, $de, $0b, $fe, $02 4820 .db $5d, $c7 4821 .db $fd 4822 4823 ;level 3-4 4824 L_CastleArea4: 4825 .db $9b, $07 4826 .db $05, $32, $06, $33, $07, $34, $fe, $06, $0c, $81 4827 .db $39, $0a, $5c, $01, $89, $0a, $ac, $01, $d9, $0a 4828 .db $fc, $01, $2e, $83, $a7, $01, $b7, $00, $c7, $01 4829 .db $de, $0a, $fe, $02, $4e, $83, $5a, $32, $63, $0a 4830 .db $69, $0a, $7e, $02, $ee, $03, $fa, $32, $03, $8a 4831 .db $09, $0a, $1e, $02, $ee, $03, $fa, $32, $03, $8a 4832 .db $09, $0a, $14, $42, $1e, $02, $7e, $0a, $9e, $07 4833 .db $fe, $0a, $2e, $86, $5e, $0a, $8e, $06, $be, $0a 4834 .db $ee, $07, $3e, $83, $5e, $07, $fe, $0a, $0d, $c4 4835 .db $41, $52, $51, $52, $cd, $43, $ce, $09, $de, $0b 4836 .db $dd, $42, $fe, $02, $5d, $c7 4837 .db $fd 4838 4839 ;level 7-4 4840 L_CastleArea5: 4841 .db $5b, $07 4842 .db $05, $32, $06, $33, $07, $34, $fe, $0a, $ae, $86 4843 .db $be, $07, $fe, $02, $0d, $02, $27, $32, $46, $61 4844 .db $55, $62, $5e, $0e, $1e, $82, $68, $3c, $74, $3a 4845 .db $7d, $4b, $5e, $8e, $7d, $4b, $7e, $82, $84, $62 4846 .db $94, $61, $a4, $31, $bd, $4b, $ce, $06, $fe, $02 4847 .db $0d, $06, $34, $31, $3e, $0a, $64, $32, $75, $0a 4848 .db $7b, $61, $a4, $33, $ae, $02, $de, $0e, $3e, $82 4849 .db $64, $32, $78, $32, $b4, $36, $c8, $36, $dd, $4b 4850 .db $44, $b2, $58, $32, $94, $63, $a4, $3e, $ba, $30 4851 .db $c9, $61, $ce, $06, $dd, $4b, $ce, $86, $dd, $4b 4852 .db $fe, $02, $2e, $86, $5e, $02, $7e, $06, $fe, $02 4853 .db $1e, $86, $3e, $02, $5e, $06, $7e, $02, $9e, $06 4854 .db $fe, $0a, $0d, $c4, $cd, $43, $ce, $09, $de, $0b 4855 .db $dd, $42, $fe, $02, $5d, $c7 4856 .db $fd 4857 4858 ;level 8-4 4859 L_CastleArea6: 4860 .db $5b, $06 4861 .db $05, $32, $06, $33, $07, $34, $5e, $0a, $ae, $02 4862 .db $0d, $01, $39, $73, $0d, $03, $39, $7b, $4d, $4b 4863 .db $de, $06, $1e, $8a, $ae, $06, $c4, $33, $16, $fe 4864 .db $a5, $77, $fe, $02, $fe, $82, $0d, $07, $39, $73 4865 .db $a8, $74, $ed, $4b, $49, $fb, $e8, $74, $fe, $0a 4866 .db $2e, $82, $67, $02, $84, $7a, $87, $31, $0d, $0b 4867 .db $fe, $02, $0d, $0c, $39, $73, $5e, $06, $c6, $76 4868 .db $45, $ff, $be, $0a, $dd, $48, $fe, $06, $3d, $cb 4869 .db $46, $7e, $ad, $4a, $fe, $82, $39, $f3, $a9, $7b 4870 .db $4e, $8a, $9e, $07, $fe, $0a, $0d, $c4, $cd, $43 4871 .db $ce, $09, $de, $0b, $dd, $42, $fe, $02, $5d, $c7 4872 .db $fd 4873 4874 ;level 3-3 4875 L_GroundArea1: 4876 .db $94, $11 4877 .db $0f, $26, $fe, $10, $28, $94, $65, $15, $eb, $12 4878 .db $fa, $41, $4a, $96, $54, $40, $a4, $42, $b7, $13 4879 .db $e9, $19, $f5, $15, $11, $80, $47, $42, $71, $13 4880 .db $80, $41, $15, $92, $1b, $1f, $24, $40, $55, $12 4881 .db $64, $40, $95, $12, $a4, $40, $d2, $12, $e1, $40 4882 .db $13, $c0, $2c, $17, $2f, $12, $49, $13, $83, $40 4883 .db $9f, $14, $a3, $40, $17, $92, $83, $13, $92, $41 4884 .db $b9, $14, $c5, $12, $c8, $40, $d4, $40, $4b, $92 4885 .db $78, $1b, $9c, $94, $9f, $11, $df, $14, $fe, $11 4886 .db $7d, $c1, $9e, $42, $cf, $20 4887 .db $fd 4888 4889 ;level 8-3 4890 L_GroundArea2: 4891 .db $90, $b1 4892 .db $0f, $26, $29, $91, $7e, $42, $fe, $40, $28, $92 4893 .db $4e, $42, $2e, $c0, $57, $73, $c3, $25, $c7, $27 4894 .db $23, $84, $33, $20, $5c, $01, $77, $63, $88, $62 4895 .db $99, $61, $aa, $60, $bc, $01, $ee, $42, $4e, $c0 4896 .db $69, $11, $7e, $42, $de, $40, $f8, $62, $0e, $c2 4897 .db $ae, $40, $d7, $63, $e7, $63, $33, $a7, $37, $27 4898 .db $43, $04, $cc, $01, $e7, $73, $0c, $81, $3e, $42 4899 .db $0d, $0a, $5e, $40, $88, $72, $be, $42, $e7, $87 4900 .db $fe, $40, $39, $e1, $4e, $00, $69, $60, $87, $60 4901 .db $a5, $60, $c3, $31, $fe, $31, $6d, $c1, $be, $42 4902 .db $ef, $20 4903 .db $fd 4904 4905 ;level 4-1 4906 L_GroundArea3: 4907 .db $52, $21 4908 .db $0f, $20, $6e, $40, $58, $f2, $93, $01, $97, $00 4909 .db $0c, $81, $97, $40, $a6, $41, $c7, $40, $0d, $04 4910 .db $03, $01, $07, $01, $23, $01, $27, $01, $ec, $03 4911 .db $ac, $f3, $c3, $03, $78, $e2, $94, $43, $47, $f3 4912 .db $74, $43, $47, $fb, $74, $43, $2c, $f1, $4c, $63 4913 .db $47, $00, $57, $21, $5c, $01, $7c, $72, $39, $f1 4914 .db $ec, $02, $4c, $81, $d8, $62, $ec, $01, $0d, $0d 4915 .db $0f, $38, $c7, $07, $ed, $4a, $1d, $c1, $5f, $26 4916 .db $fd 4917 4918 ;level 6-2 4919 L_GroundArea4: 4920 .db $54, $21 4921 .db $0f, $26, $a7, $22, $37, $fb, $73, $20, $83, $07 4922 .db $87, $02, $93, $20, $c7, $73, $04, $f1, $06, $31 4923 .db $39, $71, $59, $71, $e7, $73, $37, $a0, $47, $04 4924 .db $86, $7c, $e5, $71, $e7, $31, $33, $a4, $39, $71 4925 .db $a9, $71, $d3, $23, $08, $f2, $13, $05, $27, $02 4926 .db $49, $71, $75, $75, $e8, $72, $67, $f3, $99, $71 4927 .db $e7, $20, $f4, $72, $f7, $31, $17, $a0, $33, $20 4928 .db $39, $71, $73, $28, $bc, $05, $39, $f1, $79, $71 4929 .db $a6, $21, $c3, $06, $d3, $20, $dc, $00, $fc, $00 4930 .db $07, $a2, $13, $21, $5f, $32, $8c, $00, $98, $7a 4931 .db $c7, $63, $d9, $61, $03, $a2, $07, $22, $74, $72 4932 .db $77, $31, $e7, $73, $39, $f1, $58, $72, $77, $73 4933 .db $d8, $72, $7f, $b1, $97, $73, $b6, $64, $c5, $65 4934 .db $d4, $66, $e3, $67, $f3, $67, $8d, $c1, $cf, $26 4935 .db $fd 4936 4937 ;level 3-1 4938 L_GroundArea5: 4939 .db $52, $31 4940 .db $0f, $20, $6e, $66, $07, $81, $36, $01, $66, $00 4941 .db $a7, $22, $08, $f2, $67, $7b, $dc, $02, $98, $f2 4942 .db $d7, $20, $39, $f1, $9f, $33, $dc, $27, $dc, $57 4943 .db $23, $83, $57, $63, $6c, $51, $87, $63, $99, $61 4944 .db $a3, $06, $b3, $21, $77, $f3, $f3, $21, $f7, $2a 4945 .db $13, $81, $23, $22, $53, $00, $63, $22, $e9, $0b 4946 .db $0c, $83, $13, $21, $16, $22, $33, $05, $8f, $35 4947 .db $ec, $01, $63, $a0, $67, $20, $73, $01, $77, $01 4948 .db $83, $20, $87, $20, $b3, $20, $b7, $20, $c3, $01 4949 .db $c7, $00, $d3, $20, $d7, $20, $67, $a0, $77, $07 4950 .db $87, $22, $e8, $62, $f5, $65, $1c, $82, $7f, $38 4951 .db $8d, $c1, $cf, $26 4952 .db $fd 4953 4954 ;level 1-1 4955 L_GroundArea6: 4956 .db $50, $21 4957 .db $07, $81, $47, $24, $57, $00, $63, $01, $77, $01 4958 .db $c9, $71, $68, $f2, $e7, $73, $97, $fb, $06, $83 4959 .db $5c, $01, $d7, $22, $e7, $00, $03, $a7, $6c, $02 4960 .db $b3, $22, $e3, $01, $e7, $07, $47, $a0, $57, $06 4961 .db $a7, $01, $d3, $00, $d7, $01, $07, $81, $67, $20 4962 .db $93, $22, $03, $a3, $1c, $61, $17, $21, $6f, $33 4963 .db $c7, $63, $d8, $62, $e9, $61, $fa, $60, $4f, $b3 4964 .db $87, $63, $9c, $01, $b7, $63, $c8, $62, $d9, $61 4965 .db $ea, $60, $39, $f1, $87, $21, $a7, $01, $b7, $20 4966 .db $39, $f1, $5f, $38, $6d, $c1, $af, $26 4967 .db $fd 4968 4969 ;level 1-3/5-3 4970 L_GroundArea7: 4971 .db $90, $11 4972 .db $0f, $26, $fe, $10, $2a, $93, $87, $17, $a3, $14 4973 .db $b2, $42, $0a, $92, $19, $40, $36, $14, $50, $41 4974 .db $82, $16, $2b, $93, $24, $41, $bb, $14, $b8, $00 4975 .db $c2, $43, $c3, $13, $1b, $94, $67, $12, $c4, $15 4976 .db $53, $c1, $d2, $41, $12, $c1, $29, $13, $85, $17 4977 .db $1b, $92, $1a, $42, $47, $13, $83, $41, $a7, $13 4978 .db $0e, $91, $a7, $63, $b7, $63, $c5, $65, $d5, $65 4979 .db $dd, $4a, $e3, $67, $f3, $67, $8d, $c1, $ae, $42 4980 .db $df, $20 4981 .db $fd 4982 4983 ;level 2-3/7-3 4984 L_GroundArea8: 4985 .db $90, $11 4986 .db $0f, $26, $6e, $10, $8b, $17, $af, $32, $d8, $62 4987 .db $e8, $62, $fc, $3f, $ad, $c8, $f8, $64, $0c, $be 4988 .db $43, $43, $f8, $64, $0c, $bf, $73, $40, $84, $40 4989 .db $93, $40, $a4, $40, $b3, $40, $f8, $64, $48, $e4 4990 .db $5c, $39, $83, $40, $92, $41, $b3, $40, $f8, $64 4991 .db $48, $e4, $5c, $39, $f8, $64, $13, $c2, $37, $65 4992 .db $4c, $24, $63, $00, $97, $65, $c3, $42, $0b, $97 4993 .db $ac, $32, $f8, $64, $0c, $be, $53, $45, $9d, $48 4994 .db $f8, $64, $2a, $e2, $3c, $47, $56, $43, $ba, $62 4995 .db $f8, $64, $0c, $b7, $88, $64, $bc, $31, $d4, $45 4996 .db $fc, $31, $3c, $b1, $78, $64, $8c, $38, $0b, $9c 4997 .db $1a, $33, $18, $61, $28, $61, $39, $60, $5d, $4a 4998 .db $ee, $11, $0f, $b8, $1d, $c1, $3e, $42, $6f, $20 4999 .db $fd 5000 5001 ;level 2-1 5002 L_GroundArea9: 5003 .db $52, $31 5004 .db $0f, $20, $6e, $40, $f7, $20, $07, $84, $17, $20 5005 .db $4f, $34, $c3, $03, $c7, $02, $d3, $22, $27, $e3 5006 .db $39, $61, $e7, $73, $5c, $e4, $57, $00, $6c, $73 5007 .db $47, $a0, $53, $06, $63, $22, $a7, $73, $fc, $73 5008 .db $13, $a1, $33, $05, $43, $21, $5c, $72, $c3, $23 5009 .db $cc, $03, $77, $fb, $ac, $02, $39, $f1, $a7, $73 5010 .db $d3, $04, $e8, $72, $e3, $22, $26, $f4, $bc, $02 5011 .db $8c, $81, $a8, $62, $17, $87, $43, $24, $a7, $01 5012 .db $c3, $04, $08, $f2, $97, $21, $a3, $02, $c9, $0b 5013 .db $e1, $69, $f1, $69, $8d, $c1, $cf, $26 5014 .db $fd 5015 5016 ;pipe intro area 5017 L_GroundArea10: 5018 .db $38, $11 5019 .db $0f, $26, $ad, $40, $3d, $c7 5020 .db $fd 5021 5022 ;level 5-1 5023 L_GroundArea11: 5024 .db $95, $b1 5025 .db $0f, $26, $0d, $02, $c8, $72, $1c, $81, $38, $72 5026 .db $0d, $05, $97, $34, $98, $62, $a3, $20, $b3, $06 5027 .db $c3, $20, $cc, $03, $f9, $91, $2c, $81, $48, $62 5028 .db $0d, $09, $37, $63, $47, $03, $57, $21, $8c, $02 5029 .db $c5, $79, $c7, $31, $f9, $11, $39, $f1, $a9, $11 5030 .db $6f, $b4, $d3, $65, $e3, $65, $7d, $c1, $bf, $26 5031 .db $fd 5032 5033 ;cloud level used in levels 2-1 and 5-2 5034 L_GroundArea12: 5035 .db $00, $c1 5036 .db $4c, $00, $f4, $4f, $0d, $02, $02, $42, $43, $4f 5037 .db $52, $c2, $de, $00, $5a, $c2, $4d, $c7 5038 .db $fd 5039 5040 ;level 4-3 5041 L_GroundArea13: 5042 .db $90, $51 5043 .db $0f, $26, $ee, $10, $0b, $94, $33, $14, $42, $42 5044 .db $77, $16, $86, $44, $02, $92, $4a, $16, $69, $42 5045 .db $73, $14, $b0, $00, $c7, $12, $05, $c0, $1c, $17 5046 .db $1f, $11, $36, $12, $8f, $14, $91, $40, $1b, $94 5047 .db $35, $12, $34, $42, $60, $42, $61, $12, $87, $12 5048 .db $96, $40, $a3, $14, $1c, $98, $1f, $11, $47, $12 5049 .db $9f, $15, $cc, $15, $cf, $11, $05, $c0, $1f, $15 5050 .db $39, $12, $7c, $16, $7f, $11, $82, $40, $98, $12 5051 .db $df, $15, $16, $c4, $17, $14, $54, $12, $9b, $16 5052 .db $28, $94, $ce, $01, $3d, $c1, $5e, $42, $8f, $20 5053 .db $fd 5054 5055 ;level 6-3 5056 L_GroundArea14: 5057 .db $97, $11 5058 .db $0f, $26, $fe, $10, $2b, $92, $57, $12, $8b, $12 5059 .db $c0, $41, $f7, $13, $5b, $92, $69, $0b, $bb, $12 5060 .db $b2, $46, $19, $93, $71, $00, $17, $94, $7c, $14 5061 .db $7f, $11, $93, $41, $bf, $15, $fc, $13, $ff, $11 5062 .db $2f, $95, $50, $42, $51, $12, $58, $14, $a6, $12 5063 .db $db, $12, $1b, $93, $46, $43, $7b, $12, $8d, $49 5064 .db $b7, $14, $1b, $94, $49, $0b, $bb, $12, $fc, $13 5065 .db $ff, $12, $03, $c1, $2f, $15, $43, $12, $4b, $13 5066 .db $77, $13, $9d, $4a, $15, $c1, $a1, $41, $c3, $12 5067 .db $fe, $01, $7d, $c1, $9e, $42, $cf, $20 5068 .db $fd 5069 5070 ;level 6-1 5071 L_GroundArea15: 5072 .db $52, $21 5073 .db $0f, $20, $6e, $44, $0c, $f1, $4c, $01, $aa, $35 5074 .db $d9, $34, $ee, $20, $08, $b3, $37, $32, $43, $04 5075 .db $4e, $21, $53, $20, $7c, $01, $97, $21, $b7, $07 5076 .db $9c, $81, $e7, $42, $5f, $b3, $97, $63, $ac, $02 5077 .db $c5, $41, $49, $e0, $58, $61, $76, $64, $85, $65 5078 .db $94, $66, $a4, $22, $a6, $03, $c8, $22, $dc, $02 5079 .db $68, $f2, $96, $42, $13, $82, $17, $02, $af, $34 5080 .db $f6, $21, $fc, $06, $26, $80, $2a, $24, $36, $01 5081 .db $8c, $00, $ff, $35, $4e, $a0, $55, $21, $77, $20 5082 .db $87, $07, $89, $22, $ae, $21, $4c, $82, $9f, $34 5083 .db $ec, $01, $03, $e7, $13, $67, $8d, $4a, $ad, $41 5084 .db $0f, $a6 5085 .db $fd 5086 5087 ;warp zone area used in level 4-2 5088 L_GroundArea16: 5089 .db $10, $51 5090 .db $4c, $00, $c7, $12, $c6, $42, $03, $92, $02, $42 5091 .db $29, $12, $63, $12, $62, $42, $69, $14, $a5, $12 5092 .db $a4, $42, $e2, $14, $e1, $44, $f8, $16, $37, $c1 5093 .db $8f, $38, $02, $bb, $28, $7a, $68, $7a, $a8, $7a 5094 .db $e0, $6a, $f0, $6a, $6d, $c5 5095 .db $fd 5096 5097 ;level 8-1 5098 L_GroundArea17: 5099 .db $92, $31 5100 .db $0f, $20, $6e, $40, $0d, $02, $37, $73, $ec, $00 5101 .db $0c, $80, $3c, $00, $6c, $00, $9c, $00, $06, $c0 5102 .db $c7, $73, $06, $83, $28, $72, $96, $40, $e7, $73 5103 .db $26, $c0, $87, $7b, $d2, $41, $39, $f1, $c8, $f2 5104 .db $97, $e3, $a3, $23, $e7, $02, $e3, $07, $f3, $22 5105 .db $37, $e3, $9c, $00, $bc, $00, $ec, $00, $0c, $80 5106 .db $3c, $00, $86, $21, $a6, $06, $b6, $24, $5c, $80 5107 .db $7c, $00, $9c, $00, $29, $e1, $dc, $05, $f6, $41 5108 .db $dc, $80, $e8, $72, $0c, $81, $27, $73, $4c, $01 5109 .db $66, $74, $0d, $11, $3f, $35, $b6, $41, $2c, $82 5110 .db $36, $40, $7c, $02, $86, $40, $f9, $61, $39, $e1 5111 .db $ac, $04, $c6, $41, $0c, $83, $16, $41, $88, $f2 5112 .db $39, $f1, $7c, $00, $89, $61, $9c, $00, $a7, $63 5113 .db $bc, $00, $c5, $65, $dc, $00, $e3, $67, $f3, $67 5114 .db $8d, $c1, $cf, $26 5115 .db $fd 5116 5117 ;level 5-2 5118 L_GroundArea18: 5119 .db $55, $b1 5120 .db $0f, $26, $cf, $33, $07, $b2, $15, $11, $52, $42 5121 .db $99, $0b, $ac, $02, $d3, $24, $d6, $42, $d7, $25 5122 .db $23, $84, $cf, $33, $07, $e3, $19, $61, $78, $7a 5123 .db $ef, $33, $2c, $81, $46, $64, $55, $65, $65, $65 5124 .db $ec, $74, $47, $82, $53, $05, $63, $21, $62, $41 5125 .db $96, $22, $9a, $41, $cc, $03, $b9, $91, $39, $f1 5126 .db $63, $26, $67, $27, $d3, $06, $fc, $01, $18, $e2 5127 .db $d9, $07, $e9, $04, $0c, $86, $37, $22, $93, $24 5128 .db $87, $84, $ac, $02, $c2, $41, $c3, $23, $d9, $71 5129 .db $fc, $01, $7f, $b1, $9c, $00, $a7, $63, $b6, $64 5130 .db $cc, $00, $d4, $66, $e3, $67, $f3, $67, $8d, $c1 5131 .db $cf, $26 5132 .db $fd 5133 5134 ;level 8-2 5135 L_GroundArea19: 5136 .db $50, $b1 5137 .db $0f, $26, $fc, $00, $1f, $b3, $5c, $00, $65, $65 5138 .db $74, $66, $83, $67, $93, $67, $dc, $73, $4c, $80 5139 .db $b3, $20, $c9, $0b, $c3, $08, $d3, $2f, $dc, $00 5140 .db $2c, $80, $4c, $00, $8c, $00, $d3, $2e, $ed, $4a 5141 .db $fc, $00, $d7, $a1, $ec, $01, $4c, $80, $59, $11 5142 .db $d8, $11, $da, $10, $37, $a0, $47, $04, $99, $11 5143 .db $e7, $21, $3a, $90, $67, $20, $76, $10, $77, $60 5144 .db $87, $07, $d8, $12, $39, $f1, $ac, $00, $e9, $71 5145 .db $0c, $80, $2c, $00, $4c, $05, $c7, $7b, $39, $f1 5146 .db $ec, $00, $f9, $11, $0c, $82, $6f, $34, $f8, $11 5147 .db $fa, $10, $7f, $b2, $ac, $00, $b6, $64, $cc, $01 5148 .db $e3, $67, $f3, $67, $8d, $c1, $cf, $26 5149 .db $fd 5150 5151 ;level 7-1 5152 L_GroundArea20: 5153 .db $52, $b1 5154 .db $0f, $20, $6e, $45, $39, $91, $b3, $04, $c3, $21 5155 .db $c8, $11, $ca, $10, $49, $91, $7c, $73, $e8, $12 5156 .db $88, $91, $8a, $10, $e7, $21, $05, $91, $07, $30 5157 .db $17, $07, $27, $20, $49, $11, $9c, $01, $c8, $72 5158 .db $23, $a6, $27, $26, $d3, $03, $d8, $7a, $89, $91 5159 .db $d8, $72, $39, $f1, $a9, $11, $09, $f1, $63, $24 5160 .db $67, $24, $d8, $62, $28, $91, $2a, $10, $56, $21 5161 .db $70, $04, $79, $0b, $8c, $00, $94, $21, $9f, $35 5162 .db $2f, $b8, $3d, $c1, $7f, $26 5163 .db $fd 5164 5165 ;cloud level used in levels 3-1 and 6-2 5166 L_GroundArea21: 5167 .db $06, $c1 5168 .db $4c, $00, $f4, $4f, $0d, $02, $06, $20, $24, $4f 5169 .db $35, $a0, $36, $20, $53, $46, $d5, $20, $d6, $20 5170 .db $34, $a1, $73, $49, $74, $20, $94, $20, $b4, $20 5171 .db $d4, $20, $f4, $20, $2e, $80, $59, $42, $4d, $c7 5172 .db $fd 5173 5174 ;level 3-2 5175 L_GroundArea22: 5176 .db $96, $31 5177 .db $0f, $26, $0d, $03, $1a, $60, $77, $42, $c4, $00 5178 .db $c8, $62, $b9, $e1, $d3, $06, $d7, $07, $f9, $61 5179 .db $0c, $81, $4e, $b1, $8e, $b1, $bc, $01, $e4, $50 5180 .db $e9, $61, $0c, $81, $0d, $0a, $84, $43, $98, $72 5181 .db $0d, $0c, $0f, $38, $1d, $c1, $5f, $26 5182 .db $fd 5183 5184 ;level 1-2 5185 L_UndergroundArea1: 5186 .db $48, $0f 5187 .db $0e, $01, $5e, $02, $a7, $00, $bc, $73, $1a, $e0 5188 .db $39, $61, $58, $62, $77, $63, $97, $63, $b8, $62 5189 .db $d6, $07, $f8, $62, $19, $e1, $75, $52, $86, $40 5190 .db $87, $50, $95, $52, $93, $43, $a5, $21, $c5, $52 5191 .db $d6, $40, $d7, $20, $e5, $06, $e6, $51, $3e, $8d 5192 .db $5e, $03, $67, $52, $77, $52, $7e, $02, $9e, $03 5193 .db $a6, $43, $a7, $23, $de, $05, $fe, $02, $1e, $83 5194 .db $33, $54, $46, $40, $47, $21, $56, $04, $5e, $02 5195 .db $83, $54, $93, $52, $96, $07, $97, $50, $be, $03 5196 .db $c7, $23, $fe, $02, $0c, $82, $43, $45, $45, $24 5197 .db $46, $24, $90, $08, $95, $51, $78, $fa, $d7, $73 5198 .db $39, $f1, $8c, $01, $a8, $52, $b8, $52, $cc, $01 5199 .db $5f, $b3, $97, $63, $9e, $00, $0e, $81, $16, $24 5200 .db $66, $04, $8e, $00, $fe, $01, $08, $d2, $0e, $06 5201 .db $6f, $47, $9e, $0f, $0e, $82, $2d, $47, $28, $7a 5202 .db $68, $7a, $a8, $7a, $ae, $01, $de, $0f, $6d, $c5 5203 .db $fd 5204 5205 ;level 4-2 5206 L_UndergroundArea2: 5207 .db $48, $0f 5208 .db $0e, $01, $5e, $02, $bc, $01, $fc, $01, $2c, $82 5209 .db $41, $52, $4e, $04, $67, $25, $68, $24, $69, $24 5210 .db $ba, $42, $c7, $04, $de, $0b, $b2, $87, $fe, $02 5211 .db $2c, $e1, $2c, $71, $67, $01, $77, $00, $87, $01 5212 .db $8e, $00, $ee, $01, $f6, $02, $03, $85, $05, $02 5213 .db $13, $21, $16, $02, $27, $02, $2e, $02, $88, $72 5214 .db $c7, $20, $d7, $07, $e4, $76, $07, $a0, $17, $06 5215 .db $48, $7a, $76, $20, $98, $72, $79, $e1, $88, $62 5216 .db $9c, $01, $b7, $73, $dc, $01, $f8, $62, $fe, $01 5217 .db $08, $e2, $0e, $00, $6e, $02, $73, $20, $77, $23 5218 .db $83, $04, $93, $20, $ae, $00, $fe, $0a, $0e, $82 5219 .db $39, $71, $a8, $72, $e7, $73, $0c, $81, $8f, $32 5220 .db $ae, $00, $fe, $04, $04, $d1, $17, $04, $26, $49 5221 .db $27, $29, $df, $33, $fe, $02, $44, $f6, $7c, $01 5222 .db $8e, $06, $bf, $47, $ee, $0f, $4d, $c7, $0e, $82 5223 .db $68, $7a, $ae, $01, $de, $0f, $6d, $c5 5224 .db $fd 5225 5226 ;underground bonus rooms area used in many levels 5227 L_UndergroundArea3: 5228 .db $48, $01 5229 .db $0e, $01, $00, $5a, $3e, $06, $45, $46, $47, $46 5230 .db $53, $44, $ae, $01, $df, $4a, $4d, $c7, $0e, $81 5231 .db $00, $5a, $2e, $04, $37, $28, $3a, $48, $46, $47 5232 .db $c7, $07, $ce, $0f, $df, $4a, $4d, $c7, $0e, $81 5233 .db $00, $5a, $33, $53, $43, $51, $46, $40, $47, $50 5234 .db $53, $04, $55, $40, $56, $50, $62, $43, $64, $40 5235 .db $65, $50, $71, $41, $73, $51, $83, $51, $94, $40 5236 .db $95, $50, $a3, $50, $a5, $40, $a6, $50, $b3, $51 5237 .db $b6, $40, $b7, $50, $c3, $53, $df, $4a, $4d, $c7 5238 .db $0e, $81, $00, $5a, $2e, $02, $36, $47, $37, $52 5239 .db $3a, $49, $47, $25, $a7, $52, $d7, $04, $df, $4a 5240 .db $4d, $c7, $0e, $81, $00, $5a, $3e, $02, $44, $51 5241 .db $53, $44, $54, $44, $55, $24, $a1, $54, $ae, $01 5242 .db $b4, $21, $df, $4a, $e5, $07, $4d, $c7 5243 .db $fd 5244 5245 ;water area used in levels 5-2 and 6-2 5246 L_WaterArea1: 5247 .db $41, $01 5248 .db $b4, $34, $c8, $52, $f2, $51, $47, $d3, $6c, $03 5249 .db $65, $49, $9e, $07, $be, $01, $cc, $03, $fe, $07 5250 .db $0d, $c9, $1e, $01, $6c, $01, $62, $35, $63, $53 5251 .db $8a, $41, $ac, $01, $b3, $53, $e9, $51, $26, $c3 5252 .db $27, $33, $63, $43, $64, $33, $ba, $60, $c9, $61 5253 .db $ce, $0b, $e5, $09, $ee, $0f, $7d, $ca, $7d, $47 5254 .db $fd 5255 5256 ;level 2-2/7-2 5257 L_WaterArea2: 5258 .db $41, $01 5259 .db $b8, $52, $ea, $41, $27, $b2, $b3, $42, $16, $d4 5260 .db $4a, $42, $a5, $51, $a7, $31, $27, $d3, $08, $e2 5261 .db $16, $64, $2c, $04, $38, $42, $76, $64, $88, $62 5262 .db $de, $07, $fe, $01, $0d, $c9, $23, $32, $31, $51 5263 .db $98, $52, $0d, $c9, $59, $42, $63, $53, $67, $31 5264 .db $14, $c2, $36, $31, $87, $53, $17, $e3, $29, $61 5265 .db $30, $62, $3c, $08, $42, $37, $59, $40, $6a, $42 5266 .db $99, $40, $c9, $61, $d7, $63, $39, $d1, $58, $52 5267 .db $c3, $67, $d3, $31, $dc, $06, $f7, $42, $fa, $42 5268 .db $23, $b1, $43, $67, $c3, $34, $c7, $34, $d1, $51 5269 .db $43, $b3, $47, $33, $9a, $30, $a9, $61, $b8, $62 5270 .db $be, $0b, $d5, $09, $de, $0f, $0d, $ca, $7d, $47 5271 .db $fd 5272 5273 ;water area used in level 8-4 5274 L_WaterArea3: 5275 .db $49, $0f 5276 .db $1e, $01, $39, $73, $5e, $07, $ae, $0b, $1e, $82 5277 .db $6e, $88, $9e, $02, $0d, $04, $2e, $0b, $45, $09 5278 .db $4e, $0f, $ed, $47 5279 .db $fd 5280 5281 ;------------------------------------------------------------------------------------- 5282 5283 ;unused space 5284 .db $ff 5285 5286 ;------------------------------------------------------------------------------------- 5287 5288 ;indirect jump routine called when 5289 ;$0770 is set to 1 5290 GameMode: 5291 lda OperMode_Task 5292 jsr JumpEngine 5293 5294 .dw InitializeArea 5295 .dw ScreenRoutines 5296 .dw SecondaryGameSetup 5297 .dw GameCoreRoutine 5298 5299 ;------------------------------------------------------------------------------------- 5300 5301 GameCoreRoutine: 5302 ldx CurrentPlayer ;get which player is on the screen 5303 lda SavedJoypadBits,x ;use appropriate player's controller bits 5304 sta SavedJoypadBits ;as the master controller bits 5305 jsr GameRoutines ;execute one of many possible subs 5306 lda OperMode_Task ;check major task of operating mode 5307 cmp #$03 ;if we are supposed to be here, 5308 bcs GameEngine ;branch to the game engine itself 5309 rts 5310 5311 GameEngine: 5312 jsr ProcFireball_Bubble ;process fireballs and air bubbles 5313 ldx #$00 5314 ProcELoop: stx ObjectOffset ;put incremented offset in X as enemy object offset 5315 jsr EnemiesAndLoopsCore ;process enemy objects 5316 jsr FloateyNumbersRoutine ;process floatey numbers 5317 inx 5318 cpx #$06 ;do these two subroutines until the whole buffer is done 5319 bne ProcELoop 5320 jsr GetPlayerOffscreenBits ;get offscreen bits for player object 5321 jsr RelativePlayerPosition ;get relative coordinates for player object 5322 jsr PlayerGfxHandler ;draw the player 5323 jsr BlockObjMT_Updater ;replace block objects with metatiles if necessary 5324 ldx #$01 5325 stx ObjectOffset ;set offset for second 5326 jsr BlockObjectsCore ;process second block object 5327 dex 5328 stx ObjectOffset ;set offset for first 5329 jsr BlockObjectsCore ;process first block object 5330 jsr MiscObjectsCore ;process misc objects (hammer, jumping coins) 5331 jsr ProcessCannons ;process bullet bill cannons 5332 jsr ProcessWhirlpools ;process whirlpools 5333 jsr FlagpoleRoutine ;process the flagpole 5334 jsr RunGameTimer ;count down the game timer 5335 jsr ColorRotation ;cycle one of the background colors 5336 lda Player_Y_HighPos 5337 cmp #$02 ;if player is below the screen, don't bother with the music 5338 bpl NoChgMus 5339 lda StarInvincibleTimer ;if star mario invincibility timer at zero, 5340 beq ClrPlrPal ;skip this part 5341 cmp #$04 5342 bne NoChgMus ;if not yet at a certain point, continue 5343 lda IntervalTimerControl ;if interval timer not yet expired, 5344 bne NoChgMus ;branch ahead, don't bother with the music 5345 jsr GetAreaMusic ;to re-attain appropriate level music 5346 NoChgMus: ldy StarInvincibleTimer ;get invincibility timer 5347 lda FrameCounter ;get frame counter 5348 cpy #$08 ;if timer still above certain point, 5349 bcs CycleTwo ;branch to cycle player's palette quickly 5350 lsr ;otherwise, divide by 8 to cycle every eighth frame 5351 lsr 5352 CycleTwo: lsr ;if branched here, divide by 2 to cycle every other frame 5353 jsr CyclePlayerPalette ;do sub to cycle the palette (note: shares fire flower code) 5354 jmp SaveAB ;then skip this sub to finish up the game engine 5355 ClrPlrPal: jsr ResetPalStar ;do sub to clear player's palette bits in attributes 5356 SaveAB: lda A_B_Buttons ;save current A and B button 5357 sta PreviousA_B_Buttons ;into temp variable to be used on next frame 5358 lda #$00 5359 sta Left_Right_Buttons ;nullify left and right buttons temp variable 5360 UpdScrollVar: lda VRAM_Buffer_AddrCtrl 5361 cmp #$06 ;if vram address controller set to 6 (one of two $0341s) 5362 beq ExitEng ;then branch to leave 5363 lda AreaParserTaskNum ;otherwise check number of tasks 5364 bne RunParser 5365 lda ScrollThirtyTwo ;get horizontal scroll in 0-31 or $00-$20 range 5366 cmp #$20 ;check to see if exceeded $21 5367 bmi ExitEng ;branch to leave if not 5368 lda ScrollThirtyTwo 5369 sbc #$20 ;otherwise subtract $20 to set appropriately 5370 sta ScrollThirtyTwo ;and store 5371 lda #$00 ;reset vram buffer offset used in conjunction with 5372 sta VRAM_Buffer2_Offset ;level graphics buffer at $0341-$035f 5373 RunParser: jsr AreaParserTaskHandler ;update the name table with more level graphics 5374 ExitEng: rts ;and after all that, we're finally done! 5375 5376 ;------------------------------------------------------------------------------------- 5377 5378 ScrollHandler: 5379 lda Player_X_Scroll ;load value saved here 5380 clc 5381 adc Platform_X_Scroll ;add value used by left/right platforms 5382 sta Player_X_Scroll ;save as new value here to impose force on scroll 5383 lda ScrollLock ;check scroll lock flag 5384 bne InitScrlAmt ;skip a bunch of code here if set 5385 lda Player_Pos_ForScroll 5386 cmp #$50 ;check player's horizontal screen position 5387 bcc InitScrlAmt ;if less than 80 pixels to the right, branch 5388 lda SideCollisionTimer ;if timer related to player's side collision 5389 bne InitScrlAmt ;not expired, branch 5390 ldy Player_X_Scroll ;get value and decrement by one 5391 dey ;if value originally set to zero or otherwise 5392 bmi InitScrlAmt ;negative for left movement, branch 5393 iny 5394 cpy #$02 ;if value $01, branch and do not decrement 5395 bcc ChkNearMid 5396 dey ;otherwise decrement by one 5397 ChkNearMid: lda Player_Pos_ForScroll 5398 cmp #$70 ;check player's horizontal screen position 5399 bcc ScrollScreen ;if less than 112 pixels to the right, branch 5400 ldy Player_X_Scroll ;otherwise get original value undecremented 5401 5402 ScrollScreen: 5403 tya 5404 sta ScrollAmount ;save value here 5405 clc 5406 adc ScrollThirtyTwo ;add to value already set here 5407 sta ScrollThirtyTwo ;save as new value here 5408 tya 5409 clc 5410 adc ScreenLeft_X_Pos ;add to left side coordinate 5411 sta ScreenLeft_X_Pos ;save as new left side coordinate 5412 sta HorizontalScroll ;save here also 5413 lda ScreenLeft_PageLoc 5414 adc #$00 ;add carry to page location for left 5415 sta ScreenLeft_PageLoc ;side of the screen 5416 and #$01 ;get LSB of page location 5417 sta $00 ;save as temp variable for PPU register 1 mirror 5418 lda Mirror_PPU_CTRL_REG1 ;get PPU register 1 mirror 5419 and #%11111110 ;save all bits except d0 5420 ora $00 ;get saved bit here and save in PPU register 1 5421 sta Mirror_PPU_CTRL_REG1 ;mirror to be used to set name table later 5422 jsr GetScreenPosition ;figure out where the right side is 5423 lda #$08 5424 sta ScrollIntervalTimer ;set scroll timer (residual, not used elsewhere) 5425 jmp ChkPOffscr ;skip this part 5426 InitScrlAmt: lda #$00 5427 sta ScrollAmount ;initialize value here 5428 ChkPOffscr: ldx #$00 ;set X for player offset 5429 jsr GetXOffscreenBits ;get horizontal offscreen bits for player 5430 sta $00 ;save them here 5431 ldy #$00 ;load default offset (left side) 5432 asl ;if d7 of offscreen bits are set, 5433 bcs KeepOnscr ;branch with default offset 5434 iny ;otherwise use different offset (right side) 5435 lda $00 5436 and #%00100000 ;check offscreen bits for d5 set 5437 beq InitPlatScrl ;if not set, branch ahead of this part 5438 KeepOnscr: lda ScreenEdge_X_Pos,y ;get left or right side coordinate based on offset 5439 sec 5440 sbc X_SubtracterData,y ;subtract amount based on offset 5441 sta Player_X_Position ;store as player position to prevent movement further 5442 lda ScreenEdge_PageLoc,y ;get left or right page location based on offset 5443 sbc #$00 ;subtract borrow 5444 sta Player_PageLoc ;save as player's page location 5445 lda Left_Right_Buttons ;check saved controller bits 5446 cmp OffscrJoypadBitsData,y ;against bits based on offset 5447 beq InitPlatScrl ;if not equal, branch 5448 lda #$00 5449 sta Player_X_Speed ;otherwise nullify horizontal speed of player 5450 InitPlatScrl: lda #$00 ;nullify platform force imposed on scroll 5451 sta Platform_X_Scroll 5452 rts 5453 5454 X_SubtracterData: 5455 .db $00, $10 5456 5457 OffscrJoypadBitsData: 5458 .db $01, $02 5459 5460 ;------------------------------------------------------------------------------------- 5461 5462 GetScreenPosition: 5463 lda ScreenLeft_X_Pos ;get coordinate of screen's left boundary 5464 clc 5465 adc #$ff ;add 255 pixels 5466 sta ScreenRight_X_Pos ;store as coordinate of screen's right boundary 5467 lda ScreenLeft_PageLoc ;get page number where left boundary is 5468 adc #$00 ;add carry from before 5469 sta ScreenRight_PageLoc ;store as page number where right boundary is 5470 rts 5471 5472 ;------------------------------------------------------------------------------------- 5473 5474 GameRoutines: 5475 lda GameEngineSubroutine ;run routine based on number (a few of these routines are 5476 jsr JumpEngine ;merely placeholders as conditions for other routines) 5477 5478 .dw Entrance_GameTimerSetup 5479 .dw Vine_AutoClimb 5480 .dw SideExitPipeEntry 5481 .dw VerticalPipeEntry 5482 .dw FlagpoleSlide 5483 .dw PlayerEndLevel 5484 .dw PlayerLoseLife 5485 .dw PlayerEntrance 5486 .dw PlayerCtrlRoutine 5487 .dw PlayerChangeSize 5488 .dw PlayerInjuryBlink 5489 .dw PlayerDeath 5490 .dw PlayerFireFlower 5491 5492 ;------------------------------------------------------------------------------------- 5493 5494 PlayerEntrance: 5495 lda AltEntranceControl ;check for mode of alternate entry 5496 cmp #$02 5497 beq EntrMode2 ;if found, branch to enter from pipe or with vine 5498 lda #$00 5499 ldy Player_Y_Position ;if vertical position above a certain 5500 cpy #$30 ;point, nullify controller bits and continue 5501 bcc AutoControlPlayer ;with player movement code, do not return 5502 lda PlayerEntranceCtrl ;check player entry bits from header 5503 cmp #$06 5504 beq ChkBehPipe ;if set to 6 or 7, execute pipe intro code 5505 cmp #$07 ;otherwise branch to normal entry 5506 bne PlayerRdy 5507 ChkBehPipe: lda Player_SprAttrib ;check for sprite attributes 5508 bne IntroEntr ;branch if found 5509 lda #$01 5510 jmp AutoControlPlayer ;force player to walk to the right 5511 IntroEntr: jsr EnterSidePipe ;execute sub to move player to the right 5512 dec ChangeAreaTimer ;decrement timer for change of area 5513 bne ExitEntr ;branch to exit if not yet expired 5514 inc DisableIntermediate ;set flag to skip world and lives display 5515 jmp NextArea ;jump to increment to next area and set modes 5516 EntrMode2: lda JoypadOverride ;if controller override bits set here, 5517 bne VineEntr ;branch to enter with vine 5518 lda #$ff ;otherwise, set value here then execute sub 5519 jsr MovePlayerYAxis ;to move player upwards (note $ff = -1) 5520 lda Player_Y_Position ;check to see if player is at a specific coordinate 5521 cmp #$91 ;if player risen to a certain point (this requires pipes 5522 bcc PlayerRdy ;to be at specific height to look/function right) branch 5523 rts ;to the last part, otherwise leave 5524 VineEntr: lda VineHeight 5525 cmp #$60 ;check vine height 5526 bne ExitEntr ;if vine not yet reached maximum height, branch to leave 5527 lda Player_Y_Position ;get player's vertical coordinate 5528 cmp #$99 ;check player's vertical coordinate against preset value 5529 ldy #$00 ;load default values to be written to 5530 lda #$01 ;this value moves player to the right off the vine 5531 bcc OffVine ;if vertical coordinate < preset value, use defaults 5532 lda #$03 5533 sta Player_State ;otherwise set player state to climbing 5534 iny ;increment value in Y 5535 lda #$08 ;set block in block buffer to cover hole, then 5536 sta Block_Buffer_1+$b4 ;use same value to force player to climb 5537 OffVine: sty DisableCollisionDet ;set collision detection disable flag 5538 jsr AutoControlPlayer ;use contents of A to move player up or right, execute sub 5539 lda Player_X_Position 5540 cmp #$48 ;check player's horizontal position 5541 bcc ExitEntr ;if not far enough to the right, branch to leave 5542 PlayerRdy: lda #$08 ;set routine to be executed by game engine next frame 5543 sta GameEngineSubroutine 5544 lda #$01 ;set to face player to the right 5545 sta PlayerFacingDir 5546 lsr ;init A 5547 sta AltEntranceControl ;init mode of entry 5548 sta DisableCollisionDet ;init collision detection disable flag 5549 sta JoypadOverride ;nullify controller override bits 5550 ExitEntr: rts ;leave! 5551 5552 ;------------------------------------------------------------------------------------- 5553 ;$07 - used to hold upper limit of high byte when player falls down hole 5554 5555 AutoControlPlayer: 5556 sta SavedJoypadBits ;override controller bits with contents of A if executing here 5557 5558 PlayerCtrlRoutine: 5559 lda GameEngineSubroutine ;check task here 5560 cmp #$0b ;if certain value is set, branch to skip controller bit loading 5561 beq SizeChk 5562 lda AreaType ;are we in a water type area? 5563 bne SaveJoyp ;if not, branch 5564 ldy Player_Y_HighPos 5565 dey ;if not in vertical area between 5566 bne DisJoyp ;status bar and bottom, branch 5567 lda Player_Y_Position 5568 cmp #$d0 ;if nearing the bottom of the screen or 5569 bcc SaveJoyp ;not in the vertical area between status bar or bottom, 5570 DisJoyp: lda #$00 ;disable controller bits 5571 sta SavedJoypadBits 5572 SaveJoyp: lda SavedJoypadBits ;otherwise store A and B buttons in $0a 5573 and #%11000000 5574 sta A_B_Buttons 5575 lda SavedJoypadBits ;store left and right buttons in $0c 5576 and #%00000011 5577 sta Left_Right_Buttons 5578 lda SavedJoypadBits ;store up and down buttons in $0b 5579 and #%00001100 5580 sta Up_Down_Buttons 5581 and #%00000100 ;check for pressing down 5582 beq SizeChk ;if not, branch 5583 lda Player_State ;check player's state 5584 bne SizeChk ;if not on the ground, branch 5585 ldy Left_Right_Buttons ;check left and right 5586 beq SizeChk ;if neither pressed, branch 5587 lda #$00 5588 sta Left_Right_Buttons ;if pressing down while on the ground, 5589 sta Up_Down_Buttons ;nullify directional bits 5590 SizeChk: jsr PlayerMovementSubs ;run movement subroutines 5591 ldy #$01 ;is player small? 5592 lda PlayerSize 5593 bne ChkMoveDir 5594 ldy #$00 ;check for if crouching 5595 lda CrouchingFlag 5596 beq ChkMoveDir ;if not, branch ahead 5597 ldy #$02 ;if big and crouching, load y with 2 5598 ChkMoveDir: sty Player_BoundBoxCtrl ;set contents of Y as player's bounding box size control 5599 lda #$01 ;set moving direction to right by default 5600 ldy Player_X_Speed ;check player's horizontal speed 5601 beq PlayerSubs ;if not moving at all horizontally, skip this part 5602 bpl SetMoveDir ;if moving to the right, use default moving direction 5603 asl ;otherwise change to move to the left 5604 SetMoveDir: sta Player_MovingDir ;set moving direction 5605 PlayerSubs: jsr ScrollHandler ;move the screen if necessary 5606 jsr GetPlayerOffscreenBits ;get player's offscreen bits 5607 jsr RelativePlayerPosition ;get coordinates relative to the screen 5608 ldx #$00 ;set offset for player object 5609 jsr BoundingBoxCore ;get player's bounding box coordinates 5610 jsr PlayerBGCollision ;do collision detection and process 5611 lda Player_Y_Position 5612 cmp #$40 ;check to see if player is higher than 64th pixel 5613 bcc PlayerHole ;if so, branch ahead 5614 lda GameEngineSubroutine 5615 cmp #$05 ;if running end-of-level routine, branch ahead 5616 beq PlayerHole 5617 cmp #$07 ;if running player entrance routine, branch ahead 5618 beq PlayerHole 5619 cmp #$04 ;if running routines $00-$03, branch ahead 5620 bcc PlayerHole 5621 lda Player_SprAttrib 5622 and #%11011111 ;otherwise nullify player's 5623 sta Player_SprAttrib ;background priority flag 5624 PlayerHole: lda Player_Y_HighPos ;check player's vertical high byte 5625 cmp #$02 ;for below the screen 5626 bmi ExitCtrl ;branch to leave if not that far down 5627 ldx #$01 5628 stx ScrollLock ;set scroll lock 5629 ldy #$04 5630 sty $07 ;set value here 5631 ldx #$00 ;use X as flag, and clear for cloud level 5632 ldy GameTimerExpiredFlag ;check game timer expiration flag 5633 bne HoleDie ;if set, branch 5634 ldy CloudTypeOverride ;check for cloud type override 5635 bne ChkHoleX ;skip to last part if found 5636 HoleDie: inx ;set flag in X for player death 5637 ldy GameEngineSubroutine 5638 cpy #$0b ;check for some other routine running 5639 beq ChkHoleX ;if so, branch ahead 5640 ldy DeathMusicLoaded ;check value here 5641 bne HoleBottom ;if already set, branch to next part 5642 iny 5643 sty EventMusicQueue ;otherwise play death music 5644 sty DeathMusicLoaded ;and set value here 5645 HoleBottom: ldy #$06 5646 sty $07 ;change value here 5647 ChkHoleX: cmp $07 ;compare vertical high byte with value set here 5648 bmi ExitCtrl ;if less, branch to leave 5649 dex ;otherwise decrement flag in X 5650 bmi CloudExit ;if flag was clear, branch to set modes and other values 5651 ldy EventMusicBuffer ;check to see if music is still playing 5652 bne ExitCtrl ;branch to leave if so 5653 lda #$06 ;otherwise set to run lose life routine 5654 sta GameEngineSubroutine ;on next frame 5655 ExitCtrl: rts ;leave 5656 5657 CloudExit: 5658 lda #$00 5659 sta JoypadOverride ;clear controller override bits if any are set 5660 jsr SetEntr ;do sub to set secondary mode 5661 inc AltEntranceControl ;set mode of entry to 3 5662 rts 5663 5664 ;------------------------------------------------------------------------------------- 5665 5666 Vine_AutoClimb: 5667 lda Player_Y_HighPos ;check to see whether player reached position 5668 bne AutoClimb ;above the status bar yet and if so, set modes 5669 lda Player_Y_Position 5670 cmp #$e4 5671 bcc SetEntr 5672 AutoClimb: lda #%00001000 ;set controller bits override to up 5673 sta JoypadOverride 5674 ldy #$03 ;set player state to climbing 5675 sty Player_State 5676 jmp AutoControlPlayer 5677 SetEntr: lda #$02 ;set starting position to override 5678 sta AltEntranceControl 5679 jmp ChgAreaMode ;set modes 5680 5681 ;------------------------------------------------------------------------------------- 5682 5683 VerticalPipeEntry: 5684 lda #$01 ;set 1 as movement amount 5685 jsr MovePlayerYAxis ;do sub to move player downwards 5686 jsr ScrollHandler ;do sub to scroll screen with saved force if necessary 5687 ldy #$00 ;load default mode of entry 5688 lda WarpZoneControl ;check warp zone control variable/flag 5689 bne ChgAreaPipe ;if set, branch to use mode 0 5690 iny 5691 lda AreaType ;check for castle level type 5692 cmp #$03 5693 bne ChgAreaPipe ;if not castle type level, use mode 1 5694 iny 5695 jmp ChgAreaPipe ;otherwise use mode 2 5696 5697 MovePlayerYAxis: 5698 clc 5699 adc Player_Y_Position ;add contents of A to player position 5700 sta Player_Y_Position 5701 rts 5702 5703 ;------------------------------------------------------------------------------------- 5704 5705 SideExitPipeEntry: 5706 jsr EnterSidePipe ;execute sub to move player to the right 5707 ldy #$02 5708 ChgAreaPipe: dec ChangeAreaTimer ;decrement timer for change of area 5709 bne ExitCAPipe 5710 sty AltEntranceControl ;when timer expires set mode of alternate entry 5711 ChgAreaMode: inc DisableScreenFlag ;set flag to disable screen output 5712 lda #$00 5713 sta OperMode_Task ;set secondary mode of operation 5714 sta Sprite0HitDetectFlag ;disable sprite 0 check 5715 ExitCAPipe: rts ;leave 5716 5717 EnterSidePipe: 5718 lda #$08 ;set player's horizontal speed 5719 sta Player_X_Speed 5720 ldy #$01 ;set controller right button by default 5721 lda Player_X_Position ;mask out higher nybble of player's 5722 and #%00001111 ;horizontal position 5723 bne RightPipe 5724 sta Player_X_Speed ;if lower nybble = 0, set as horizontal speed 5725 tay ;and nullify controller bit override here 5726 RightPipe: tya ;use contents of Y to 5727 jsr AutoControlPlayer ;execute player control routine with ctrl bits nulled 5728 rts 5729 5730 ;------------------------------------------------------------------------------------- 5731 5732 PlayerChangeSize: 5733 lda TimerControl ;check master timer control 5734 cmp #$f8 ;for specific moment in time 5735 bne EndChgSize ;branch if before or after that point 5736 jmp InitChangeSize ;otherwise run code to get growing/shrinking going 5737 EndChgSize: cmp #$c4 ;check again for another specific moment 5738 bne ExitChgSize ;and branch to leave if before or after that point 5739 jsr DonePlayerTask ;otherwise do sub to init timer control and set routine 5740 ExitChgSize: rts ;and then leave 5741 5742 ;------------------------------------------------------------------------------------- 5743 5744 PlayerInjuryBlink: 5745 lda TimerControl ;check master timer control 5746 cmp #$f0 ;for specific moment in time 5747 bcs ExitBlink ;branch if before that point 5748 cmp #$c8 ;check again for another specific point 5749 beq DonePlayerTask ;branch if at that point, and not before or after 5750 jmp PlayerCtrlRoutine ;otherwise run player control routine 5751 ExitBlink: bne ExitBoth ;do unconditional branch to leave 5752 5753 InitChangeSize: 5754 ldy PlayerChangeSizeFlag ;if growing/shrinking flag already set 5755 bne ExitBoth ;then branch to leave 5756 sty PlayerAnimCtrl ;otherwise initialize player's animation frame control 5757 inc PlayerChangeSizeFlag ;set growing/shrinking flag 5758 lda PlayerSize 5759 eor #$01 ;invert player's size 5760 sta PlayerSize 5761 ExitBoth: rts ;leave 5762 5763 ;------------------------------------------------------------------------------------- 5764 ;$00 - used in CyclePlayerPalette to store current palette to cycle 5765 5766 PlayerDeath: 5767 lda TimerControl ;check master timer control 5768 cmp #$f0 ;for specific moment in time 5769 bcs ExitDeath ;branch to leave if before that point 5770 jmp PlayerCtrlRoutine ;otherwise run player control routine 5771 5772 DonePlayerTask: 5773 lda #$00 5774 sta TimerControl ;initialize master timer control to continue timers 5775 lda #$08 5776 sta GameEngineSubroutine ;set player control routine to run next frame 5777 rts ;leave 5778 5779 PlayerFireFlower: 5780 lda TimerControl ;check master timer control 5781 cmp #$c0 ;for specific moment in time 5782 beq ResetPalFireFlower ;branch if at moment, not before or after 5783 lda FrameCounter ;get frame counter 5784 lsr 5785 lsr ;divide by four to change every four frames 5786 5787 CyclePlayerPalette: 5788 and #$03 ;mask out all but d1-d0 (previously d3-d2) 5789 sta $00 ;store result here to use as palette bits 5790 lda Player_SprAttrib ;get player attributes 5791 and #%11111100 ;save any other bits but palette bits 5792 ora $00 ;add palette bits 5793 sta Player_SprAttrib ;store as new player attributes 5794 rts ;and leave 5795 5796 ResetPalFireFlower: 5797 jsr DonePlayerTask ;do sub to init timer control and run player control routine 5798 5799 ResetPalStar: 5800 lda Player_SprAttrib ;get player attributes 5801 and #%11111100 ;mask out palette bits to force palette 0 5802 sta Player_SprAttrib ;store as new player attributes 5803 rts ;and leave 5804 5805 ExitDeath: 5806 rts ;leave from death routine 5807 5808 ;------------------------------------------------------------------------------------- 5809 5810 FlagpoleSlide: 5811 lda Enemy_ID+5 ;check special use enemy slot 5812 cmp #FlagpoleFlagObject ;for flagpole flag object 5813 bne NoFPObj ;if not found, branch to something residual 5814 lda FlagpoleSoundQueue ;load flagpole sound 5815 sta Square1SoundQueue ;into square 1's sfx queue 5816 lda #$00 5817 sta FlagpoleSoundQueue ;init flagpole sound queue 5818 ldy Player_Y_Position 5819 cpy #$9e ;check to see if player has slid down 5820 bcs SlidePlayer ;far enough, and if so, branch with no controller bits set 5821 lda #$04 ;otherwise force player to climb down (to slide) 5822 SlidePlayer: jmp AutoControlPlayer ;jump to player control routine 5823 NoFPObj: inc GameEngineSubroutine ;increment to next routine (this may 5824 rts ;be residual code) 5825 5826 ;------------------------------------------------------------------------------------- 5827 5828 Hidden1UpCoinAmts: 5829 .db $15, $23, $16, $1b, $17, $18, $23, $63 5830 5831 PlayerEndLevel: 5832 lda #$01 ;force player to walk to the right 5833 jsr AutoControlPlayer 5834 lda Player_Y_Position ;check player's vertical position 5835 cmp #$ae 5836 bcc ChkStop ;if player is not yet off the flagpole, skip this part 5837 lda ScrollLock ;if scroll lock not set, branch ahead to next part 5838 beq ChkStop ;because we only need to do this part once 5839 lda #EndOfLevelMusic 5840 sta EventMusicQueue ;load win level music in event music queue 5841 lda #$00 5842 sta ScrollLock ;turn off scroll lock to skip this part later 5843 ChkStop: lda Player_CollisionBits ;get player collision bits 5844 lsr ;check for d0 set 5845 bcs RdyNextA ;if d0 set, skip to next part 5846 lda StarFlagTaskControl ;if star flag task control already set, 5847 bne InCastle ;go ahead with the rest of the code 5848 inc StarFlagTaskControl ;otherwise set task control now (this gets ball rolling!) 5849 InCastle: lda #%00100000 ;set player's background priority bit to 5850 sta Player_SprAttrib ;give illusion of being inside the castle 5851 RdyNextA: lda StarFlagTaskControl 5852 cmp #$05 ;if star flag task control not yet set 5853 bne ExitNA ;beyond last valid task number, branch to leave 5854 inc LevelNumber ;increment level number used for game logic 5855 lda LevelNumber 5856 cmp #$03 ;check to see if we have yet reached level -4 5857 bne NextArea ;and skip this last part here if not 5858 ldy WorldNumber ;get world number as offset 5859 lda CoinTallyFor1Ups ;check third area coin tally for bonus 1-ups 5860 cmp Hidden1UpCoinAmts,y ;against minimum value, if player has not collected 5861 bcc NextArea ;at least this number of coins, leave flag clear 5862 inc Hidden1UpFlag ;otherwise set hidden 1-up box control flag 5863 NextArea: inc AreaNumber ;increment area number used for address loader 5864 jsr LoadAreaPointer ;get new level pointer 5865 inc FetchNewGameTimerFlag ;set flag to load new game timer 5866 jsr ChgAreaMode ;do sub to set secondary mode, disable screen and sprite 0 5867 sta HalfwayPage ;reset halfway page to 0 (beginning) 5868 lda #Silence 5869 sta EventMusicQueue ;silence music and leave 5870 ExitNA: rts 5871 5872 ;------------------------------------------------------------------------------------- 5873 5874 PlayerMovementSubs: 5875 lda #$00 ;set A to init crouch flag by default 5876 ldy PlayerSize ;is player small? 5877 bne SetCrouch ;if so, branch 5878 lda Player_State ;check state of player 5879 bne ProcMove ;if not on the ground, branch 5880 lda Up_Down_Buttons ;load controller bits for up and down 5881 and #%00000100 ;single out bit for down button 5882 SetCrouch: sta CrouchingFlag ;store value in crouch flag 5883 ProcMove: jsr PlayerPhysicsSub ;run sub related to jumping and swimming 5884 lda PlayerChangeSizeFlag ;if growing/shrinking flag set, 5885 bne NoMoveSub ;branch to leave 5886 lda Player_State 5887 cmp #$03 ;get player state 5888 beq MoveSubs ;if climbing, branch ahead, leave timer unset 5889 ldy #$18 5890 sty ClimbSideTimer ;otherwise reset timer now 5891 MoveSubs: jsr JumpEngine 5892 5893 .dw OnGroundStateSub 5894 .dw JumpSwimSub 5895 .dw FallingSub 5896 .dw ClimbingSub 5897 5898 NoMoveSub: rts 5899 5900 ;------------------------------------------------------------------------------------- 5901 ;$00 - used by ClimbingSub to store high vertical adder 5902 5903 OnGroundStateSub: 5904 jsr GetPlayerAnimSpeed ;do a sub to set animation frame timing 5905 lda Left_Right_Buttons 5906 beq GndMove ;if left/right controller bits not set, skip instruction 5907 sta PlayerFacingDir ;otherwise set new facing direction 5908 GndMove: jsr ImposeFriction ;do a sub to impose friction on player's walk/run 5909 jsr MovePlayerHorizontally ;do another sub to move player horizontally 5910 sta Player_X_Scroll ;set returned value as player's movement speed for scroll 5911 rts 5912 5913 ;-------------------------------- 5914 5915 FallingSub: 5916 lda VerticalForceDown 5917 sta VerticalForce ;dump vertical movement force for falling into main one 5918 jmp LRAir ;movement force, then skip ahead to process left/right movement 5919 5920 ;-------------------------------- 5921 5922 JumpSwimSub: 5923 ldy Player_Y_Speed ;if player's vertical speed zero 5924 bpl DumpFall ;or moving downwards, branch to falling 5925 lda A_B_Buttons 5926 and #A_Button ;check to see if A button is being pressed 5927 and PreviousA_B_Buttons ;and was pressed in previous frame 5928 bne ProcSwim ;if so, branch elsewhere 5929 lda JumpOrigin_Y_Position ;get vertical position player jumped from 5930 sec 5931 sbc Player_Y_Position ;subtract current from original vertical coordinate 5932 cmp DiffToHaltJump ;compare to value set here to see if player is in mid-jump 5933 bcc ProcSwim ;or just starting to jump, if just starting, skip ahead 5934 DumpFall: lda VerticalForceDown ;otherwise dump falling into main fractional 5935 sta VerticalForce 5936 ProcSwim: lda SwimmingFlag ;if swimming flag not set, 5937 beq LRAir ;branch ahead to last part 5938 jsr GetPlayerAnimSpeed ;do a sub to get animation frame timing 5939 lda Player_Y_Position 5940 cmp #$14 ;check vertical position against preset value 5941 bcs LRWater ;if not yet reached a certain position, branch ahead 5942 lda #$18 5943 sta VerticalForce ;otherwise set fractional 5944 LRWater: lda Left_Right_Buttons ;check left/right controller bits (check for swimming) 5945 beq LRAir ;if not pressing any, skip 5946 sta PlayerFacingDir ;otherwise set facing direction accordingly 5947 LRAir: lda Left_Right_Buttons ;check left/right controller bits (check for jumping/falling) 5948 beq JSMove ;if not pressing any, skip 5949 jsr ImposeFriction ;otherwise process horizontal movement 5950 JSMove: jsr MovePlayerHorizontally ;do a sub to move player horizontally 5951 sta Player_X_Scroll ;set player's speed here, to be used for scroll later 5952 lda GameEngineSubroutine 5953 cmp #$0b ;check for specific routine selected 5954 bne ExitMov1 ;branch if not set to run 5955 lda #$28 5956 sta VerticalForce ;otherwise set fractional 5957 ExitMov1: jmp MovePlayerVertically ;jump to move player vertically, then leave 5958 5959 ;-------------------------------- 5960 5961 ClimbAdderLow: 5962 .db $0e, $04, $fc, $f2 5963 ClimbAdderHigh: 5964 .db $00, $00, $ff, $ff 5965 5966 ClimbingSub: 5967 lda Player_YMF_Dummy 5968 clc ;add movement force to dummy variable 5969 adc Player_Y_MoveForce ;save with carry 5970 sta Player_YMF_Dummy 5971 ldy #$00 ;set default adder here 5972 lda Player_Y_Speed ;get player's vertical speed 5973 bpl MoveOnVine ;if not moving upwards, branch 5974 dey ;otherwise set adder to $ff 5975 MoveOnVine: sty $00 ;store adder here 5976 adc Player_Y_Position ;add carry to player's vertical position 5977 sta Player_Y_Position ;and store to move player up or down 5978 lda Player_Y_HighPos 5979 adc $00 ;add carry to player's page location 5980 sta Player_Y_HighPos ;and store 5981 lda Left_Right_Buttons ;compare left/right controller bits 5982 and Player_CollisionBits ;to collision flag 5983 beq InitCSTimer ;if not set, skip to end 5984 ldy ClimbSideTimer ;otherwise check timer 5985 bne ExitCSub ;if timer not expired, branch to leave 5986 ldy #$18 5987 sty ClimbSideTimer ;otherwise set timer now 5988 ldx #$00 ;set default offset here 5989 ldy PlayerFacingDir ;get facing direction 5990 lsr ;move right button controller bit to carry 5991 bcs ClimbFD ;if controller right pressed, branch ahead 5992 inx 5993 inx ;otherwise increment offset by 2 bytes 5994 ClimbFD: dey ;check to see if facing right 5995 beq CSetFDir ;if so, branch, do not increment 5996 inx ;otherwise increment by 1 byte 5997 CSetFDir: lda Player_X_Position 5998 clc ;add or subtract from player's horizontal position 5999 adc ClimbAdderLow,x ;using value here as adder and X as offset 6000 sta Player_X_Position 6001 lda Player_PageLoc ;add or subtract carry or borrow using value here 6002 adc ClimbAdderHigh,x ;from the player's page location 6003 sta Player_PageLoc 6004 lda Left_Right_Buttons ;get left/right controller bits again 6005 eor #%00000011 ;invert them and store them while player 6006 sta PlayerFacingDir ;is on vine to face player in opposite direction 6007 ExitCSub: rts ;then leave 6008 InitCSTimer: sta ClimbSideTimer ;initialize timer here 6009 rts 6010 6011 ;------------------------------------------------------------------------------------- 6012 ;$00 - used to store offset to friction data 6013 6014 JumpMForceData: 6015 .db $20, $20, $1e, $28, $28, $0d, $04 6016 6017 FallMForceData: 6018 .db $70, $70, $60, $90, $90, $0a, $09 6019 6020 PlayerYSpdData: 6021 .db $fc, $fc, $fc, $fb, $fb, $fe, $ff 6022 6023 InitMForceData: 6024 .db $00, $00, $00, $00, $00, $80, $00 6025 6026 MaxLeftXSpdData: 6027 .db $d8, $e8, $f0 6028 6029 MaxRightXSpdData: 6030 .db $28, $18, $10 6031 .db $0c ;used for pipe intros 6032 6033 FrictionData: 6034 .db $e4, $98, $d0 6035 6036 Climb_Y_SpeedData: 6037 .db $00, $ff, $01 6038 6039 Climb_Y_MForceData: 6040 .db $00, $20, $ff 6041 6042 PlayerPhysicsSub: 6043 lda Player_State ;check player state 6044 cmp #$03 6045 bne CheckForJumping ;if not climbing, branch 6046 ldy #$00 6047 lda Up_Down_Buttons ;get controller bits for up/down 6048 and Player_CollisionBits ;check against player's collision detection bits 6049 beq ProcClimb ;if not pressing up or down, branch 6050 iny 6051 and #%00001000 ;check for pressing up 6052 bne ProcClimb 6053 iny 6054 ProcClimb: ldx Climb_Y_MForceData,y ;load value here 6055 stx Player_Y_MoveForce ;store as vertical movement force 6056 lda #$08 ;load default animation timing 6057 ldx Climb_Y_SpeedData,y ;load some other value here 6058 stx Player_Y_Speed ;store as vertical speed 6059 bmi SetCAnim ;if climbing down, use default animation timing value 6060 lsr ;otherwise divide timer setting by 2 6061 SetCAnim: sta PlayerAnimTimerSet ;store animation timer setting and leave 6062 rts 6063 6064 CheckForJumping: 6065 lda JumpspringAnimCtrl ;if jumpspring animating, 6066 bne NoJump ;skip ahead to something else 6067 lda A_B_Buttons ;check for A button press 6068 and #A_Button 6069 beq NoJump ;if not, branch to something else 6070 and PreviousA_B_Buttons ;if button not pressed in previous frame, branch 6071 beq ProcJumping 6072 NoJump: jmp X_Physics ;otherwise, jump to something else 6073 6074 ProcJumping: 6075 lda Player_State ;check player state 6076 beq InitJS ;if on the ground, branch 6077 lda SwimmingFlag ;if swimming flag not set, jump to do something else 6078 beq NoJump ;to prevent midair jumping, otherwise continue 6079 lda JumpSwimTimer ;if jump/swim timer nonzero, branch 6080 bne InitJS 6081 lda Player_Y_Speed ;check player's vertical speed 6082 bpl InitJS ;if player's vertical speed motionless or down, branch 6083 jmp X_Physics ;if timer at zero and player still rising, do not swim 6084 InitJS: lda #$20 ;set jump/swim timer 6085 sta JumpSwimTimer 6086 ldy #$00 ;initialize vertical force and dummy variable 6087 sty Player_YMF_Dummy 6088 sty Player_Y_MoveForce 6089 lda Player_Y_HighPos ;get vertical high and low bytes of jump origin 6090 sta JumpOrigin_Y_HighPos ;and store them next to each other here 6091 lda Player_Y_Position 6092 sta JumpOrigin_Y_Position 6093 lda #$01 ;set player state to jumping/swimming 6094 sta Player_State 6095 lda Player_XSpeedAbsolute ;check value related to walking/running speed 6096 cmp #$09 6097 bcc ChkWtr ;branch if below certain values, increment Y 6098 iny ;for each amount equal or exceeded 6099 cmp #$10 6100 bcc ChkWtr 6101 iny 6102 cmp #$19 6103 bcc ChkWtr 6104 iny 6105 cmp #$1c 6106 bcc ChkWtr ;note that for jumping, range is 0-4 for Y 6107 iny 6108 ChkWtr: lda #$01 ;set value here (apparently always set to 1) 6109 sta DiffToHaltJump 6110 lda SwimmingFlag ;if swimming flag disabled, branch 6111 beq GetYPhy 6112 ldy #$05 ;otherwise set Y to 5, range is 5-6 6113 lda Whirlpool_Flag ;if whirlpool flag not set, branch 6114 beq GetYPhy 6115 iny ;otherwise increment to 6 6116 GetYPhy: lda JumpMForceData,y ;store appropriate jump/swim 6117 sta VerticalForce ;data here 6118 lda FallMForceData,y 6119 sta VerticalForceDown 6120 lda InitMForceData,y 6121 sta Player_Y_MoveForce 6122 lda PlayerYSpdData,y 6123 sta Player_Y_Speed 6124 lda SwimmingFlag ;if swimming flag disabled, branch 6125 beq PJumpSnd 6126 lda #Sfx_EnemyStomp ;load swim/goomba stomp sound into 6127 sta Square1SoundQueue ;square 1's sfx queue 6128 lda Player_Y_Position 6129 cmp #$14 ;check vertical low byte of player position 6130 bcs X_Physics ;if below a certain point, branch 6131 lda #$00 ;otherwise reset player's vertical speed 6132 sta Player_Y_Speed ;and jump to something else to keep player 6133 jmp X_Physics ;from swimming above water level 6134 PJumpSnd: lda #Sfx_BigJump ;load big mario's jump sound by default 6135 ldy PlayerSize ;is mario big? 6136 beq SJumpSnd 6137 lda #Sfx_SmallJump ;if not, load small mario's jump sound 6138 SJumpSnd: sta Square1SoundQueue ;store appropriate jump sound in square 1 sfx queue 6139 X_Physics: ldy #$00 6140 sty $00 ;init value here 6141 lda Player_State ;if mario is on the ground, branch 6142 beq ProcPRun 6143 lda Player_XSpeedAbsolute ;check something that seems to be related 6144 cmp #$19 ;to mario's speed 6145 bcs GetXPhy ;if =>$19 branch here 6146 bcc ChkRFast ;if not branch elsewhere 6147 ProcPRun: iny ;if mario on the ground, increment Y 6148 lda AreaType ;check area type 6149 beq ChkRFast ;if water type, branch 6150 dey ;decrement Y by default for non-water type area 6151 lda Left_Right_Buttons ;get left/right controller bits 6152 cmp Player_MovingDir ;check against moving direction 6153 bne ChkRFast ;if controller bits <> moving direction, skip this part 6154 lda A_B_Buttons ;check for b button pressed 6155 and #B_Button 6156 bne SetRTmr ;if pressed, skip ahead to set timer 6157 lda RunningTimer ;check for running timer set 6158 bne GetXPhy ;if set, branch 6159 ChkRFast: iny ;if running timer not set or level type is water, 6160 inc $00 ;increment Y again and temp variable in memory 6161 lda RunningSpeed 6162 bne FastXSp ;if running speed set here, branch 6163 lda Player_XSpeedAbsolute 6164 cmp #$21 ;otherwise check player's walking/running speed 6165 bcc GetXPhy ;if less than a certain amount, branch ahead 6166 FastXSp: inc $00 ;if running speed set or speed => $21 increment $00 6167 jmp GetXPhy ;and jump ahead 6168 SetRTmr: lda #$0a ;if b button pressed, set running timer 6169 sta RunningTimer 6170 GetXPhy: lda MaxLeftXSpdData,y ;get maximum speed to the left 6171 sta MaximumLeftSpeed 6172 lda GameEngineSubroutine ;check for specific routine running 6173 cmp #$07 ;(player entrance) 6174 bne GetXPhy2 ;if not running, skip and use old value of Y 6175 ldy #$03 ;otherwise set Y to 3 6176 GetXPhy2: lda MaxRightXSpdData,y ;get maximum speed to the right 6177 sta MaximumRightSpeed 6178 ldy $00 ;get other value in memory 6179 lda FrictionData,y ;get value using value in memory as offset 6180 sta FrictionAdderLow 6181 lda #$00 6182 sta FrictionAdderHigh ;init something here 6183 lda PlayerFacingDir 6184 cmp Player_MovingDir ;check facing direction against moving direction 6185 beq ExitPhy ;if the same, branch to leave 6186 asl FrictionAdderLow ;otherwise shift d7 of friction adder low into carry 6187 rol FrictionAdderHigh ;then rotate carry onto d0 of friction adder high 6188 ExitPhy: rts ;and then leave 6189 6190 ;------------------------------------------------------------------------------------- 6191 6192 PlayerAnimTmrData: 6193 .db $02, $04, $07 6194 6195 GetPlayerAnimSpeed: 6196 ldy #$00 ;initialize offset in Y 6197 lda Player_XSpeedAbsolute ;check player's walking/running speed 6198 cmp #$1c ;against preset amount 6199 bcs SetRunSpd ;if greater than a certain amount, branch ahead 6200 iny ;otherwise increment Y 6201 cmp #$0e ;compare against lower amount 6202 bcs ChkSkid ;if greater than this but not greater than first, skip increment 6203 iny ;otherwise increment Y again 6204 ChkSkid: lda SavedJoypadBits ;get controller bits 6205 and #%01111111 ;mask out A button 6206 beq SetAnimSpd ;if no other buttons pressed, branch ahead of all this 6207 and #$03 ;mask out all others except left and right 6208 cmp Player_MovingDir ;check against moving direction 6209 bne ProcSkid ;if left/right controller bits <> moving direction, branch 6210 lda #$00 ;otherwise set zero value here 6211 SetRunSpd: sta RunningSpeed ;store zero or running speed here 6212 jmp SetAnimSpd 6213 ProcSkid: lda Player_XSpeedAbsolute ;check player's walking/running speed 6214 cmp #$0b ;against one last amount 6215 bcs SetAnimSpd ;if greater than this amount, branch 6216 lda PlayerFacingDir 6217 sta Player_MovingDir ;otherwise use facing direction to set moving direction 6218 lda #$00 6219 sta Player_X_Speed ;nullify player's horizontal speed 6220 sta Player_X_MoveForce ;and dummy variable for player 6221 SetAnimSpd: lda PlayerAnimTmrData,y ;get animation timer setting using Y as offset 6222 sta PlayerAnimTimerSet 6223 rts 6224 6225 ;------------------------------------------------------------------------------------- 6226 6227 ImposeFriction: 6228 and Player_CollisionBits ;perform AND between left/right controller bits and collision flag 6229 cmp #$00 ;then compare to zero (this instruction is redundant) 6230 bne JoypFrict ;if any bits set, branch to next part 6231 lda Player_X_Speed 6232 beq SetAbsSpd ;if player has no horizontal speed, branch ahead to last part 6233 bpl RghtFrict ;if player moving to the right, branch to slow 6234 bmi LeftFrict ;otherwise logic dictates player moving left, branch to slow 6235 JoypFrict: lsr ;put right controller bit into carry 6236 bcc RghtFrict ;if left button pressed, carry = 0, thus branch 6237 LeftFrict: lda Player_X_MoveForce ;load value set here 6238 clc 6239 adc FrictionAdderLow ;add to it another value set here 6240 sta Player_X_MoveForce ;store here 6241 lda Player_X_Speed 6242 adc FrictionAdderHigh ;add value plus carry to horizontal speed 6243 sta Player_X_Speed ;set as new horizontal speed 6244 cmp MaximumRightSpeed ;compare against maximum value for right movement 6245 bmi XSpdSign ;if horizontal speed greater negatively, branch 6246 lda MaximumRightSpeed ;otherwise set preset value as horizontal speed 6247 sta Player_X_Speed ;thus slowing the player's left movement down 6248 jmp SetAbsSpd ;skip to the end 6249 RghtFrict: lda Player_X_MoveForce ;load value set here 6250 sec 6251 sbc FrictionAdderLow ;subtract from it another value set here 6252 sta Player_X_MoveForce ;store here 6253 lda Player_X_Speed 6254 sbc FrictionAdderHigh ;subtract value plus borrow from horizontal speed 6255 sta Player_X_Speed ;set as new horizontal speed 6256 cmp MaximumLeftSpeed ;compare against maximum value for left movement 6257 bpl XSpdSign ;if horizontal speed greater positively, branch 6258 lda MaximumLeftSpeed ;otherwise set preset value as horizontal speed 6259 sta Player_X_Speed ;thus slowing the player's right movement down 6260 XSpdSign: cmp #$00 ;if player not moving or moving to the right, 6261 bpl SetAbsSpd ;branch and leave horizontal speed value unmodified 6262 eor #$ff 6263 clc ;otherwise get two's compliment to get absolute 6264 adc #$01 ;unsigned walking/running speed 6265 SetAbsSpd: sta Player_XSpeedAbsolute ;store walking/running speed here and leave 6266 rts 6267 6268 ;------------------------------------------------------------------------------------- 6269 ;$00 - used to store downward movement force in FireballObjCore 6270 ;$02 - used to store maximum vertical speed in FireballObjCore 6271 ;$07 - used to store pseudorandom bit in BubbleCheck 6272 6273 ProcFireball_Bubble: 6274 lda PlayerStatus ;check player's status 6275 cmp #$02 6276 bcc ProcAirBubbles ;if not fiery, branch 6277 lda A_B_Buttons 6278 and #B_Button ;check for b button pressed 6279 beq ProcFireballs ;branch if not pressed 6280 and PreviousA_B_Buttons 6281 bne ProcFireballs ;if button pressed in previous frame, branch 6282 lda FireballCounter ;load fireball counter 6283 and #%00000001 ;get LSB and use as offset for buffer 6284 tax 6285 lda Fireball_State,x ;load fireball state 6286 bne ProcFireballs ;if not inactive, branch 6287 ldy Player_Y_HighPos ;if player too high or too low, branch 6288 dey 6289 bne ProcFireballs 6290 lda CrouchingFlag ;if player crouching, branch 6291 bne ProcFireballs 6292 lda Player_State ;if player's state = climbing, branch 6293 cmp #$03 6294 beq ProcFireballs 6295 lda #Sfx_Fireball ;play fireball sound effect 6296 sta Square1SoundQueue 6297 lda #$02 ;load state 6298 sta Fireball_State,x 6299 ldy PlayerAnimTimerSet ;copy animation frame timer setting 6300 sty FireballThrowingTimer ;into fireball throwing timer 6301 dey 6302 sty PlayerAnimTimer ;decrement and store in player's animation timer 6303 inc FireballCounter ;increment fireball counter 6304 6305 ProcFireballs: 6306 ldx #$00 6307 jsr FireballObjCore ;process first fireball object 6308 ldx #$01 6309 jsr FireballObjCore ;process second fireball object, then do air bubbles 6310 6311 ProcAirBubbles: 6312 lda AreaType ;if not water type level, skip the rest of this 6313 bne BublExit 6314 ldx #$02 ;otherwise load counter and use as offset 6315 BublLoop: stx ObjectOffset ;store offset 6316 jsr BubbleCheck ;check timers and coordinates, create air bubble 6317 jsr RelativeBubblePosition ;get relative coordinates 6318 jsr GetBubbleOffscreenBits ;get offscreen information 6319 jsr DrawBubble ;draw the air bubble 6320 dex 6321 bpl BublLoop ;do this until all three are handled 6322 BublExit: rts ;then leave 6323 6324 FireballXSpdData: 6325 .db $40, $c0 6326 6327 FireballObjCore: 6328 stx ObjectOffset ;store offset as current object 6329 lda Fireball_State,x ;check for d7 = 1 6330 asl 6331 bcs FireballExplosion ;if so, branch to get relative coordinates and draw explosion 6332 ldy Fireball_State,x ;if fireball inactive, branch to leave 6333 beq NoFBall 6334 dey ;if fireball state set to 1, skip this part and just run it 6335 beq RunFB 6336 lda Player_X_Position ;get player's horizontal position 6337 adc #$04 ;add four pixels and store as fireball's horizontal position 6338 sta Fireball_X_Position,x 6339 lda Player_PageLoc ;get player's page location 6340 adc #$00 ;add carry and store as fireball's page location 6341 sta Fireball_PageLoc,x 6342 lda Player_Y_Position ;get player's vertical position and store 6343 sta Fireball_Y_Position,x 6344 lda #$01 ;set high byte of vertical position 6345 sta Fireball_Y_HighPos,x 6346 ldy PlayerFacingDir ;get player's facing direction 6347 dey ;decrement to use as offset here 6348 lda FireballXSpdData,y ;set horizontal speed of fireball accordingly 6349 sta Fireball_X_Speed,x 6350 lda #$04 ;set vertical speed of fireball 6351 sta Fireball_Y_Speed,x 6352 lda #$07 6353 sta Fireball_BoundBoxCtrl,x ;set bounding box size control for fireball 6354 dec Fireball_State,x ;decrement state to 1 to skip this part from now on 6355 RunFB: txa ;add 7 to offset to use 6356 clc ;as fireball offset for next routines 6357 adc #$07 6358 tax 6359 lda #$50 ;set downward movement force here 6360 sta $00 6361 lda #$03 ;set maximum speed here 6362 sta $02 6363 lda #$00 6364 jsr ImposeGravity ;do sub here to impose gravity on fireball and move vertically 6365 jsr MoveObjectHorizontally ;do another sub to move it horizontally 6366 ldx ObjectOffset ;return fireball offset to X 6367 jsr RelativeFireballPosition ;get relative coordinates 6368 jsr GetFireballOffscreenBits ;get offscreen information 6369 jsr GetFireballBoundBox ;get bounding box coordinates 6370 jsr FireballBGCollision ;do fireball to background collision detection 6371 lda FBall_OffscreenBits ;get fireball offscreen bits 6372 and #%11001100 ;mask out certain bits 6373 bne EraseFB ;if any bits still set, branch to kill fireball 6374 jsr FireballEnemyCollision ;do fireball to enemy collision detection and deal with collisions 6375 jmp DrawFireball ;draw fireball appropriately and leave 6376 EraseFB: lda #$00 ;erase fireball state 6377 sta Fireball_State,x 6378 NoFBall: rts ;leave 6379 6380 FireballExplosion: 6381 jsr RelativeFireballPosition 6382 jmp DrawExplosion_Fireball 6383 6384 BubbleCheck: 6385 lda PseudoRandomBitReg+1,x ;get part of LSFR 6386 and #$01 6387 sta $07 ;store pseudorandom bit here 6388 lda Bubble_Y_Position,x ;get vertical coordinate for air bubble 6389 cmp #$f8 ;if offscreen coordinate not set, 6390 bne MoveBubl ;branch to move air bubble 6391 lda AirBubbleTimer ;if air bubble timer not expired, 6392 bne ExitBubl ;branch to leave, otherwise create new air bubble 6393 6394 SetupBubble: 6395 ldy #$00 ;load default value here 6396 lda PlayerFacingDir ;get player's facing direction 6397 lsr ;move d0 to carry 6398 bcc PosBubl ;branch to use default value if facing left 6399 ldy #$08 ;otherwise load alternate value here 6400 PosBubl: tya ;use value loaded as adder 6401 adc Player_X_Position ;add to player's horizontal position 6402 sta Bubble_X_Position,x ;save as horizontal position for airbubble 6403 lda Player_PageLoc 6404 adc #$00 ;add carry to player's page location 6405 sta Bubble_PageLoc,x ;save as page location for airbubble 6406 lda Player_Y_Position 6407 clc ;add eight pixels to player's vertical position 6408 adc #$08 6409 sta Bubble_Y_Position,x ;save as vertical position for air bubble 6410 lda #$01 6411 sta Bubble_Y_HighPos,x ;set vertical high byte for air bubble 6412 ldy $07 ;get pseudorandom bit, use as offset 6413 lda BubbleTimerData,y ;get data for air bubble timer 6414 sta AirBubbleTimer ;set air bubble timer 6415 MoveBubl: ldy $07 ;get pseudorandom bit again, use as offset 6416 lda Bubble_YMF_Dummy,x 6417 sec ;subtract pseudorandom amount from dummy variable 6418 sbc Bubble_MForceData,y 6419 sta Bubble_YMF_Dummy,x ;save dummy variable 6420 lda Bubble_Y_Position,x 6421 sbc #$00 ;subtract borrow from airbubble's vertical coordinate 6422 cmp #$20 ;if below the status bar, 6423 bcs Y_Bubl ;branch to go ahead and use to move air bubble upwards 6424 lda #$f8 ;otherwise set offscreen coordinate 6425 Y_Bubl: sta Bubble_Y_Position,x ;store as new vertical coordinate for air bubble 6426 ExitBubl: rts ;leave 6427 6428 Bubble_MForceData: 6429 .db $ff, $50 6430 6431 BubbleTimerData: 6432 .db $40, $20 6433 6434 ;------------------------------------------------------------------------------------- 6435 6436 RunGameTimer: 6437 lda OperMode ;get primary mode of operation 6438 beq ExGTimer ;branch to leave if in title screen mode 6439 lda GameEngineSubroutine 6440 cmp #$08 ;if routine number less than eight running, 6441 bcc ExGTimer ;branch to leave 6442 cmp #$0b ;if running death routine, 6443 beq ExGTimer ;branch to leave 6444 lda Player_Y_HighPos 6445 cmp #$02 ;if player below the screen, 6446 bcs ExGTimer ;branch to leave regardless of level type 6447 lda GameTimerCtrlTimer ;if game timer control not yet expired, 6448 bne ExGTimer ;branch to leave 6449 lda GameTimerDisplay 6450 ora GameTimerDisplay+1 ;otherwise check game timer digits 6451 ora GameTimerDisplay+2 6452 beq TimeUpOn ;if game timer digits at 000, branch to time-up code 6453 ldy GameTimerDisplay ;otherwise check first digit 6454 dey ;if first digit not on 1, 6455 bne ResGTCtrl ;branch to reset game timer control 6456 lda GameTimerDisplay+1 ;otherwise check second and third digits 6457 ora GameTimerDisplay+2 6458 bne ResGTCtrl ;if timer not at 100, branch to reset game timer control 6459 lda #TimeRunningOutMusic 6460 sta EventMusicQueue ;otherwise load time running out music 6461 ResGTCtrl: lda #$18 ;reset game timer control 6462 sta GameTimerCtrlTimer 6463 ldy #$23 ;set offset for last digit 6464 lda #$ff ;set value to decrement game timer digit 6465 sta DigitModifier+5 6466 jsr DigitsMathRoutine ;do sub to decrement game timer slowly 6467 lda #$a4 ;set status nybbles to update game timer display 6468 jmp PrintStatusBarNumbers ;do sub to update the display 6469 TimeUpOn: sta PlayerStatus ;init player status (note A will always be zero here) 6470 jsr ForceInjury ;do sub to kill the player (note player is small here) 6471 inc GameTimerExpiredFlag ;set game timer expiration flag 6472 ExGTimer: rts ;leave 6473 6474 ;------------------------------------------------------------------------------------- 6475 6476 WarpZoneObject: 6477 lda ScrollLock ;check for scroll lock flag 6478 beq ExGTimer ;branch if not set to leave 6479 lda Player_Y_Position ;check to see if player's vertical coordinate has 6480 and Player_Y_HighPos ;same bits set as in vertical high byte (why?) 6481 bne ExGTimer ;if so, branch to leave 6482 sta ScrollLock ;otherwise nullify scroll lock flag 6483 inc WarpZoneControl ;increment warp zone flag to make warp pipes for warp zone 6484 jmp EraseEnemyObject ;kill this object 6485 6486 ;------------------------------------------------------------------------------------- 6487 ;$00 - used in WhirlpoolActivate to store whirlpool length / 2, page location of center of whirlpool 6488 ;and also to store movement force exerted on player 6489 ;$01 - used in ProcessWhirlpools to store page location of right extent of whirlpool 6490 ;and in WhirlpoolActivate to store center of whirlpool 6491 ;$02 - used in ProcessWhirlpools to store right extent of whirlpool and in 6492 ;WhirlpoolActivate to store maximum vertical speed 6493 6494 ProcessWhirlpools: 6495 lda AreaType ;check for water type level 6496 bne ExitWh ;branch to leave if not found 6497 sta Whirlpool_Flag ;otherwise initialize whirlpool flag 6498 lda TimerControl ;if master timer control set, 6499 bne ExitWh ;branch to leave 6500 ldy #$04 ;otherwise start with last whirlpool data 6501 WhLoop: lda Whirlpool_LeftExtent,y ;get left extent of whirlpool 6502 clc 6503 adc Whirlpool_Length,y ;add length of whirlpool 6504 sta $02 ;store result as right extent here 6505 lda Whirlpool_PageLoc,y ;get page location 6506 beq NextWh ;if none or page 0, branch to get next data 6507 adc #$00 ;add carry 6508 sta $01 ;store result as page location of right extent here 6509 lda Player_X_Position ;get player's horizontal position 6510 sec 6511 sbc Whirlpool_LeftExtent,y ;subtract left extent 6512 lda Player_PageLoc ;get player's page location 6513 sbc Whirlpool_PageLoc,y ;subtract borrow 6514 bmi NextWh ;if player too far left, branch to get next data 6515 lda $02 ;otherwise get right extent 6516 sec 6517 sbc Player_X_Position ;subtract player's horizontal coordinate 6518 lda $01 ;get right extent's page location 6519 sbc Player_PageLoc ;subtract borrow 6520 bpl WhirlpoolActivate ;if player within right extent, branch to whirlpool code 6521 NextWh: dey ;move onto next whirlpool data 6522 bpl WhLoop ;do this until all whirlpools are checked 6523 ExitWh: rts ;leave 6524 6525 WhirlpoolActivate: 6526 lda Whirlpool_Length,y ;get length of whirlpool 6527 lsr ;divide by 2 6528 sta $00 ;save here 6529 lda Whirlpool_LeftExtent,y ;get left extent of whirlpool 6530 clc 6531 adc $00 ;add length divided by 2 6532 sta $01 ;save as center of whirlpool 6533 lda Whirlpool_PageLoc,y ;get page location 6534 adc #$00 ;add carry 6535 sta $00 ;save as page location of whirlpool center 6536 lda FrameCounter ;get frame counter 6537 lsr ;shift d0 into carry (to run on every other frame) 6538 bcc WhPull ;if d0 not set, branch to last part of code 6539 lda $01 ;get center 6540 sec 6541 sbc Player_X_Position ;subtract player's horizontal coordinate 6542 lda $00 ;get page location of center 6543 sbc Player_PageLoc ;subtract borrow 6544 bpl LeftWh ;if player to the left of center, branch 6545 lda Player_X_Position ;otherwise slowly pull player left, towards the center 6546 sec 6547 sbc #$01 ;subtract one pixel 6548 sta Player_X_Position ;set player's new horizontal coordinate 6549 lda Player_PageLoc 6550 sbc #$00 ;subtract borrow 6551 jmp SetPWh ;jump to set player's new page location 6552 LeftWh: lda Player_CollisionBits ;get player's collision bits 6553 lsr ;shift d0 into carry 6554 bcc WhPull ;if d0 not set, branch 6555 lda Player_X_Position ;otherwise slowly pull player right, towards the center 6556 clc 6557 adc #$01 ;add one pixel 6558 sta Player_X_Position ;set player's new horizontal coordinate 6559 lda Player_PageLoc 6560 adc #$00 ;add carry 6561 SetPWh: sta Player_PageLoc ;set player's new page location 6562 WhPull: lda #$10 6563 sta $00 ;set vertical movement force 6564 lda #$01 6565 sta Whirlpool_Flag ;set whirlpool flag to be used later 6566 sta $02 ;also set maximum vertical speed 6567 lsr 6568 tax ;set X for player offset 6569 jmp ImposeGravity ;jump to put whirlpool effect on player vertically, do not return 6570 6571 ;------------------------------------------------------------------------------------- 6572 6573 FlagpoleScoreMods: 6574 .db $05, $02, $08, $04, $01 6575 6576 FlagpoleScoreDigits: 6577 .db $03, $03, $04, $04, $04 6578 6579 FlagpoleRoutine: 6580 ldx #$05 ;set enemy object offset 6581 stx ObjectOffset ;to special use slot 6582 lda Enemy_ID,x 6583 cmp #FlagpoleFlagObject ;if flagpole flag not found, 6584 bne ExitFlagP ;branch to leave 6585 lda GameEngineSubroutine 6586 cmp #$04 ;if flagpole slide routine not running, 6587 bne SkipScore ;branch to near the end of code 6588 lda Player_State 6589 cmp #$03 ;if player state not climbing, 6590 bne SkipScore ;branch to near the end of code 6591 lda Enemy_Y_Position,x ;check flagpole flag's vertical coordinate 6592 cmp #$aa ;if flagpole flag down to a certain point, 6593 bcs GiveFPScr ;branch to end the level 6594 lda Player_Y_Position ;check player's vertical coordinate 6595 cmp #$a2 ;if player down to a certain point, 6596 bcs GiveFPScr ;branch to end the level 6597 lda Enemy_YMF_Dummy,x 6598 adc #$ff ;add movement amount to dummy variable 6599 sta Enemy_YMF_Dummy,x ;save dummy variable 6600 lda Enemy_Y_Position,x ;get flag's vertical coordinate 6601 adc #$01 ;add 1 plus carry to move flag, and 6602 sta Enemy_Y_Position,x ;store vertical coordinate 6603 lda FlagpoleFNum_YMFDummy 6604 sec ;subtract movement amount from dummy variable 6605 sbc #$ff 6606 sta FlagpoleFNum_YMFDummy ;save dummy variable 6607 lda FlagpoleFNum_Y_Pos 6608 sbc #$01 ;subtract one plus borrow to move floatey number, 6609 sta FlagpoleFNum_Y_Pos ;and store vertical coordinate here 6610 SkipScore: jmp FPGfx ;jump to skip ahead and draw flag and floatey number 6611 GiveFPScr: ldy FlagpoleScore ;get score offset from earlier (when player touched flagpole) 6612 lda FlagpoleScoreMods,y ;get amount to award player points 6613 ldx FlagpoleScoreDigits,y ;get digit with which to award points 6614 sta DigitModifier,x ;store in digit modifier 6615 jsr AddToScore ;do sub to award player points depending on height of collision 6616 lda #$05 6617 sta GameEngineSubroutine ;set to run end-of-level subroutine on next frame 6618 FPGfx: jsr GetEnemyOffscreenBits ;get offscreen information 6619 jsr RelativeEnemyPosition ;get relative coordinates 6620 jsr FlagpoleGfxHandler ;draw flagpole flag and floatey number 6621 ExitFlagP: rts 6622 6623 ;------------------------------------------------------------------------------------- 6624 6625 Jumpspring_Y_PosData: 6626 .db $08, $10, $08, $00 6627 6628 JumpspringHandler: 6629 jsr GetEnemyOffscreenBits ;get offscreen information 6630 lda TimerControl ;check master timer control 6631 bne DrawJSpr ;branch to last section if set 6632 lda JumpspringAnimCtrl ;check jumpspring frame control 6633 beq DrawJSpr ;branch to last section if not set 6634 tay 6635 dey ;subtract one from frame control, 6636 tya ;the only way a poor nmos 6502 can 6637 and #%00000010 ;mask out all but d1, original value still in Y 6638 bne DownJSpr ;if set, branch to move player up 6639 inc Player_Y_Position 6640 inc Player_Y_Position ;move player's vertical position down two pixels 6641 jmp PosJSpr ;skip to next part 6642 DownJSpr: dec Player_Y_Position ;move player's vertical position up two pixels 6643 dec Player_Y_Position 6644 PosJSpr: lda Jumpspring_FixedYPos,x ;get permanent vertical position 6645 clc 6646 adc Jumpspring_Y_PosData,y ;add value using frame control as offset 6647 sta Enemy_Y_Position,x ;store as new vertical position 6648 cpy #$01 ;check frame control offset (second frame is $00) 6649 bcc BounceJS ;if offset not yet at third frame ($01), skip to next part 6650 lda A_B_Buttons 6651 and #A_Button ;check saved controller bits for A button press 6652 beq BounceJS ;skip to next part if A not pressed 6653 and PreviousA_B_Buttons ;check for A button pressed in previous frame 6654 bne BounceJS ;skip to next part if so 6655 lda #$f4 6656 sta JumpspringForce ;otherwise write new jumpspring force here 6657 BounceJS: cpy #$03 ;check frame control offset again 6658 bne DrawJSpr ;skip to last part if not yet at fifth frame ($03) 6659 lda JumpspringForce 6660 sta Player_Y_Speed ;store jumpspring force as player's new vertical speed 6661 lda #$00 6662 sta JumpspringAnimCtrl ;initialize jumpspring frame control 6663 DrawJSpr: jsr RelativeEnemyPosition ;get jumpspring's relative coordinates 6664 jsr EnemyGfxHandler ;draw jumpspring 6665 jsr OffscreenBoundsCheck ;check to see if we need to kill it 6666 lda JumpspringAnimCtrl ;if frame control at zero, don't bother 6667 beq ExJSpring ;trying to animate it, just leave 6668 lda JumpspringTimer 6669 bne ExJSpring ;if jumpspring timer not expired yet, leave 6670 lda #$04 6671 sta JumpspringTimer ;otherwise initialize jumpspring timer 6672 inc JumpspringAnimCtrl ;increment frame control to animate jumpspring 6673 ExJSpring: rts ;leave 6674 6675 ;------------------------------------------------------------------------------------- 6676 6677 Setup_Vine: 6678 lda #VineObject ;load identifier for vine object 6679 sta Enemy_ID,x ;store in buffer 6680 lda #$01 6681 sta Enemy_Flag,x ;set flag for enemy object buffer 6682 lda Block_PageLoc,y 6683 sta Enemy_PageLoc,x ;copy page location from previous object 6684 lda Block_X_Position,y 6685 sta Enemy_X_Position,x ;copy horizontal coordinate from previous object 6686 lda Block_Y_Position,y 6687 sta Enemy_Y_Position,x ;copy vertical coordinate from previous object 6688 ldy VineFlagOffset ;load vine flag/offset to next available vine slot 6689 bne NextVO ;if set at all, don't bother to store vertical 6690 sta VineStart_Y_Position ;otherwise store vertical coordinate here 6691 NextVO: txa ;store object offset to next available vine slot 6692 sta VineObjOffset,y ;using vine flag as offset 6693 inc VineFlagOffset ;increment vine flag offset 6694 lda #Sfx_GrowVine 6695 sta Square2SoundQueue ;load vine grow sound 6696 rts 6697 6698 ;------------------------------------------------------------------------------------- 6699 ;$06-$07 - used as address to block buffer data 6700 ;$02 - used as vertical high nybble of block buffer offset 6701 6702 VineHeightData: 6703 .db $30, $60 6704 6705 VineObjectHandler: 6706 cpx #$05 ;check enemy offset for special use slot 6707 bne ExitVH ;if not in last slot, branch to leave 6708 ldy VineFlagOffset 6709 dey ;decrement vine flag in Y, use as offset 6710 lda VineHeight 6711 cmp VineHeightData,y ;if vine has reached certain height, 6712 beq RunVSubs ;branch ahead to skip this part 6713 lda FrameCounter ;get frame counter 6714 lsr ;shift d1 into carry 6715 lsr 6716 bcc RunVSubs ;if d1 not set (2 frames every 4) skip this part 6717 lda Enemy_Y_Position+5 6718 sbc #$01 ;subtract vertical position of vine 6719 sta Enemy_Y_Position+5 ;one pixel every frame it's time 6720 inc VineHeight ;increment vine height 6721 RunVSubs: lda VineHeight ;if vine still very small, 6722 cmp #$08 ;branch to leave 6723 bcc ExitVH 6724 jsr RelativeEnemyPosition ;get relative coordinates of vine, 6725 jsr GetEnemyOffscreenBits ;and any offscreen bits 6726 ldy #$00 ;initialize offset used in draw vine sub 6727 VDrawLoop: jsr DrawVine ;draw vine 6728 iny ;increment offset 6729 cpy VineFlagOffset ;if offset in Y and offset here 6730 bne VDrawLoop ;do not yet match, loop back to draw more vine 6731 lda Enemy_OffscreenBits 6732 and #%00001100 ;mask offscreen bits 6733 beq WrCMTile ;if none of the saved offscreen bits set, skip ahead 6734 dey ;otherwise decrement Y to get proper offset again 6735 KillVine: ldx VineObjOffset,y ;get enemy object offset for this vine object 6736 jsr EraseEnemyObject ;kill this vine object 6737 dey ;decrement Y 6738 bpl KillVine ;if any vine objects left, loop back to kill it 6739 sta VineFlagOffset ;initialize vine flag/offset 6740 sta VineHeight ;initialize vine height 6741 WrCMTile: lda VineHeight ;check vine height 6742 cmp #$20 ;if vine small (less than 32 pixels tall) 6743 bcc ExitVH ;then branch ahead to leave 6744 ldx #$06 ;set offset in X to last enemy slot 6745 lda #$01 ;set A to obtain horizontal in $04, but we don't care 6746 ldy #$1b ;set Y to offset to get block at ($04, $10) of coordinates 6747 jsr BlockBufferCollision ;do a sub to get block buffer address set, return contents 6748 ldy $02 6749 cpy #$d0 ;if vertical high nybble offset beyond extent of 6750 bcs ExitVH ;current block buffer, branch to leave, do not write 6751 lda ($06),y ;otherwise check contents of block buffer at 6752 bne ExitVH ;current offset, if not empty, branch to leave 6753 lda #$26 6754 sta ($06),y ;otherwise, write climbing metatile to block buffer 6755 ExitVH: ldx ObjectOffset ;get enemy object offset and leave 6756 rts 6757 6758 ;------------------------------------------------------------------------------------- 6759 6760 CannonBitmasks: 6761 .db %00001111, %00000111 6762 6763 ProcessCannons: 6764 lda AreaType ;get area type 6765 beq ExCannon ;if water type area, branch to leave 6766 ldx #$02 6767 ThreeSChk: stx ObjectOffset ;start at third enemy slot 6768 lda Enemy_Flag,x ;check enemy buffer flag 6769 bne Chk_BB ;if set, branch to check enemy 6770 lda PseudoRandomBitReg+1,x ;otherwise get part of LSFR 6771 ldy SecondaryHardMode ;get secondary hard mode flag, use as offset 6772 and CannonBitmasks,y ;mask out bits of LSFR as decided by flag 6773 cmp #$06 ;check to see if lower nybble is above certain value 6774 bcs Chk_BB ;if so, branch to check enemy 6775 tay ;transfer masked contents of LSFR to Y as pseudorandom offset 6776 lda Cannon_PageLoc,y ;get page location 6777 beq Chk_BB ;if not set or on page 0, branch to check enemy 6778 lda Cannon_Timer,y ;get cannon timer 6779 beq FireCannon ;if expired, branch to fire cannon 6780 sbc #$00 ;otherwise subtract borrow (note carry will always be clear here) 6781 sta Cannon_Timer,y ;to count timer down 6782 jmp Chk_BB ;then jump ahead to check enemy 6783 6784 FireCannon: 6785 lda TimerControl ;if master timer control set, 6786 bne Chk_BB ;branch to check enemy 6787 lda #$0e ;otherwise we start creating one 6788 sta Cannon_Timer,y ;first, reset cannon timer 6789 lda Cannon_PageLoc,y ;get page location of cannon 6790 sta Enemy_PageLoc,x ;save as page location of bullet bill 6791 lda Cannon_X_Position,y ;get horizontal coordinate of cannon 6792 sta Enemy_X_Position,x ;save as horizontal coordinate of bullet bill 6793 lda Cannon_Y_Position,y ;get vertical coordinate of cannon 6794 sec 6795 sbc #$08 ;subtract eight pixels (because enemies are 24 pixels tall) 6796 sta Enemy_Y_Position,x ;save as vertical coordinate of bullet bill 6797 lda #$01 6798 sta Enemy_Y_HighPos,x ;set vertical high byte of bullet bill 6799 sta Enemy_Flag,x ;set buffer flag 6800 lsr ;shift right once to init A 6801 sta Enemy_State,x ;then initialize enemy's state 6802 lda #$09 6803 sta Enemy_BoundBoxCtrl,x ;set bounding box size control for bullet bill 6804 lda #BulletBill_CannonVar 6805 sta Enemy_ID,x ;load identifier for bullet bill (cannon variant) 6806 jmp Next3Slt ;move onto next slot 6807 Chk_BB: lda Enemy_ID,x ;check enemy identifier for bullet bill (cannon variant) 6808 cmp #BulletBill_CannonVar 6809 bne Next3Slt ;if not found, branch to get next slot 6810 jsr OffscreenBoundsCheck ;otherwise, check to see if it went offscreen 6811 lda Enemy_Flag,x ;check enemy buffer flag 6812 beq Next3Slt ;if not set, branch to get next slot 6813 jsr GetEnemyOffscreenBits ;otherwise, get offscreen information 6814 jsr BulletBillHandler ;then do sub to handle bullet bill 6815 Next3Slt: dex ;move onto next slot 6816 bpl ThreeSChk ;do this until first three slots are checked 6817 ExCannon: rts ;then leave 6818 6819 ;-------------------------------- 6820 6821 BulletBillXSpdData: 6822 .db $18, $e8 6823 6824 BulletBillHandler: 6825 lda TimerControl ;if master timer control set, 6826 bne RunBBSubs ;branch to run subroutines except movement sub 6827 lda Enemy_State,x 6828 bne ChkDSte ;if bullet bill's state set, branch to check defeated state 6829 lda Enemy_OffscreenBits ;otherwise load offscreen bits 6830 and #%00001100 ;mask out bits 6831 cmp #%00001100 ;check to see if all bits are set 6832 beq KillBB ;if so, branch to kill this object 6833 ldy #$01 ;set to move right by default 6834 jsr PlayerEnemyDiff ;get horizontal difference between player and bullet bill 6835 bmi SetupBB ;if enemy to the left of player, branch 6836 iny ;otherwise increment to move left 6837 SetupBB: sty Enemy_MovingDir,x ;set bullet bill's moving direction 6838 dey ;decrement to use as offset 6839 lda BulletBillXSpdData,y ;get horizontal speed based on moving direction 6840 sta Enemy_X_Speed,x ;and store it 6841 lda $00 ;get horizontal difference 6842 adc #$28 ;add 40 pixels 6843 cmp #$50 ;if less than a certain amount, player is too close 6844 bcc KillBB ;to cannon either on left or right side, thus branch 6845 lda #$01 6846 sta Enemy_State,x ;otherwise set bullet bill's state 6847 lda #$0a 6848 sta EnemyFrameTimer,x ;set enemy frame timer 6849 lda #Sfx_Blast 6850 sta Square2SoundQueue ;play fireworks/gunfire sound 6851 ChkDSte: lda Enemy_State,x ;check enemy state for d5 set 6852 and #%00100000 6853 beq BBFly ;if not set, skip to move horizontally 6854 jsr MoveD_EnemyVertically ;otherwise do sub to move bullet bill vertically 6855 BBFly: jsr MoveEnemyHorizontally ;do sub to move bullet bill horizontally 6856 RunBBSubs: jsr GetEnemyOffscreenBits ;get offscreen information 6857 jsr RelativeEnemyPosition ;get relative coordinates 6858 jsr GetEnemyBoundBox ;get bounding box coordinates 6859 jsr PlayerEnemyCollision ;handle player to enemy collisions 6860 jmp EnemyGfxHandler ;draw the bullet bill and leave 6861 KillBB: jsr EraseEnemyObject ;kill bullet bill and leave 6862 rts 6863 6864 ;------------------------------------------------------------------------------------- 6865 6866 HammerEnemyOfsData: 6867 .db $04, $04, $04, $05, $05, $05 6868 .db $06, $06, $06 6869 6870 HammerXSpdData: 6871 .db $10, $f0 6872 6873 SpawnHammerObj: 6874 lda PseudoRandomBitReg+1 ;get pseudorandom bits from 6875 and #%00000111 ;second part of LSFR 6876 bne SetMOfs ;if any bits are set, branch and use as offset 6877 lda PseudoRandomBitReg+1 6878 and #%00001000 ;get d3 from same part of LSFR 6879 SetMOfs: tay ;use either d3 or d2-d0 for offset here 6880 lda Misc_State,y ;if any values loaded in 6881 bne NoHammer ;$2a-$32 where offset is then leave with carry clear 6882 ldx HammerEnemyOfsData,y ;get offset of enemy slot to check using Y as offset 6883 lda Enemy_Flag,x ;check enemy buffer flag at offset 6884 bne NoHammer ;if buffer flag set, branch to leave with carry clear 6885 ldx ObjectOffset ;get original enemy object offset 6886 txa 6887 sta HammerEnemyOffset,y ;save here 6888 lda #$90 6889 sta Misc_State,y ;save hammer's state here 6890 lda #$07 6891 sta Misc_BoundBoxCtrl,y ;set something else entirely, here 6892 sec ;return with carry set 6893 rts 6894 NoHammer: ldx ObjectOffset ;get original enemy object offset 6895 clc ;return with carry clear 6896 rts 6897 6898 ;-------------------------------- 6899 ;$00 - used to set downward force 6900 ;$01 - used to set upward force (residual) 6901 ;$02 - used to set maximum speed 6902 6903 ProcHammerObj: 6904 lda TimerControl ;if master timer control set 6905 bne RunHSubs ;skip all of this code and go to last subs at the end 6906 lda Misc_State,x ;otherwise get hammer's state 6907 and #%01111111 ;mask out d7 6908 ldy HammerEnemyOffset,x ;get enemy object offset that spawned this hammer 6909 cmp #$02 ;check hammer's state 6910 beq SetHSpd ;if currently at 2, branch 6911 bcs SetHPos ;if greater than 2, branch elsewhere 6912 txa 6913 clc ;add 13 bytes to use 6914 adc #$0d ;proper misc object 6915 tax ;return offset to X 6916 lda #$10 6917 sta $00 ;set downward movement force 6918 lda #$0f 6919 sta $01 ;set upward movement force (not used) 6920 lda #$04 6921 sta $02 ;set maximum vertical speed 6922 lda #$00 ;set A to impose gravity on hammer 6923 jsr ImposeGravity ;do sub to impose gravity on hammer and move vertically 6924 jsr MoveObjectHorizontally ;do sub to move it horizontally 6925 ldx ObjectOffset ;get original misc object offset 6926 jmp RunAllH ;branch to essential subroutines 6927 SetHSpd: lda #$fe 6928 sta Misc_Y_Speed,x ;set hammer's vertical speed 6929 lda Enemy_State,y ;get enemy object state 6930 and #%11110111 ;mask out d3 6931 sta Enemy_State,y ;store new state 6932 ldx Enemy_MovingDir,y ;get enemy's moving direction 6933 dex ;decrement to use as offset 6934 lda HammerXSpdData,x ;get proper speed to use based on moving direction 6935 ldx ObjectOffset ;reobtain hammer's buffer offset 6936 sta Misc_X_Speed,x ;set hammer's horizontal speed 6937 SetHPos: dec Misc_State,x ;decrement hammer's state 6938 lda Enemy_X_Position,y ;get enemy's horizontal position 6939 clc 6940 adc #$02 ;set position 2 pixels to the right 6941 sta Misc_X_Position,x ;store as hammer's horizontal position 6942 lda Enemy_PageLoc,y ;get enemy's page location 6943 adc #$00 ;add carry 6944 sta Misc_PageLoc,x ;store as hammer's page location 6945 lda Enemy_Y_Position,y ;get enemy's vertical position 6946 sec 6947 sbc #$0a ;move position 10 pixels upward 6948 sta Misc_Y_Position,x ;store as hammer's vertical position 6949 lda #$01 6950 sta Misc_Y_HighPos,x ;set hammer's vertical high byte 6951 bne RunHSubs ;unconditional branch to skip first routine 6952 RunAllH: jsr PlayerHammerCollision ;handle collisions 6953 RunHSubs: jsr GetMiscOffscreenBits ;get offscreen information 6954 jsr RelativeMiscPosition ;get relative coordinates 6955 jsr GetMiscBoundBox ;get bounding box coordinates 6956 jsr DrawHammer ;draw the hammer 6957 rts ;and we are done here 6958 6959 ;------------------------------------------------------------------------------------- 6960 ;$02 - used to store vertical high nybble offset from block buffer routine 6961 ;$06 - used to store low byte of block buffer address 6962 6963 CoinBlock: 6964 jsr FindEmptyMiscSlot ;set offset for empty or last misc object buffer slot 6965 lda Block_PageLoc,x ;get page location of block object 6966 sta Misc_PageLoc,y ;store as page location of misc object 6967 lda Block_X_Position,x ;get horizontal coordinate of block object 6968 ora #$05 ;add 5 pixels 6969 sta Misc_X_Position,y ;store as horizontal coordinate of misc object 6970 lda Block_Y_Position,x ;get vertical coordinate of block object 6971 sbc #$10 ;subtract 16 pixels 6972 sta Misc_Y_Position,y ;store as vertical coordinate of misc object 6973 jmp JCoinC ;jump to rest of code as applies to this misc object 6974 6975 SetupJumpCoin: 6976 jsr FindEmptyMiscSlot ;set offset for empty or last misc object buffer slot 6977 lda Block_PageLoc2,x ;get page location saved earlier 6978 sta Misc_PageLoc,y ;and save as page location for misc object 6979 lda $06 ;get low byte of block buffer offset 6980 asl 6981 asl ;multiply by 16 to use lower nybble 6982 asl 6983 asl 6984 ora #$05 ;add five pixels 6985 sta Misc_X_Position,y ;save as horizontal coordinate for misc object 6986 lda $02 ;get vertical high nybble offset from earlier 6987 adc #$20 ;add 32 pixels for the status bar 6988 sta Misc_Y_Position,y ;store as vertical coordinate 6989 JCoinC: lda #$fb 6990 sta Misc_Y_Speed,y ;set vertical speed 6991 lda #$01 6992 sta Misc_Y_HighPos,y ;set vertical high byte 6993 sta Misc_State,y ;set state for misc object 6994 sta Square2SoundQueue ;load coin grab sound 6995 stx ObjectOffset ;store current control bit as misc object offset 6996 jsr GiveOneCoin ;update coin tally on the screen and coin amount variable 6997 inc CoinTallyFor1Ups ;increment coin tally used to activate 1-up block flag 6998 rts 6999 7000 FindEmptyMiscSlot: 7001 ldy #$08 ;start at end of misc objects buffer 7002 FMiscLoop: lda Misc_State,y ;get misc object state 7003 beq UseMiscS ;branch if none found to use current offset 7004 dey ;decrement offset 7005 cpy #$05 ;do this for three slots 7006 bne FMiscLoop ;do this until all slots are checked 7007 ldy #$08 ;if no empty slots found, use last slot 7008 UseMiscS: sty JumpCoinMiscOffset ;store offset of misc object buffer here (residual) 7009 rts 7010 7011 ;------------------------------------------------------------------------------------- 7012 7013 MiscObjectsCore: 7014 ldx #$08 ;set at end of misc object buffer 7015 MiscLoop: stx ObjectOffset ;store misc object offset here 7016 lda Misc_State,x ;check misc object state 7017 beq MiscLoopBack ;branch to check next slot 7018 asl ;otherwise shift d7 into carry 7019 bcc ProcJumpCoin ;if d7 not set, jumping coin, thus skip to rest of code here 7020 jsr ProcHammerObj ;otherwise go to process hammer, 7021 jmp MiscLoopBack ;then check next slot 7022 7023 ;-------------------------------- 7024 ;$00 - used to set downward force 7025 ;$01 - used to set upward force (residual) 7026 ;$02 - used to set maximum speed 7027 7028 ProcJumpCoin: 7029 ldy Misc_State,x ;check misc object state 7030 dey ;decrement to see if it's set to 1 7031 beq JCoinRun ;if so, branch to handle jumping coin 7032 inc Misc_State,x ;otherwise increment state to either start off or as timer 7033 lda Misc_X_Position,x ;get horizontal coordinate for misc object 7034 clc ;whether its jumping coin (state 0 only) or floatey number 7035 adc ScrollAmount ;add current scroll speed 7036 sta Misc_X_Position,x ;store as new horizontal coordinate 7037 lda Misc_PageLoc,x ;get page location 7038 adc #$00 ;add carry 7039 sta Misc_PageLoc,x ;store as new page location 7040 lda Misc_State,x 7041 cmp #$30 ;check state of object for preset value 7042 bne RunJCSubs ;if not yet reached, branch to subroutines 7043 lda #$00 7044 sta Misc_State,x ;otherwise nullify object state 7045 jmp MiscLoopBack ;and move onto next slot 7046 JCoinRun: txa 7047 clc ;add 13 bytes to offset for next subroutine 7048 adc #$0d 7049 tax 7050 lda #$50 ;set downward movement amount 7051 sta $00 7052 lda #$06 ;set maximum vertical speed 7053 sta $02 7054 lsr ;divide by 2 and set 7055 sta $01 ;as upward movement amount (apparently residual) 7056 lda #$00 ;set A to impose gravity on jumping coin 7057 jsr ImposeGravity ;do sub to move coin vertically and impose gravity on it 7058 ldx ObjectOffset ;get original misc object offset 7059 lda Misc_Y_Speed,x ;check vertical speed 7060 cmp #$05 7061 bne RunJCSubs ;if not moving downward fast enough, keep state as-is 7062 inc Misc_State,x ;otherwise increment state to change to floatey number 7063 RunJCSubs: jsr RelativeMiscPosition ;get relative coordinates 7064 jsr GetMiscOffscreenBits ;get offscreen information 7065 jsr GetMiscBoundBox ;get bounding box coordinates (why?) 7066 jsr JCoinGfxHandler ;draw the coin or floatey number 7067 7068 MiscLoopBack: 7069 dex ;decrement misc object offset 7070 bpl MiscLoop ;loop back until all misc objects handled 7071 rts ;then leave 7072 7073 ;------------------------------------------------------------------------------------- 7074 7075 CoinTallyOffsets: 7076 .db $17, $1d 7077 7078 ScoreOffsets: 7079 .db $0b, $11 7080 7081 StatusBarNybbles: 7082 .db $02, $13 7083 7084 GiveOneCoin: 7085 lda #$01 ;set digit modifier to add 1 coin 7086 sta DigitModifier+5 ;to the current player's coin tally 7087 ldx CurrentPlayer ;get current player on the screen 7088 ldy CoinTallyOffsets,x ;get offset for player's coin tally 7089 jsr DigitsMathRoutine ;update the coin tally 7090 inc CoinTally ;increment onscreen player's coin amount 7091 lda CoinTally 7092 cmp #100 ;does player have 100 coins yet? 7093 bne CoinPoints ;if not, skip all of this 7094 lda #$00 7095 sta CoinTally ;otherwise, reinitialize coin amount 7096 inc NumberofLives ;give the player an extra life 7097 lda #Sfx_ExtraLife 7098 sta Square2SoundQueue ;play 1-up sound 7099 7100 CoinPoints: 7101 lda #$02 ;set digit modifier to award 7102 sta DigitModifier+4 ;200 points to the player 7103 7104 AddToScore: 7105 ldx CurrentPlayer ;get current player 7106 ldy ScoreOffsets,x ;get offset for player's score 7107 jsr DigitsMathRoutine ;update the score internally with value in digit modifier 7108 7109 GetSBNybbles: 7110 ldy CurrentPlayer ;get current player 7111 lda StatusBarNybbles,y ;get nybbles based on player, use to update score and coins 7112 7113 UpdateNumber: 7114 jsr PrintStatusBarNumbers ;print status bar numbers based on nybbles, whatever they be 7115 ldy VRAM_Buffer1_Offset 7116 lda VRAM_Buffer1-6,y ;check highest digit of score 7117 bne NoZSup ;if zero, overwrite with space tile for zero suppression 7118 lda #$24 7119 sta VRAM_Buffer1-6,y 7120 NoZSup: ldx ObjectOffset ;get enemy object buffer offset 7121 rts 7122 7123 ;------------------------------------------------------------------------------------- 7124 7125 SetupPowerUp: 7126 lda #PowerUpObject ;load power-up identifier into 7127 sta Enemy_ID+5 ;special use slot of enemy object buffer 7128 lda Block_PageLoc,x ;store page location of block object 7129 sta Enemy_PageLoc+5 ;as page location of power-up object 7130 lda Block_X_Position,x ;store horizontal coordinate of block object 7131 sta Enemy_X_Position+5 ;as horizontal coordinate of power-up object 7132 lda #$01 7133 sta Enemy_Y_HighPos+5 ;set vertical high byte of power-up object 7134 lda Block_Y_Position,x ;get vertical coordinate of block object 7135 sec 7136 sbc #$08 ;subtract 8 pixels 7137 sta Enemy_Y_Position+5 ;and use as vertical coordinate of power-up object 7138 PwrUpJmp: lda #$01 ;this is a residual jump point in enemy object jump table 7139 sta Enemy_State+5 ;set power-up object's state 7140 sta Enemy_Flag+5 ;set buffer flag 7141 lda #$03 7142 sta Enemy_BoundBoxCtrl+5 ;set bounding box size control for power-up object 7143 lda PowerUpType 7144 cmp #$02 ;check currently loaded power-up type 7145 bcs PutBehind ;if star or 1-up, branch ahead 7146 lda PlayerStatus ;otherwise check player's current status 7147 cmp #$02 7148 bcc StrType ;if player not fiery, use status as power-up type 7149 lsr ;otherwise shift right to force fire flower type 7150 StrType: sta PowerUpType ;store type here 7151 PutBehind: lda #%00100000 7152 sta Enemy_SprAttrib+5 ;set background priority bit 7153 lda #Sfx_GrowPowerUp 7154 sta Square2SoundQueue ;load power-up reveal sound and leave 7155 rts 7156 7157 ;------------------------------------------------------------------------------------- 7158 7159 PowerUpObjHandler: 7160 ldx #$05 ;set object offset for last slot in enemy object buffer 7161 stx ObjectOffset 7162 lda Enemy_State+5 ;check power-up object's state 7163 beq ExitPUp ;if not set, branch to leave 7164 asl ;shift to check if d7 was set in object state 7165 bcc GrowThePowerUp ;if not set, branch ahead to skip this part 7166 lda TimerControl ;if master timer control set, 7167 bne RunPUSubs ;branch ahead to enemy object routines 7168 lda PowerUpType ;check power-up type 7169 beq ShroomM ;if normal mushroom, branch ahead to move it 7170 cmp #$03 7171 beq ShroomM ;if 1-up mushroom, branch ahead to move it 7172 cmp #$02 7173 bne RunPUSubs ;if not star, branch elsewhere to skip movement 7174 jsr MoveJumpingEnemy ;otherwise impose gravity on star power-up and make it jump 7175 jsr EnemyJump ;note that green paratroopa shares the same code here 7176 jmp RunPUSubs ;then jump to other power-up subroutines 7177 ShroomM: jsr MoveNormalEnemy ;do sub to make mushrooms move 7178 jsr EnemyToBGCollisionDet ;deal with collisions 7179 jmp RunPUSubs ;run the other subroutines 7180 7181 GrowThePowerUp: 7182 lda FrameCounter ;get frame counter 7183 and #$03 ;mask out all but 2 LSB 7184 bne ChkPUSte ;if any bits set here, branch 7185 dec Enemy_Y_Position+5 ;otherwise decrement vertical coordinate slowly 7186 lda Enemy_State+5 ;load power-up object state 7187 inc Enemy_State+5 ;increment state for next frame (to make power-up rise) 7188 cmp #$11 ;if power-up object state not yet past 16th pixel, 7189 bcc ChkPUSte ;branch ahead to last part here 7190 lda #$10 7191 sta Enemy_X_Speed,x ;otherwise set horizontal speed 7192 lda #%10000000 7193 sta Enemy_State+5 ;and then set d7 in power-up object's state 7194 asl ;shift once to init A 7195 sta Enemy_SprAttrib+5 ;initialize background priority bit set here 7196 rol ;rotate A to set right moving direction 7197 sta Enemy_MovingDir,x ;set moving direction 7198 ChkPUSte: lda Enemy_State+5 ;check power-up object's state 7199 cmp #$06 ;for if power-up has risen enough 7200 bcc ExitPUp ;if not, don't even bother running these routines 7201 RunPUSubs: jsr RelativeEnemyPosition ;get coordinates relative to screen 7202 jsr GetEnemyOffscreenBits ;get offscreen bits 7203 jsr GetEnemyBoundBox ;get bounding box coordinates 7204 jsr DrawPowerUp ;draw the power-up object 7205 jsr PlayerEnemyCollision ;check for collision with player 7206 jsr OffscreenBoundsCheck ;check to see if it went offscreen 7207 ExitPUp: rts ;and we're done 7208 7209 ;------------------------------------------------------------------------------------- 7210 ;These apply to all routines in this section unless otherwise noted: 7211 ;$00 - used to store metatile from block buffer routine 7212 ;$02 - used to store vertical high nybble offset from block buffer routine 7213 ;$05 - used to store metatile stored in A at beginning of PlayerHeadCollision 7214 ;$06-$07 - used as block buffer address indirect 7215 7216 BlockYPosAdderData: 7217 .db $04, $12 7218 7219 PlayerHeadCollision: 7220 pha ;store metatile number to stack 7221 lda #$11 ;load unbreakable block object state by default 7222 ldx SprDataOffset_Ctrl ;load offset control bit here 7223 ldy PlayerSize ;check player's size 7224 bne DBlockSte ;if small, branch 7225 lda #$12 ;otherwise load breakable block object state 7226 DBlockSte: sta Block_State,x ;store into block object buffer 7227 jsr DestroyBlockMetatile ;store blank metatile in vram buffer to write to name table 7228 ldx SprDataOffset_Ctrl ;load offset control bit 7229 lda $02 ;get vertical high nybble offset used in block buffer routine 7230 sta Block_Orig_YPos,x ;set as vertical coordinate for block object 7231 tay 7232 lda $06 ;get low byte of block buffer address used in same routine 7233 sta Block_BBuf_Low,x ;save as offset here to be used later 7234 lda ($06),y ;get contents of block buffer at old address at $06, $07 7235 jsr BlockBumpedChk ;do a sub to check which block player bumped head on 7236 sta $00 ;store metatile here 7237 ldy PlayerSize ;check player's size 7238 bne ChkBrick ;if small, use metatile itself as contents of A 7239 tya ;otherwise init A (note: big = 0) 7240 ChkBrick: bcc PutMTileB ;if no match was found in previous sub, skip ahead 7241 ldy #$11 ;otherwise load unbreakable state into block object buffer 7242 sty Block_State,x ;note this applies to both player sizes 7243 lda #$c4 ;load empty block metatile into A for now 7244 ldy $00 ;get metatile from before 7245 cpy #$58 ;is it brick with coins (with line)? 7246 beq StartBTmr ;if so, branch 7247 cpy #$5d ;is it brick with coins (without line)? 7248 bne PutMTileB ;if not, branch ahead to store empty block metatile 7249 StartBTmr: lda BrickCoinTimerFlag ;check brick coin timer flag 7250 bne ContBTmr ;if set, timer expired or counting down, thus branch 7251 lda #$0b 7252 sta BrickCoinTimer ;if not set, set brick coin timer 7253 inc BrickCoinTimerFlag ;and set flag linked to it 7254 ContBTmr: lda BrickCoinTimer ;check brick coin timer 7255 bne PutOldMT ;if not yet expired, branch to use current metatile 7256 ldy #$c4 ;otherwise use empty block metatile 7257 PutOldMT: tya ;put metatile into A 7258 PutMTileB: sta Block_Metatile,x ;store whatever metatile be appropriate here 7259 jsr InitBlock_XY_Pos ;get block object horizontal coordinates saved 7260 ldy $02 ;get vertical high nybble offset 7261 lda #$23 7262 sta ($06),y ;write blank metatile $23 to block buffer 7263 lda #$10 7264 sta BlockBounceTimer ;set block bounce timer 7265 pla ;pull original metatile from stack 7266 sta $05 ;and save here 7267 ldy #$00 ;set default offset 7268 lda CrouchingFlag ;is player crouching? 7269 bne SmallBP ;if so, branch to increment offset 7270 lda PlayerSize ;is player big? 7271 beq BigBP ;if so, branch to use default offset 7272 SmallBP: iny ;increment for small or big and crouching 7273 BigBP: lda Player_Y_Position ;get player's vertical coordinate 7274 clc 7275 adc BlockYPosAdderData,y ;add value determined by size 7276 and #$f0 ;mask out low nybble to get 16-pixel correspondence 7277 sta Block_Y_Position,x ;save as vertical coordinate for block object 7278 ldy Block_State,x ;get block object state 7279 cpy #$11 7280 beq Unbreak ;if set to value loaded for unbreakable, branch 7281 jsr BrickShatter ;execute code for breakable brick 7282 jmp InvOBit ;skip subroutine to do last part of code here 7283 Unbreak: jsr BumpBlock ;execute code for unbreakable brick or question block 7284 InvOBit: lda SprDataOffset_Ctrl ;invert control bit used by block objects 7285 eor #$01 ;and floatey numbers 7286 sta SprDataOffset_Ctrl 7287 rts ;leave! 7288 7289 ;-------------------------------- 7290 7291 InitBlock_XY_Pos: 7292 lda Player_X_Position ;get player's horizontal coordinate 7293 clc 7294 adc #$08 ;add eight pixels 7295 and #$f0 ;mask out low nybble to give 16-pixel correspondence 7296 sta Block_X_Position,x ;save as horizontal coordinate for block object 7297 lda Player_PageLoc 7298 adc #$00 ;add carry to page location of player 7299 sta Block_PageLoc,x ;save as page location of block object 7300 sta Block_PageLoc2,x ;save elsewhere to be used later 7301 lda Player_Y_HighPos 7302 sta Block_Y_HighPos,x ;save vertical high byte of player into 7303 rts ;vertical high byte of block object and leave 7304 7305 ;-------------------------------- 7306 7307 BumpBlock: 7308 jsr CheckTopOfBlock ;check to see if there's a coin directly above this block 7309 lda #Sfx_Bump 7310 sta Square1SoundQueue ;play bump sound 7311 lda #$00 7312 sta Block_X_Speed,x ;initialize horizontal speed for block object 7313 sta Block_Y_MoveForce,x ;init fractional movement force 7314 sta Player_Y_Speed ;init player's vertical speed 7315 lda #$fe 7316 sta Block_Y_Speed,x ;set vertical speed for block object 7317 lda $05 ;get original metatile from stack 7318 jsr BlockBumpedChk ;do a sub to check which block player bumped head on 7319 bcc ExitBlockChk ;if no match was found, branch to leave 7320 tya ;move block number to A 7321 cmp #$09 ;if block number was within 0-8 range, 7322 bcc BlockCode ;branch to use current number 7323 sbc #$05 ;otherwise subtract 5 for second set to get proper number 7324 BlockCode: jsr JumpEngine ;run appropriate subroutine depending on block number 7325 7326 .dw MushFlowerBlock 7327 .dw CoinBlock 7328 .dw CoinBlock 7329 .dw ExtraLifeMushBlock 7330 .dw MushFlowerBlock 7331 .dw VineBlock 7332 .dw StarBlock 7333 .dw CoinBlock 7334 .dw ExtraLifeMushBlock 7335 7336 ;-------------------------------- 7337 7338 MushFlowerBlock: 7339 lda #$00 ;load mushroom/fire flower into power-up type 7340 .db $2c ;BIT instruction opcode 7341 7342 StarBlock: 7343 lda #$02 ;load star into power-up type 7344 .db $2c ;BIT instruction opcode 7345 7346 ExtraLifeMushBlock: 7347 lda #$03 ;load 1-up mushroom into power-up type 7348 sta $39 ;store correct power-up type 7349 jmp SetupPowerUp 7350 7351 VineBlock: 7352 ldx #$05 ;load last slot for enemy object buffer 7353 ldy SprDataOffset_Ctrl ;get control bit 7354 jsr Setup_Vine ;set up vine object 7355 7356 ExitBlockChk: 7357 rts ;leave 7358 7359 ;-------------------------------- 7360 7361 BrickQBlockMetatiles: 7362 .db $c1, $c0, $5f, $60 ;used by question blocks 7363 7364 ;these two sets are functionally identical, but look different 7365 .db $55, $56, $57, $58, $59 ;used by ground level types 7366 .db $5a, $5b, $5c, $5d, $5e ;used by other level types 7367 7368 BlockBumpedChk: 7369 ldy #$0d ;start at end of metatile data 7370 BumpChkLoop: cmp BrickQBlockMetatiles,y ;check to see if current metatile matches 7371 beq MatchBump ;metatile found in block buffer, branch if so 7372 dey ;otherwise move onto next metatile 7373 bpl BumpChkLoop ;do this until all metatiles are checked 7374 clc ;if none match, return with carry clear 7375 MatchBump: rts ;note carry is set if found match 7376 7377 ;-------------------------------- 7378 7379 BrickShatter: 7380 jsr CheckTopOfBlock ;check to see if there's a coin directly above this block 7381 lda #Sfx_BrickShatter 7382 sta Block_RepFlag,x ;set flag for block object to immediately replace metatile 7383 sta NoiseSoundQueue ;load brick shatter sound 7384 jsr SpawnBrickChunks ;create brick chunk objects 7385 lda #$fe 7386 sta Player_Y_Speed ;set vertical speed for player 7387 lda #$05 7388 sta DigitModifier+5 ;set digit modifier to give player 50 points 7389 jsr AddToScore ;do sub to update the score 7390 ldx SprDataOffset_Ctrl ;load control bit and leave 7391 rts 7392 7393 ;-------------------------------- 7394 7395 CheckTopOfBlock: 7396 ldx SprDataOffset_Ctrl ;load control bit 7397 ldy $02 ;get vertical high nybble offset used in block buffer 7398 beq TopEx ;branch to leave if set to zero, because we're at the top 7399 tya ;otherwise set to A 7400 sec 7401 sbc #$10 ;subtract $10 to move up one row in the block buffer 7402 sta $02 ;store as new vertical high nybble offset 7403 tay 7404 lda ($06),y ;get contents of block buffer in same column, one row up 7405 cmp #$c2 ;is it a coin? (not underwater) 7406 bne TopEx ;if not, branch to leave 7407 lda #$00 7408 sta ($06),y ;otherwise put blank metatile where coin was 7409 jsr RemoveCoin_Axe ;write blank metatile to vram buffer 7410 ldx SprDataOffset_Ctrl ;get control bit 7411 jsr SetupJumpCoin ;create jumping coin object and update coin variables 7412 TopEx: rts ;leave! 7413 7414 ;-------------------------------- 7415 7416 SpawnBrickChunks: 7417 lda Block_X_Position,x ;set horizontal coordinate of block object 7418 sta Block_Orig_XPos,x ;as original horizontal coordinate here 7419 lda #$f0 7420 sta Block_X_Speed,x ;set horizontal speed for brick chunk objects 7421 sta Block_X_Speed+2,x 7422 lda #$fa 7423 sta Block_Y_Speed,x ;set vertical speed for one 7424 lda #$fc 7425 sta Block_Y_Speed+2,x ;set lower vertical speed for the other 7426 lda #$00 7427 sta Block_Y_MoveForce,x ;init fractional movement force for both 7428 sta Block_Y_MoveForce+2,x 7429 lda Block_PageLoc,x 7430 sta Block_PageLoc+2,x ;copy page location 7431 lda Block_X_Position,x 7432 sta Block_X_Position+2,x ;copy horizontal coordinate 7433 lda Block_Y_Position,x 7434 clc ;add 8 pixels to vertical coordinate 7435 adc #$08 ;and save as vertical coordinate for one of them 7436 sta Block_Y_Position+2,x 7437 lda #$fa 7438 sta Block_Y_Speed,x ;set vertical speed...again??? (redundant) 7439 rts 7440 7441 ;------------------------------------------------------------------------------------- 7442 7443 BlockObjectsCore: 7444 lda Block_State,x ;get state of block object 7445 beq UpdSte ;if not set, branch to leave 7446 and #$0f ;mask out high nybble 7447 pha ;push to stack 7448 tay ;put in Y for now 7449 txa 7450 clc 7451 adc #$09 ;add 9 bytes to offset (note two block objects are created 7452 tax ;when using brick chunks, but only one offset for both) 7453 dey ;decrement Y to check for solid block state 7454 beq BouncingBlockHandler ;branch if found, otherwise continue for brick chunks 7455 jsr ImposeGravityBlock ;do sub to impose gravity on one block object object 7456 jsr MoveObjectHorizontally ;do another sub to move horizontally 7457 txa 7458 clc ;move onto next block object 7459 adc #$02 7460 tax 7461 jsr ImposeGravityBlock ;do sub to impose gravity on other block object 7462 jsr MoveObjectHorizontally ;do another sub to move horizontally 7463 ldx ObjectOffset ;get block object offset used for both 7464 jsr RelativeBlockPosition ;get relative coordinates 7465 jsr GetBlockOffscreenBits ;get offscreen information 7466 jsr DrawBrickChunks ;draw the brick chunks 7467 pla ;get lower nybble of saved state 7468 ldy Block_Y_HighPos,x ;check vertical high byte of block object 7469 beq UpdSte ;if above the screen, branch to kill it 7470 pha ;otherwise save state back into stack 7471 lda #$f0 7472 cmp Block_Y_Position+2,x ;check to see if bottom block object went 7473 bcs ChkTop ;to the bottom of the screen, and branch if not 7474 sta Block_Y_Position+2,x ;otherwise set offscreen coordinate 7475 ChkTop: lda Block_Y_Position,x ;get top block object's vertical coordinate 7476 cmp #$f0 ;see if it went to the bottom of the screen 7477 pla ;pull block object state from stack 7478 bcc UpdSte ;if not, branch to save state 7479 bcs KillBlock ;otherwise do unconditional branch to kill it 7480 7481 BouncingBlockHandler: 7482 jsr ImposeGravityBlock ;do sub to impose gravity on block object 7483 ldx ObjectOffset ;get block object offset 7484 jsr RelativeBlockPosition ;get relative coordinates 7485 jsr GetBlockOffscreenBits ;get offscreen information 7486 jsr DrawBlock ;draw the block 7487 lda Block_Y_Position,x ;get vertical coordinate 7488 and #$0f ;mask out high nybble 7489 cmp #$05 ;check to see if low nybble wrapped around 7490 pla ;pull state from stack 7491 bcs UpdSte ;if still above amount, not time to kill block yet, thus branch 7492 lda #$01 7493 sta Block_RepFlag,x ;otherwise set flag to replace metatile 7494 KillBlock: lda #$00 ;if branched here, nullify object state 7495 UpdSte: sta Block_State,x ;store contents of A in block object state 7496 rts 7497 7498 ;------------------------------------------------------------------------------------- 7499 ;$02 - used to store offset to block buffer 7500 ;$06-$07 - used to store block buffer address 7501 7502 BlockObjMT_Updater: 7503 ldx #$01 ;set offset to start with second block object 7504 UpdateLoop: stx ObjectOffset ;set offset here 7505 lda VRAM_Buffer1 ;if vram buffer already being used here, 7506 bne NextBUpd ;branch to move onto next block object 7507 lda Block_RepFlag,x ;if flag for block object already clear, 7508 beq NextBUpd ;branch to move onto next block object 7509 lda Block_BBuf_Low,x ;get low byte of block buffer 7510 sta $06 ;store into block buffer address 7511 lda #$05 7512 sta $07 ;set high byte of block buffer address 7513 lda Block_Orig_YPos,x ;get original vertical coordinate of block object 7514 sta $02 ;store here and use as offset to block buffer 7515 tay 7516 lda Block_Metatile,x ;get metatile to be written 7517 sta ($06),y ;write it to the block buffer 7518 jsr ReplaceBlockMetatile ;do sub to replace metatile where block object is 7519 lda #$00 7520 sta Block_RepFlag,x ;clear block object flag 7521 NextBUpd: dex ;decrement block object offset 7522 bpl UpdateLoop ;do this until both block objects are dealt with 7523 rts ;then leave 7524 7525 ;------------------------------------------------------------------------------------- 7526 ;$00 - used to store high nybble of horizontal speed as adder 7527 ;$01 - used to store low nybble of horizontal speed 7528 ;$02 - used to store adder to page location 7529 7530 MoveEnemyHorizontally: 7531 inx ;increment offset for enemy offset 7532 jsr MoveObjectHorizontally ;position object horizontally according to 7533 ldx ObjectOffset ;counters, return with saved value in A, 7534 rts ;put enemy offset back in X and leave 7535 7536 MovePlayerHorizontally: 7537 lda JumpspringAnimCtrl ;if jumpspring currently animating, 7538 bne ExXMove ;branch to leave 7539 tax ;otherwise set zero for offset to use player's stuff 7540 7541 MoveObjectHorizontally: 7542 lda SprObject_X_Speed,x ;get currently saved value (horizontal 7543 asl ;speed, secondary counter, whatever) 7544 asl ;and move low nybble to high 7545 asl 7546 asl 7547 sta $01 ;store result here 7548 lda SprObject_X_Speed,x ;get saved value again 7549 lsr ;move high nybble to low 7550 lsr 7551 lsr 7552 lsr 7553 cmp #$08 ;if < 8, branch, do not change 7554 bcc SaveXSpd 7555 ora #%11110000 ;otherwise alter high nybble 7556 SaveXSpd: sta $00 ;save result here 7557 ldy #$00 ;load default Y value here 7558 cmp #$00 ;if result positive, leave Y alone 7559 bpl UseAdder 7560 dey ;otherwise decrement Y 7561 UseAdder: sty $02 ;save Y here 7562 lda SprObject_X_MoveForce,x ;get whatever number's here 7563 clc 7564 adc $01 ;add low nybble moved to high 7565 sta SprObject_X_MoveForce,x ;store result here 7566 lda #$00 ;init A 7567 rol ;rotate carry into d0 7568 pha ;push onto stack 7569 ror ;rotate d0 back onto carry 7570 lda SprObject_X_Position,x 7571 adc $00 ;add carry plus saved value (high nybble moved to low 7572 sta SprObject_X_Position,x ;plus $f0 if necessary) to object's horizontal position 7573 lda SprObject_PageLoc,x 7574 adc $02 ;add carry plus other saved value to the 7575 sta SprObject_PageLoc,x ;object's page location and save 7576 pla 7577 clc ;pull old carry from stack and add 7578 adc $00 ;to high nybble moved to low 7579 ExXMove: rts ;and leave 7580 7581 ;------------------------------------------------------------------------------------- 7582 ;$00 - used for downward force 7583 ;$01 - used for upward force 7584 ;$02 - used for maximum vertical speed 7585 7586 MovePlayerVertically: 7587 ldx #$00 ;set X for player offset 7588 lda TimerControl 7589 bne NoJSChk ;if master timer control set, branch ahead 7590 lda JumpspringAnimCtrl ;otherwise check to see if jumpspring is animating 7591 bne ExXMove ;branch to leave if so 7592 NoJSChk: lda VerticalForce ;dump vertical force 7593 sta $00 7594 lda #$04 ;set maximum vertical speed here 7595 jmp ImposeGravitySprObj ;then jump to move player vertically 7596 7597 ;-------------------------------- 7598 7599 MoveD_EnemyVertically: 7600 ldy #$3d ;set quick movement amount downwards 7601 lda Enemy_State,x ;then check enemy state 7602 cmp #$05 ;if not set to unique state for spiny's egg, go ahead 7603 bne ContVMove ;and use, otherwise set different movement amount, continue on 7604 7605 MoveFallingPlatform: 7606 ldy #$20 ;set movement amount 7607 ContVMove: jmp SetHiMax ;jump to skip the rest of this 7608 7609 ;-------------------------------- 7610 7611 MoveRedPTroopaDown: 7612 ldy #$00 ;set Y to move downwards 7613 jmp MoveRedPTroopa ;skip to movement routine 7614 7615 MoveRedPTroopaUp: 7616 ldy #$01 ;set Y to move upwards 7617 7618 MoveRedPTroopa: 7619 inx ;increment X for enemy offset 7620 lda #$03 7621 sta $00 ;set downward movement amount here 7622 lda #$06 7623 sta $01 ;set upward movement amount here 7624 lda #$02 7625 sta $02 ;set maximum speed here 7626 tya ;set movement direction in A, and 7627 jmp RedPTroopaGrav ;jump to move this thing 7628 7629 ;-------------------------------- 7630 7631 MoveDropPlatform: 7632 ldy #$7f ;set movement amount for drop platform 7633 bne SetMdMax ;skip ahead of other value set here 7634 7635 MoveEnemySlowVert: 7636 ldy #$0f ;set movement amount for bowser/other objects 7637 SetMdMax: lda #$02 ;set maximum speed in A 7638 bne SetXMoveAmt ;unconditional branch 7639 7640 ;-------------------------------- 7641 7642 MoveJ_EnemyVertically: 7643 ldy #$1c ;set movement amount for podoboo/other objects 7644 SetHiMax: lda #$03 ;set maximum speed in A 7645 SetXMoveAmt: sty $00 ;set movement amount here 7646 inx ;increment X for enemy offset 7647 jsr ImposeGravitySprObj ;do a sub to move enemy object downwards 7648 ldx ObjectOffset ;get enemy object buffer offset and leave 7649 rts 7650 7651 ;-------------------------------- 7652 7653 MaxSpdBlockData: 7654 .db $06, $08 7655 7656 ResidualGravityCode: 7657 ldy #$00 ;this part appears to be residual, 7658 .db $2c ;no code branches or jumps to it... 7659 7660 ImposeGravityBlock: 7661 ldy #$01 ;set offset for maximum speed 7662 lda #$50 ;set movement amount here 7663 sta $00 7664 lda MaxSpdBlockData,y ;get maximum speed 7665 7666 ImposeGravitySprObj: 7667 sta $02 ;set maximum speed here 7668 lda #$00 ;set value to move downwards 7669 jmp ImposeGravity ;jump to the code that actually moves it 7670 7671 ;-------------------------------- 7672 7673 MovePlatformDown: 7674 lda #$00 ;save value to stack (if branching here, execute next 7675 .db $2c ;part as BIT instruction) 7676 7677 MovePlatformUp: 7678 lda #$01 ;save value to stack 7679 pha 7680 ldy Enemy_ID,x ;get enemy object identifier 7681 inx ;increment offset for enemy object 7682 lda #$05 ;load default value here 7683 cpy #$29 ;residual comparison, object #29 never executes 7684 bne SetDplSpd ;this code, thus unconditional branch here 7685 lda #$09 ;residual code 7686 SetDplSpd: sta $00 ;save downward movement amount here 7687 lda #$0a ;save upward movement amount here 7688 sta $01 7689 lda #$03 ;save maximum vertical speed here 7690 sta $02 7691 pla ;get value from stack 7692 tay ;use as Y, then move onto code shared by red koopa 7693 7694 RedPTroopaGrav: 7695 jsr ImposeGravity ;do a sub to move object gradually 7696 ldx ObjectOffset ;get enemy object offset and leave 7697 rts 7698 7699 ;------------------------------------------------------------------------------------- 7700 ;$00 - used for downward force 7701 ;$01 - used for upward force 7702 ;$07 - used as adder for vertical position 7703 7704 ImposeGravity: 7705 pha ;push value to stack 7706 lda SprObject_YMF_Dummy,x 7707 clc ;add value in movement force to contents of dummy variable 7708 adc SprObject_Y_MoveForce,x 7709 sta SprObject_YMF_Dummy,x 7710 ldy #$00 ;set Y to zero by default 7711 lda SprObject_Y_Speed,x ;get current vertical speed 7712 bpl AlterYP ;if currently moving downwards, do not decrement Y 7713 dey ;otherwise decrement Y 7714 AlterYP: sty $07 ;store Y here 7715 adc SprObject_Y_Position,x ;add vertical position to vertical speed plus carry 7716 sta SprObject_Y_Position,x ;store as new vertical position 7717 lda SprObject_Y_HighPos,x 7718 adc $07 ;add carry plus contents of $07 to vertical high byte 7719 sta SprObject_Y_HighPos,x ;store as new vertical high byte 7720 lda SprObject_Y_MoveForce,x 7721 clc 7722 adc $00 ;add downward movement amount to contents of $0433 7723 sta SprObject_Y_MoveForce,x 7724 lda SprObject_Y_Speed,x ;add carry to vertical speed and store 7725 adc #$00 7726 sta SprObject_Y_Speed,x 7727 cmp $02 ;compare to maximum speed 7728 bmi ChkUpM ;if less than preset value, skip this part 7729 lda SprObject_Y_MoveForce,x 7730 cmp #$80 ;if less positively than preset maximum, skip this part 7731 bcc ChkUpM 7732 lda $02 7733 sta SprObject_Y_Speed,x ;keep vertical speed within maximum value 7734 lda #$00 7735 sta SprObject_Y_MoveForce,x ;clear fractional 7736 ChkUpM: pla ;get value from stack 7737 beq ExVMove ;if set to zero, branch to leave 7738 lda $02 7739 eor #%11111111 ;otherwise get two's compliment of maximum speed 7740 tay 7741 iny 7742 sty $07 ;store two's compliment here 7743 lda SprObject_Y_MoveForce,x 7744 sec ;subtract upward movement amount from contents 7745 sbc $01 ;of movement force, note that $01 is twice as large as $00, 7746 sta SprObject_Y_MoveForce,x ;thus it effectively undoes add we did earlier 7747 lda SprObject_Y_Speed,x 7748 sbc #$00 ;subtract borrow from vertical speed and store 7749 sta SprObject_Y_Speed,x 7750 cmp $07 ;compare vertical speed to two's compliment 7751 bpl ExVMove ;if less negatively than preset maximum, skip this part 7752 lda SprObject_Y_MoveForce,x 7753 cmp #$80 ;check if fractional part is above certain amount, 7754 bcs ExVMove ;and if so, branch to leave 7755 lda $07 7756 sta SprObject_Y_Speed,x ;keep vertical speed within maximum value 7757 lda #$ff 7758 sta SprObject_Y_MoveForce,x ;clear fractional 7759 ExVMove: rts ;leave! 7760 7761 ;------------------------------------------------------------------------------------- 7762 7763 EnemiesAndLoopsCore: 7764 lda Enemy_Flag,x ;check data here for MSB set 7765 pha ;save in stack 7766 asl 7767 bcs ChkBowserF ;if MSB set in enemy flag, branch ahead of jumps 7768 pla ;get from stack 7769 beq ChkAreaTsk ;if data zero, branch 7770 jmp RunEnemyObjectsCore ;otherwise, jump to run enemy subroutines 7771 ChkAreaTsk: lda AreaParserTaskNum ;check number of tasks to perform 7772 and #$07 7773 cmp #$07 ;if at a specific task, jump and leave 7774 beq ExitELCore 7775 jmp ProcLoopCommand ;otherwise, jump to process loop command/load enemies 7776 ChkBowserF: pla ;get data from stack 7777 and #%00001111 ;mask out high nybble 7778 tay 7779 lda Enemy_Flag,y ;use as pointer and load same place with different offset 7780 bne ExitELCore 7781 sta Enemy_Flag,x ;if second enemy flag not set, also clear first one 7782 ExitELCore: rts 7783 7784 ;-------------------------------- 7785 7786 ;loop command data 7787 LoopCmdWorldNumber: 7788 .db $03, $03, $06, $06, $06, $06, $06, $06, $07, $07, $07 7789 7790 LoopCmdPageNumber: 7791 .db $05, $09, $04, $05, $06, $08, $09, $0a, $06, $0b, $10 7792 7793 LoopCmdYPosition: 7794 .db $40, $b0, $b0, $80, $40, $40, $80, $40, $f0, $f0, $f0 7795 7796 ExecGameLoopback: 7797 lda Player_PageLoc ;send player back four pages 7798 sec 7799 sbc #$04 7800 sta Player_PageLoc 7801 lda CurrentPageLoc ;send current page back four pages 7802 sec 7803 sbc #$04 7804 sta CurrentPageLoc 7805 lda ScreenLeft_PageLoc ;subtract four from page location 7806 sec ;of screen's left border 7807 sbc #$04 7808 sta ScreenLeft_PageLoc 7809 lda ScreenRight_PageLoc ;do the same for the page location 7810 sec ;of screen's right border 7811 sbc #$04 7812 sta ScreenRight_PageLoc 7813 lda AreaObjectPageLoc ;subtract four from page control 7814 sec ;for area objects 7815 sbc #$04 7816 sta AreaObjectPageLoc 7817 lda #$00 ;initialize page select for both 7818 sta EnemyObjectPageSel ;area and enemy objects 7819 sta AreaObjectPageSel 7820 sta EnemyDataOffset ;initialize enemy object data offset 7821 sta EnemyObjectPageLoc ;and enemy object page control 7822 lda AreaDataOfsLoopback,y ;adjust area object offset based on 7823 sta AreaDataOffset ;which loop command we encountered 7824 rts 7825 7826 ProcLoopCommand: 7827 lda LoopCommand ;check if loop command was found 7828 beq ChkEnemyFrenzy 7829 lda CurrentColumnPos ;check to see if we're still on the first page 7830 bne ChkEnemyFrenzy ;if not, do not loop yet 7831 ldy #$0b ;start at the end of each set of loop data 7832 FindLoop: dey 7833 bmi ChkEnemyFrenzy ;if all data is checked and not match, do not loop 7834 lda WorldNumber ;check to see if one of the world numbers 7835 cmp LoopCmdWorldNumber,y ;matches our current world number 7836 bne FindLoop 7837 lda CurrentPageLoc ;check to see if one of the page numbers 7838 cmp LoopCmdPageNumber,y ;matches the page we're currently on 7839 bne FindLoop 7840 lda Player_Y_Position ;check to see if the player is at the correct position 7841 cmp LoopCmdYPosition,y ;if not, branch to check for world 7 7842 bne WrongChk 7843 lda Player_State ;check to see if the player is 7844 cmp #$00 ;on solid ground (i.e. not jumping or falling) 7845 bne WrongChk ;if not, player fails to pass loop, and loopback 7846 lda WorldNumber ;are we in world 7? (check performed on correct 7847 cmp #World7 ;vertical position and on solid ground) 7848 bne InitMLp ;if not, initialize flags used there, otherwise 7849 inc MultiLoopCorrectCntr ;increment counter for correct progression 7850 IncMLoop: inc MultiLoopPassCntr ;increment master multi-part counter 7851 lda MultiLoopPassCntr ;have we done all three parts? 7852 cmp #$03 7853 bne InitLCmd ;if not, skip this part 7854 lda MultiLoopCorrectCntr ;if so, have we done them all correctly? 7855 cmp #$03 7856 beq InitMLp ;if so, branch past unnecessary check here 7857 bne DoLpBack ;unconditional branch if previous branch fails 7858 WrongChk: lda WorldNumber ;are we in world 7? (check performed on 7859 cmp #World7 ;incorrect vertical position or not on solid ground) 7860 beq IncMLoop 7861 DoLpBack: jsr ExecGameLoopback ;if player is not in right place, loop back 7862 jsr KillAllEnemies 7863 InitMLp: lda #$00 ;initialize counters used for multi-part loop commands 7864 sta MultiLoopPassCntr 7865 sta MultiLoopCorrectCntr 7866 InitLCmd: lda #$00 ;initialize loop command flag 7867 sta LoopCommand 7868 7869 ;-------------------------------- 7870 7871 ChkEnemyFrenzy: 7872 lda EnemyFrenzyQueue ;check for enemy object in frenzy queue 7873 beq ProcessEnemyData ;if not, skip this part 7874 sta Enemy_ID,x ;store as enemy object identifier here 7875 lda #$01 7876 sta Enemy_Flag,x ;activate enemy object flag 7877 lda #$00 7878 sta Enemy_State,x ;initialize state and frenzy queue 7879 sta EnemyFrenzyQueue 7880 jmp InitEnemyObject ;and then jump to deal with this enemy 7881 7882 ;-------------------------------- 7883 ;$06 - used to hold page location of extended right boundary 7884 ;$07 - used to hold high nybble of position of extended right boundary 7885 7886 ProcessEnemyData: 7887 ldy EnemyDataOffset ;get offset of enemy object data 7888 lda (EnemyData),y ;load first byte 7889 cmp #$ff ;check for EOD terminator 7890 bne CheckEndofBuffer 7891 jmp CheckFrenzyBuffer ;if found, jump to check frenzy buffer, otherwise 7892 7893 CheckEndofBuffer: 7894 and #%00001111 ;check for special row $0e 7895 cmp #$0e 7896 beq CheckRightBounds ;if found, branch, otherwise 7897 cpx #$05 ;check for end of buffer 7898 bcc CheckRightBounds ;if not at end of buffer, branch 7899 iny 7900 lda (EnemyData),y ;check for specific value here 7901 and #%00111111 ;not sure what this was intended for, exactly 7902 cmp #$2e ;this part is quite possibly residual code 7903 beq CheckRightBounds ;but it has the effect of keeping enemies out of 7904 rts ;the sixth slot 7905 7906 CheckRightBounds: 7907 lda ScreenRight_X_Pos ;add 48 to pixel coordinate of right boundary 7908 clc 7909 adc #$30 7910 and #%11110000 ;store high nybble 7911 sta $07 7912 lda ScreenRight_PageLoc ;add carry to page location of right boundary 7913 adc #$00 7914 sta $06 ;store page location + carry 7915 ldy EnemyDataOffset 7916 iny 7917 lda (EnemyData),y ;if MSB of enemy object is clear, branch to check for row $0f 7918 asl 7919 bcc CheckPageCtrlRow 7920 lda EnemyObjectPageSel ;if page select already set, do not set again 7921 bne CheckPageCtrlRow 7922 inc EnemyObjectPageSel ;otherwise, if MSB is set, set page select 7923 inc EnemyObjectPageLoc ;and increment page control 7924 7925 CheckPageCtrlRow: 7926 dey 7927 lda (EnemyData),y ;reread first byte 7928 and #$0f 7929 cmp #$0f ;check for special row $0f 7930 bne PositionEnemyObj ;if not found, branch to position enemy object 7931 lda EnemyObjectPageSel ;if page select set, 7932 bne PositionEnemyObj ;branch without reading second byte 7933 iny 7934 lda (EnemyData),y ;otherwise, get second byte, mask out 2 MSB 7935 and #%00111111 7936 sta EnemyObjectPageLoc ;store as page control for enemy object data 7937 inc EnemyDataOffset ;increment enemy object data offset 2 bytes 7938 inc EnemyDataOffset 7939 inc EnemyObjectPageSel ;set page select for enemy object data and 7940 jmp ProcLoopCommand ;jump back to process loop commands again 7941 7942 PositionEnemyObj: 7943 lda EnemyObjectPageLoc ;store page control as page location 7944 sta Enemy_PageLoc,x ;for enemy object 7945 lda (EnemyData),y ;get first byte of enemy object 7946 and #%11110000 7947 sta Enemy_X_Position,x ;store column position 7948 cmp ScreenRight_X_Pos ;check column position against right boundary 7949 lda Enemy_PageLoc,x ;without subtracting, then subtract borrow 7950 sbc ScreenRight_PageLoc ;from page location 7951 bcs CheckRightExtBounds ;if enemy object beyond or at boundary, branch 7952 lda (EnemyData),y 7953 and #%00001111 ;check for special row $0e 7954 cmp #$0e ;if found, jump elsewhere 7955 beq ParseRow0e 7956 jmp CheckThreeBytes ;if not found, unconditional jump 7957 7958 CheckRightExtBounds: 7959 lda $07 ;check right boundary + 48 against 7960 cmp Enemy_X_Position,x ;column position without subtracting, 7961 lda $06 ;then subtract borrow from page control temp 7962 sbc Enemy_PageLoc,x ;plus carry 7963 bcc CheckFrenzyBuffer ;if enemy object beyond extended boundary, branch 7964 lda #$01 ;store value in vertical high byte 7965 sta Enemy_Y_HighPos,x 7966 lda (EnemyData),y ;get first byte again 7967 asl ;multiply by four to get the vertical 7968 asl ;coordinate 7969 asl 7970 asl 7971 sta Enemy_Y_Position,x 7972 cmp #$e0 ;do one last check for special row $0e 7973 beq ParseRow0e ;(necessary if branched to $c1cb) 7974 iny 7975 lda (EnemyData),y ;get second byte of object 7976 and #%01000000 ;check to see if hard mode bit is set 7977 beq CheckForEnemyGroup ;if not, branch to check for group enemy objects 7978 lda SecondaryHardMode ;if set, check to see if secondary hard mode flag 7979 beq Inc2B ;is on, and if not, branch to skip this object completely 7980 7981 CheckForEnemyGroup: 7982 lda (EnemyData),y ;get second byte and mask out 2 MSB 7983 and #%00111111 7984 cmp #$37 ;check for value below $37 7985 bcc BuzzyBeetleMutate 7986 cmp #$3f ;if $37 or greater, check for value 7987 bcc DoGroup ;below $3f, branch if below $3f 7988 7989 BuzzyBeetleMutate: 7990 cmp #Goomba ;if below $37, check for goomba 7991 bne StrID ;value ($3f or more always fails) 7992 ldy PrimaryHardMode ;check if primary hard mode flag is set 7993 beq StrID ;and if so, change goomba to buzzy beetle 7994 lda #BuzzyBeetle 7995 StrID: sta Enemy_ID,x ;store enemy object number into buffer 7996 lda #$01 7997 sta Enemy_Flag,x ;set flag for enemy in buffer 7998 jsr InitEnemyObject 7999 lda Enemy_Flag,x ;check to see if flag is set 8000 bne Inc2B ;if not, leave, otherwise branch 8001 rts 8002 8003 CheckFrenzyBuffer: 8004 lda EnemyFrenzyBuffer ;if enemy object stored in frenzy buffer 8005 bne StrFre ;then branch ahead to store in enemy object buffer 8006 lda VineFlagOffset ;otherwise check vine flag offset 8007 cmp #$01 8008 bne ExEPar ;if other value <> 1, leave 8009 lda #VineObject ;otherwise put vine in enemy identifier 8010 StrFre: sta Enemy_ID,x ;store contents of frenzy buffer into enemy identifier value 8011 8012 InitEnemyObject: 8013 lda #$00 ;initialize enemy state 8014 sta Enemy_State,x 8015 jsr CheckpointEnemyID ;jump ahead to run jump engine and subroutines 8016 ExEPar: rts ;then leave 8017 8018 DoGroup: 8019 jmp HandleGroupEnemies ;handle enemy group objects 8020 8021 ParseRow0e: 8022 iny ;increment Y to load third byte of object 8023 iny 8024 lda (EnemyData),y 8025 lsr ;move 3 MSB to the bottom, effectively 8026 lsr ;making %xxx00000 into %00000xxx 8027 lsr 8028 lsr 8029 lsr 8030 cmp WorldNumber ;is it the same world number as we're on? 8031 bne NotUse ;if not, do not use (this allows multiple uses 8032 dey ;of the same area, like the underground bonus areas) 8033 lda (EnemyData),y ;otherwise, get second byte and use as offset 8034 sta AreaPointer ;to addresses for level and enemy object data 8035 iny 8036 lda (EnemyData),y ;get third byte again, and this time mask out 8037 and #%00011111 ;the 3 MSB from before, save as page number to be 8038 sta EntrancePage ;used upon entry to area, if area is entered 8039 NotUse: jmp Inc3B 8040 8041 CheckThreeBytes: 8042 ldy EnemyDataOffset ;load current offset for enemy object data 8043 lda (EnemyData),y ;get first byte 8044 and #%00001111 ;check for special row $0e 8045 cmp #$0e 8046 bne Inc2B 8047 Inc3B: inc EnemyDataOffset ;if row = $0e, increment three bytes 8048 Inc2B: inc EnemyDataOffset ;otherwise increment two bytes 8049 inc EnemyDataOffset 8050 lda #$00 ;init page select for enemy objects 8051 sta EnemyObjectPageSel 8052 ldx ObjectOffset ;reload current offset in enemy buffers 8053 rts ;and leave 8054 8055 CheckpointEnemyID: 8056 lda Enemy_ID,x 8057 cmp #$15 ;check enemy object identifier for $15 or greater 8058 bcs InitEnemyRoutines ;and branch straight to the jump engine if found 8059 tay ;save identifier in Y register for now 8060 lda Enemy_Y_Position,x 8061 adc #$08 ;add eight pixels to what will eventually be the 8062 sta Enemy_Y_Position,x ;enemy object's vertical coordinate ($00-$14 only) 8063 lda #$01 8064 sta EnemyOffscrBitsMasked,x ;set offscreen masked bit 8065 tya ;get identifier back and use as offset for jump engine 8066 8067 InitEnemyRoutines: 8068 jsr JumpEngine 8069 8070 ;jump engine table for newly loaded enemy objects 8071 8072 .dw InitNormalEnemy ;for objects $00-$0f 8073 .dw InitNormalEnemy 8074 .dw InitNormalEnemy 8075 .dw InitRedKoopa 8076 .dw NoInitCode 8077 .dw InitHammerBro 8078 .dw InitGoomba 8079 .dw InitBloober 8080 .dw InitBulletBill 8081 .dw NoInitCode 8082 .dw InitCheepCheep 8083 .dw InitCheepCheep 8084 .dw InitPodoboo 8085 .dw InitPiranhaPlant 8086 .dw InitJumpGPTroopa 8087 .dw InitRedPTroopa 8088 8089 .dw InitHorizFlySwimEnemy ;for objects $10-$1f 8090 .dw InitLakitu 8091 .dw InitEnemyFrenzy 8092 .dw NoInitCode 8093 .dw InitEnemyFrenzy 8094 .dw InitEnemyFrenzy 8095 .dw InitEnemyFrenzy 8096 .dw InitEnemyFrenzy 8097 .dw EndFrenzy 8098 .dw NoInitCode 8099 .dw NoInitCode 8100 .dw InitShortFirebar 8101 .dw InitShortFirebar 8102 .dw InitShortFirebar 8103 .dw InitShortFirebar 8104 .dw InitLongFirebar 8105 8106 .dw NoInitCode ;for objects $20-$2f 8107 .dw NoInitCode 8108 .dw NoInitCode 8109 .dw NoInitCode 8110 .dw InitBalPlatform 8111 .dw InitVertPlatform 8112 .dw LargeLiftUp 8113 .dw LargeLiftDown 8114 .dw InitHoriPlatform 8115 .dw InitDropPlatform 8116 .dw InitHoriPlatform 8117 .dw PlatLiftUp 8118 .dw PlatLiftDown 8119 .dw InitBowser 8120 .dw PwrUpJmp ;possibly dummy value 8121 .dw Setup_Vine 8122 8123 .dw NoInitCode ;for objects $30-$36 8124 .dw NoInitCode 8125 .dw NoInitCode 8126 .dw NoInitCode 8127 .dw NoInitCode 8128 .dw InitRetainerObj 8129 .dw EndOfEnemyInitCode 8130 8131 ;------------------------------------------------------------------------------------- 8132 8133 NoInitCode: 8134 rts ;this executed when enemy object has no init code 8135 8136 ;-------------------------------- 8137 8138 InitGoomba: 8139 jsr InitNormalEnemy ;set appropriate horizontal speed 8140 jmp SmallBBox ;set $09 as bounding box control, set other values 8141 8142 ;-------------------------------- 8143 8144 InitPodoboo: 8145 lda #$02 ;set enemy position to below 8146 sta Enemy_Y_HighPos,x ;the bottom of the screen 8147 sta Enemy_Y_Position,x 8148 lsr 8149 sta EnemyIntervalTimer,x ;set timer for enemy 8150 lsr 8151 sta Enemy_State,x ;initialize enemy state, then jump to use 8152 jmp SmallBBox ;$09 as bounding box size and set other things 8153 8154 ;-------------------------------- 8155 8156 InitRetainerObj: 8157 lda #$b8 ;set fixed vertical position for 8158 sta Enemy_Y_Position,x ;princess/mushroom retainer object 8159 rts 8160 8161 ;-------------------------------- 8162 8163 NormalXSpdData: 8164 .db $f8, $f4 8165 8166 InitNormalEnemy: 8167 ldy #$01 ;load offset of 1 by default 8168 lda PrimaryHardMode ;check for primary hard mode flag set 8169 bne GetESpd 8170 dey ;if not set, decrement offset 8171 GetESpd: lda NormalXSpdData,y ;get appropriate horizontal speed 8172 SetESpd: sta Enemy_X_Speed,x ;store as speed for enemy object 8173 jmp TallBBox ;branch to set bounding box control and other data 8174 8175 ;-------------------------------- 8176 8177 InitRedKoopa: 8178 jsr InitNormalEnemy ;load appropriate horizontal speed 8179 lda #$01 ;set enemy state for red koopa troopa $03 8180 sta Enemy_State,x 8181 rts 8182 8183 ;-------------------------------- 8184 8185 HBroWalkingTimerData: 8186 .db $80, $50 8187 8188 InitHammerBro: 8189 lda #$00 ;init horizontal speed and timer used by hammer bro 8190 sta HammerThrowingTimer,x ;apparently to time hammer throwing 8191 sta Enemy_X_Speed,x 8192 ldy SecondaryHardMode ;get secondary hard mode flag 8193 lda HBroWalkingTimerData,y 8194 sta EnemyIntervalTimer,x ;set value as delay for hammer bro to walk left 8195 lda #$0b ;set specific value for bounding box size control 8196 jmp SetBBox 8197 8198 ;-------------------------------- 8199 8200 InitHorizFlySwimEnemy: 8201 lda #$00 ;initialize horizontal speed 8202 jmp SetESpd 8203 8204 ;-------------------------------- 8205 8206 InitBloober: 8207 lda #$00 ;initialize horizontal speed 8208 sta BlooperMoveSpeed,x 8209 SmallBBox: lda #$09 ;set specific bounding box size control 8210 bne SetBBox ;unconditional branch 8211 8212 ;-------------------------------- 8213 8214 InitRedPTroopa: 8215 ldy #$30 ;load central position adder for 48 pixels down 8216 lda Enemy_Y_Position,x ;set vertical coordinate into location to 8217 sta RedPTroopaOrigXPos,x ;be used as original vertical coordinate 8218 bpl GetCent ;if vertical coordinate < $80 8219 ldy #$e0 ;if => $80, load position adder for 32 pixels up 8220 GetCent: tya ;send central position adder to A 8221 adc Enemy_Y_Position,x ;add to current vertical coordinate 8222 sta RedPTroopaCenterYPos,x ;store as central vertical coordinate 8223 TallBBox: lda #$03 ;set specific bounding box size control 8224 SetBBox: sta Enemy_BoundBoxCtrl,x ;set bounding box control here 8225 lda #$02 ;set moving direction for left 8226 sta Enemy_MovingDir,x 8227 InitVStf: lda #$00 ;initialize vertical speed 8228 sta Enemy_Y_Speed,x ;and movement force 8229 sta Enemy_Y_MoveForce,x 8230 rts 8231 8232 ;-------------------------------- 8233 8234 InitBulletBill: 8235 lda #$02 ;set moving direction for left 8236 sta Enemy_MovingDir,x 8237 lda #$09 ;set bounding box control for $09 8238 sta Enemy_BoundBoxCtrl,x 8239 rts 8240 8241 ;-------------------------------- 8242 8243 InitCheepCheep: 8244 jsr SmallBBox ;set vertical bounding box, speed, init others 8245 lda PseudoRandomBitReg,x ;check one portion of LSFR 8246 and #%00010000 ;get d4 from it 8247 sta CheepCheepMoveMFlag,x ;save as movement flag of some sort 8248 lda Enemy_Y_Position,x 8249 sta CheepCheepOrigYPos,x ;save original vertical coordinate here 8250 rts 8251 8252 ;-------------------------------- 8253 8254 InitLakitu: 8255 lda EnemyFrenzyBuffer ;check to see if an enemy is already in 8256 bne KillLakitu ;the frenzy buffer, and branch to kill lakitu if so 8257 8258 SetupLakitu: 8259 lda #$00 ;erase counter for lakitu's reappearance 8260 sta LakituReappearTimer 8261 jsr InitHorizFlySwimEnemy ;set $03 as bounding box, set other attributes 8262 jmp TallBBox2 ;set $03 as bounding box again (not necessary) and leave 8263 8264 KillLakitu: 8265 jmp EraseEnemyObject 8266 8267 ;-------------------------------- 8268 ;$01-$03 - used to hold pseudorandom difference adjusters 8269 8270 PRDiffAdjustData: 8271 .db $26, $2c, $32, $38 8272 .db $20, $22, $24, $26 8273 .db $13, $14, $15, $16 8274 8275 LakituAndSpinyHandler: 8276 lda FrenzyEnemyTimer ;if timer here not expired, leave 8277 bne ExLSHand 8278 cpx #$05 ;if we are on the special use slot, leave 8279 bcs ExLSHand 8280 lda #$80 ;set timer 8281 sta FrenzyEnemyTimer 8282 ldy #$04 ;start with the last enemy slot 8283 ChkLak: lda Enemy_ID,y ;check all enemy slots to see 8284 cmp #Lakitu ;if lakitu is on one of them 8285 beq CreateSpiny ;if so, branch out of this loop 8286 dey ;otherwise check another slot 8287 bpl ChkLak ;loop until all slots are checked 8288 inc LakituReappearTimer ;increment reappearance timer 8289 lda LakituReappearTimer 8290 cmp #$07 ;check to see if we're up to a certain value yet 8291 bcc ExLSHand ;if not, leave 8292 ldx #$04 ;start with the last enemy slot again 8293 ChkNoEn: lda Enemy_Flag,x ;check enemy buffer flag for non-active enemy slot 8294 beq CreateL ;branch out of loop if found 8295 dex ;otherwise check next slot 8296 bpl ChkNoEn ;branch until all slots are checked 8297 bmi RetEOfs ;if no empty slots were found, branch to leave 8298 CreateL: lda #$00 ;initialize enemy state 8299 sta Enemy_State,x 8300 lda #Lakitu ;create lakitu enemy object 8301 sta Enemy_ID,x 8302 jsr SetupLakitu ;do a sub to set up lakitu 8303 lda #$20 8304 jsr PutAtRightExtent ;finish setting up lakitu 8305 RetEOfs: ldx ObjectOffset ;get enemy object buffer offset again and leave 8306 ExLSHand: rts 8307 8308 ;-------------------------------- 8309 8310 CreateSpiny: 8311 lda Player_Y_Position ;if player above a certain point, branch to leave 8312 cmp #$2c 8313 bcc ExLSHand 8314 lda Enemy_State,y ;if lakitu is not in normal state, branch to leave 8315 bne ExLSHand 8316 lda Enemy_PageLoc,y ;store horizontal coordinates (high and low) of lakitu 8317 sta Enemy_PageLoc,x ;into the coordinates of the spiny we're going to create 8318 lda Enemy_X_Position,y 8319 sta Enemy_X_Position,x 8320 lda #$01 ;put spiny within vertical screen unit 8321 sta Enemy_Y_HighPos,x 8322 lda Enemy_Y_Position,y ;put spiny eight pixels above where lakitu is 8323 sec 8324 sbc #$08 8325 sta Enemy_Y_Position,x 8326 lda PseudoRandomBitReg,x ;get 2 LSB of LSFR and save to Y 8327 and #%00000011 8328 tay 8329 ldx #$02 8330 DifLoop: lda PRDiffAdjustData,y ;get three values and save them 8331 sta $01,x ;to $01-$03 8332 iny 8333 iny ;increment Y four bytes for each value 8334 iny 8335 iny 8336 dex ;decrement X for each one 8337 bpl DifLoop ;loop until all three are written 8338 ldx ObjectOffset ;get enemy object buffer offset 8339 jsr PlayerLakituDiff ;move enemy, change direction, get value - difference 8340 ldy Player_X_Speed ;check player's horizontal speed 8341 cpy #$08 8342 bcs SetSpSpd ;if moving faster than a certain amount, branch elsewhere 8343 tay ;otherwise save value in A to Y for now 8344 lda PseudoRandomBitReg+1,x 8345 and #%00000011 ;get one of the LSFR parts and save the 2 LSB 8346 beq UsePosv ;branch if neither bits are set 8347 tya 8348 eor #%11111111 ;otherwise get two's compliment of Y 8349 tay 8350 iny 8351 UsePosv: tya ;put value from A in Y back to A (they will be lost anyway) 8352 SetSpSpd: jsr SmallBBox ;set bounding box control, init attributes, lose contents of A 8353 ldy #$02 8354 sta Enemy_X_Speed,x ;set horizontal speed to zero because previous contents 8355 cmp #$00 ;of A were lost...branch here will never be taken for 8356 bmi SpinyRte ;the same reason 8357 dey 8358 SpinyRte: sty Enemy_MovingDir,x ;set moving direction to the right 8359 lda #$fd 8360 sta Enemy_Y_Speed,x ;set vertical speed to move upwards 8361 lda #$01 8362 sta Enemy_Flag,x ;enable enemy object by setting flag 8363 lda #$05 8364 sta Enemy_State,x ;put spiny in egg state and leave 8365 ChpChpEx: rts 8366 8367 ;-------------------------------- 8368 8369 FirebarSpinSpdData: 8370 .db $28, $38, $28, $38, $28 8371 8372 FirebarSpinDirData: 8373 .db $00, $00, $10, $10, $00 8374 8375 InitLongFirebar: 8376 jsr DuplicateEnemyObj ;create enemy object for long firebar 8377 8378 InitShortFirebar: 8379 lda #$00 ;initialize low byte of spin state 8380 sta FirebarSpinState_Low,x 8381 lda Enemy_ID,x ;subtract $1b from enemy identifier 8382 sec ;to get proper offset for firebar data 8383 sbc #$1b 8384 tay 8385 lda FirebarSpinSpdData,y ;get spinning speed of firebar 8386 sta FirebarSpinSpeed,x 8387 lda FirebarSpinDirData,y ;get spinning direction of firebar 8388 sta FirebarSpinDirection,x 8389 lda Enemy_Y_Position,x 8390 clc ;add four pixels to vertical coordinate 8391 adc #$04 8392 sta Enemy_Y_Position,x 8393 lda Enemy_X_Position,x 8394 clc ;add four pixels to horizontal coordinate 8395 adc #$04 8396 sta Enemy_X_Position,x 8397 lda Enemy_PageLoc,x 8398 adc #$00 ;add carry to page location 8399 sta Enemy_PageLoc,x 8400 jmp TallBBox2 ;set bounding box control (not used) and leave 8401 8402 ;-------------------------------- 8403 ;$00-$01 - used to hold pseudorandom bits 8404 8405 FlyCCXPositionData: 8406 .db $80, $30, $40, $80 8407 .db $30, $50, $50, $70 8408 .db $20, $40, $80, $a0 8409 .db $70, $40, $90, $68 8410 8411 FlyCCXSpeedData: 8412 .db $0e, $05, $06, $0e 8413 .db $1c, $20, $10, $0c 8414 .db $1e, $22, $18, $14 8415 8416 FlyCCTimerData: 8417 .db $10, $60, $20, $48 8418 8419 InitFlyingCheepCheep: 8420 lda FrenzyEnemyTimer ;if timer here not expired yet, branch to leave 8421 bne ChpChpEx 8422 jsr SmallBBox ;jump to set bounding box size $09 and init other values 8423 lda PseudoRandomBitReg+1,x 8424 and #%00000011 ;set pseudorandom offset here 8425 tay 8426 lda FlyCCTimerData,y ;load timer with pseudorandom offset 8427 sta FrenzyEnemyTimer 8428 ldy #$03 ;load Y with default value 8429 lda SecondaryHardMode 8430 beq MaxCC ;if secondary hard mode flag not set, do not increment Y 8431 iny ;otherwise, increment Y to allow as many as four onscreen 8432 MaxCC: sty $00 ;store whatever pseudorandom bits are in Y 8433 cpx $00 ;compare enemy object buffer offset with Y 8434 bcs ChpChpEx ;if X => Y, branch to leave 8435 lda PseudoRandomBitReg,x 8436 and #%00000011 ;get last two bits of LSFR, first part 8437 sta $00 ;and store in two places 8438 sta $01 8439 lda #$fb ;set vertical speed for cheep-cheep 8440 sta Enemy_Y_Speed,x 8441 lda #$00 ;load default value 8442 ldy Player_X_Speed ;check player's horizontal speed 8443 beq GSeed ;if player not moving left or right, skip this part 8444 lda #$04 8445 cpy #$19 ;if moving to the right but not very quickly, 8446 bcc GSeed ;do not change A 8447 asl ;otherwise, multiply A by 2 8448 GSeed: pha ;save to stack 8449 clc 8450 adc $00 ;add to last two bits of LSFR we saved earlier 8451 sta $00 ;save it there 8452 lda PseudoRandomBitReg+1,x 8453 and #%00000011 ;if neither of the last two bits of second LSFR set, 8454 beq RSeed ;skip this part and save contents of $00 8455 lda PseudoRandomBitReg+2,x 8456 and #%00001111 ;otherwise overwrite with lower nybble of 8457 sta $00 ;third LSFR part 8458 RSeed: pla ;get value from stack we saved earlier 8459 clc 8460 adc $01 ;add to last two bits of LSFR we saved in other place 8461 tay ;use as pseudorandom offset here 8462 lda FlyCCXSpeedData,y ;get horizontal speed using pseudorandom offset 8463 sta Enemy_X_Speed,x 8464 lda #$01 ;set to move towards the right 8465 sta Enemy_MovingDir,x 8466 lda Player_X_Speed ;if player moving left or right, branch ahead of this part 8467 bne D2XPos1 8468 ldy $00 ;get first LSFR or third LSFR lower nybble 8469 tya ;and check for d1 set 8470 and #%00000010 8471 beq D2XPos1 ;if d1 not set, branch 8472 lda Enemy_X_Speed,x 8473 eor #$ff ;if d1 set, change horizontal speed 8474 clc ;into two's compliment, thus moving in the opposite 8475 adc #$01 ;direction 8476 sta Enemy_X_Speed,x 8477 inc Enemy_MovingDir,x ;increment to move towards the left 8478 D2XPos1: tya ;get first LSFR or third LSFR lower nybble again 8479 and #%00000010 8480 beq D2XPos2 ;check for d1 set again, branch again if not set 8481 lda Player_X_Position ;get player's horizontal position 8482 clc 8483 adc FlyCCXPositionData,y ;if d1 set, add value obtained from pseudorandom offset 8484 sta Enemy_X_Position,x ;and save as enemy's horizontal position 8485 lda Player_PageLoc ;get player's page location 8486 adc #$00 ;add carry and jump past this part 8487 jmp FinCCSt 8488 D2XPos2: lda Player_X_Position ;get player's horizontal position 8489 sec 8490 sbc FlyCCXPositionData,y ;if d1 not set, subtract value obtained from pseudorandom 8491 sta Enemy_X_Position,x ;offset and save as enemy's horizontal position 8492 lda Player_PageLoc ;get player's page location 8493 sbc #$00 ;subtract borrow 8494 FinCCSt: sta Enemy_PageLoc,x ;save as enemy's page location 8495 lda #$01 8496 sta Enemy_Flag,x ;set enemy's buffer flag 8497 sta Enemy_Y_HighPos,x ;set enemy's high vertical byte 8498 lda #$f8 8499 sta Enemy_Y_Position,x ;put enemy below the screen, and we are done 8500 rts 8501 8502 ;-------------------------------- 8503 8504 InitBowser: 8505 jsr DuplicateEnemyObj ;jump to create another bowser object 8506 stx BowserFront_Offset ;save offset of first here 8507 lda #$00 8508 sta BowserBodyControls ;initialize bowser's body controls 8509 sta BridgeCollapseOffset ;and bridge collapse offset 8510 lda Enemy_X_Position,x 8511 sta BowserOrigXPos ;store original horizontal position here 8512 lda #$df 8513 sta BowserFireBreathTimer ;store something here 8514 sta Enemy_MovingDir,x ;and in moving direction 8515 lda #$20 8516 sta BowserFeetCounter ;set bowser's feet timer and in enemy timer 8517 sta EnemyFrameTimer,x 8518 lda #$05 8519 sta BowserHitPoints ;give bowser 5 hit points 8520 lsr 8521 sta BowserMovementSpeed ;set default movement speed here 8522 rts 8523 8524 ;-------------------------------- 8525 8526 DuplicateEnemyObj: 8527 ldy #$ff ;start at beginning of enemy slots 8528 FSLoop: iny ;increment one slot 8529 lda Enemy_Flag,y ;check enemy buffer flag for empty slot 8530 bne FSLoop ;if set, branch and keep checking 8531 sty DuplicateObj_Offset ;otherwise set offset here 8532 txa ;transfer original enemy buffer offset 8533 ora #%10000000 ;store with d7 set as flag in new enemy 8534 sta Enemy_Flag,y ;slot as well as enemy offset 8535 lda Enemy_PageLoc,x 8536 sta Enemy_PageLoc,y ;copy page location and horizontal coordinates 8537 lda Enemy_X_Position,x ;from original enemy to new enemy 8538 sta Enemy_X_Position,y 8539 lda #$01 8540 sta Enemy_Flag,x ;set flag as normal for original enemy 8541 sta Enemy_Y_HighPos,y ;set high vertical byte for new enemy 8542 lda Enemy_Y_Position,x 8543 sta Enemy_Y_Position,y ;copy vertical coordinate from original to new 8544 FlmEx: rts ;and then leave 8545 8546 ;-------------------------------- 8547 8548 FlameYPosData: 8549 .db $90, $80, $70, $90 8550 8551 FlameYMFAdderData: 8552 .db $ff, $01 8553 8554 InitBowserFlame: 8555 lda FrenzyEnemyTimer ;if timer not expired yet, branch to leave 8556 bne FlmEx 8557 sta Enemy_Y_MoveForce,x ;reset something here 8558 lda NoiseSoundQueue 8559 ora #Sfx_BowserFlame ;load bowser's flame sound into queue 8560 sta NoiseSoundQueue 8561 ldy BowserFront_Offset ;get bowser's buffer offset 8562 lda Enemy_ID,y ;check for bowser 8563 cmp #Bowser 8564 beq SpawnFromMouth ;branch if found 8565 jsr SetFlameTimer ;get timer data based on flame counter 8566 clc 8567 adc #$20 ;add 32 frames by default 8568 ldy SecondaryHardMode 8569 beq SetFrT ;if secondary mode flag not set, use as timer setting 8570 sec 8571 sbc #$10 ;otherwise subtract 16 frames for secondary hard mode 8572 SetFrT: sta FrenzyEnemyTimer ;set timer accordingly 8573 lda PseudoRandomBitReg,x 8574 and #%00000011 ;get 2 LSB from first part of LSFR 8575 sta BowserFlamePRandomOfs,x ;set here 8576 tay ;use as offset 8577 lda FlameYPosData,y ;load vertical position based on pseudorandom offset 8578 8579 PutAtRightExtent: 8580 sta Enemy_Y_Position,x ;set vertical position 8581 lda ScreenRight_X_Pos 8582 clc 8583 adc #$20 ;place enemy 32 pixels beyond right side of screen 8584 sta Enemy_X_Position,x 8585 lda ScreenRight_PageLoc 8586 adc #$00 ;add carry 8587 sta Enemy_PageLoc,x 8588 jmp FinishFlame ;skip this part to finish setting values 8589 8590 SpawnFromMouth: 8591 lda Enemy_X_Position,y ;get bowser's horizontal position 8592 sec 8593 sbc #$0e ;subtract 14 pixels 8594 sta Enemy_X_Position,x ;save as flame's horizontal position 8595 lda Enemy_PageLoc,y 8596 sta Enemy_PageLoc,x ;copy page location from bowser to flame 8597 lda Enemy_Y_Position,y 8598 clc ;add 8 pixels to bowser's vertical position 8599 adc #$08 8600 sta Enemy_Y_Position,x ;save as flame's vertical position 8601 lda PseudoRandomBitReg,x 8602 and #%00000011 ;get 2 LSB from first part of LSFR 8603 sta Enemy_YMF_Dummy,x ;save here 8604 tay ;use as offset 8605 lda FlameYPosData,y ;get value here using bits as offset 8606 ldy #$00 ;load default offset 8607 cmp Enemy_Y_Position,x ;compare value to flame's current vertical position 8608 bcc SetMF ;if less, do not increment offset 8609 iny ;otherwise increment now 8610 SetMF: lda FlameYMFAdderData,y ;get value here and save 8611 sta Enemy_Y_MoveForce,x ;to vertical movement force 8612 lda #$00 8613 sta EnemyFrenzyBuffer ;clear enemy frenzy buffer 8614 8615 FinishFlame: 8616 lda #$08 ;set $08 for bounding box control 8617 sta Enemy_BoundBoxCtrl,x 8618 lda #$01 ;set high byte of vertical and 8619 sta Enemy_Y_HighPos,x ;enemy buffer flag 8620 sta Enemy_Flag,x 8621 lsr 8622 sta Enemy_X_MoveForce,x ;initialize horizontal movement force, and 8623 sta Enemy_State,x ;enemy state 8624 rts 8625 8626 ;-------------------------------- 8627 8628 FireworksXPosData: 8629 .db $00, $30, $60, $60, $00, $20 8630 8631 FireworksYPosData: 8632 .db $60, $40, $70, $40, $60, $30 8633 8634 InitFireworks: 8635 lda FrenzyEnemyTimer ;if timer not expired yet, branch to leave 8636 bne ExitFWk 8637 lda #$20 ;otherwise reset timer 8638 sta FrenzyEnemyTimer 8639 dec FireworksCounter ;decrement for each explosion 8640 ldy #$06 ;start at last slot 8641 StarFChk: dey 8642 lda Enemy_ID,y ;check for presence of star flag object 8643 cmp #StarFlagObject ;if there isn't a star flag object, 8644 bne StarFChk ;routine goes into infinite loop = crash 8645 lda Enemy_X_Position,y 8646 sec ;get horizontal coordinate of star flag object, then 8647 sbc #$30 ;subtract 48 pixels from it and save to 8648 pha ;the stack 8649 lda Enemy_PageLoc,y 8650 sbc #$00 ;subtract the carry from the page location 8651 sta $00 ;of the star flag object 8652 lda FireworksCounter ;get fireworks counter 8653 clc 8654 adc Enemy_State,y ;add state of star flag object (possibly not necessary) 8655 tay ;use as offset 8656 pla ;get saved horizontal coordinate of star flag - 48 pixels 8657 clc 8658 adc FireworksXPosData,y ;add number based on offset of fireworks counter 8659 sta Enemy_X_Position,x ;store as the fireworks object horizontal coordinate 8660 lda $00 8661 adc #$00 ;add carry and store as page location for 8662 sta Enemy_PageLoc,x ;the fireworks object 8663 lda FireworksYPosData,y ;get vertical position using same offset 8664 sta Enemy_Y_Position,x ;and store as vertical coordinate for fireworks object 8665 lda #$01 8666 sta Enemy_Y_HighPos,x ;store in vertical high byte 8667 sta Enemy_Flag,x ;and activate enemy buffer flag 8668 lsr 8669 sta ExplosionGfxCounter,x ;initialize explosion counter 8670 lda #$08 8671 sta ExplosionTimerCounter,x ;set explosion timing counter 8672 ExitFWk: rts 8673 8674 ;-------------------------------- 8675 8676 Bitmasks: 8677 .db %00000001, %00000010, %00000100, %00001000, %00010000, %00100000, %01000000, %10000000 8678 8679 Enemy17YPosData: 8680 .db $40, $30, $90, $50, $20, $60, $a0, $70 8681 8682 SwimCC_IDData: 8683 .db $0a, $0b 8684 8685 BulletBillCheepCheep: 8686 lda FrenzyEnemyTimer ;if timer not expired yet, branch to leave 8687 bne ExF17 8688 lda AreaType ;are we in a water-type level? 8689 bne DoBulletBills ;if not, branch elsewhere 8690 cpx #$03 ;are we past third enemy slot? 8691 bcs ExF17 ;if so, branch to leave 8692 ldy #$00 ;load default offset 8693 lda PseudoRandomBitReg,x 8694 cmp #$aa ;check first part of LSFR against preset value 8695 bcc ChkW2 ;if less than preset, do not increment offset 8696 iny ;otherwise increment 8697 ChkW2: lda WorldNumber ;check world number 8698 cmp #World2 8699 beq Get17ID ;if we're on world 2, do not increment offset 8700 iny ;otherwise increment 8701 Get17ID: tya 8702 and #%00000001 ;mask out all but last bit of offset 8703 tay 8704 lda SwimCC_IDData,y ;load identifier for cheep-cheeps 8705 Set17ID: sta Enemy_ID,x ;store whatever's in A as enemy identifier 8706 lda BitMFilter 8707 cmp #$ff ;if not all bits set, skip init part and compare bits 8708 bne GetRBit 8709 lda #$00 ;initialize vertical position filter 8710 sta BitMFilter 8711 GetRBit: lda PseudoRandomBitReg,x ;get first part of LSFR 8712 and #%00000111 ;mask out all but 3 LSB 8713 ChkRBit: tay ;use as offset 8714 lda Bitmasks,y ;load bitmask 8715 bit BitMFilter ;perform AND on filter without changing it 8716 beq AddFBit 8717 iny ;increment offset 8718 tya 8719 and #%00000111 ;mask out all but 3 LSB thus keeping it 0-7 8720 jmp ChkRBit ;do another check 8721 AddFBit: ora BitMFilter ;add bit to already set bits in filter 8722 sta BitMFilter ;and store 8723 lda Enemy17YPosData,y ;load vertical position using offset 8724 jsr PutAtRightExtent ;set vertical position and other values 8725 sta Enemy_YMF_Dummy,x ;initialize dummy variable 8726 lda #$20 ;set timer 8727 sta FrenzyEnemyTimer 8728 jmp CheckpointEnemyID ;process our new enemy object 8729 8730 DoBulletBills: 8731 ldy #$ff ;start at beginning of enemy slots 8732 BB_SLoop: iny ;move onto the next slot 8733 cpy #$05 ;branch to play sound if we've done all slots 8734 bcs FireBulletBill 8735 lda Enemy_Flag,y ;if enemy buffer flag not set, 8736 beq BB_SLoop ;loop back and check another slot 8737 lda Enemy_ID,y 8738 cmp #BulletBill_FrenzyVar ;check enemy identifier for 8739 bne BB_SLoop ;bullet bill object (frenzy variant) 8740 ExF17: rts ;if found, leave 8741 8742 FireBulletBill: 8743 lda Square2SoundQueue 8744 ora #Sfx_Blast ;play fireworks/gunfire sound 8745 sta Square2SoundQueue 8746 lda #BulletBill_FrenzyVar ;load identifier for bullet bill object 8747 bne Set17ID ;unconditional branch 8748 8749 ;-------------------------------- 8750 ;$00 - used to store Y position of group enemies 8751 ;$01 - used to store enemy ID 8752 ;$02 - used to store page location of right side of screen 8753 ;$03 - used to store X position of right side of screen 8754 8755 HandleGroupEnemies: 8756 ldy #$00 ;load value for green koopa troopa 8757 sec 8758 sbc #$37 ;subtract $37 from second byte read 8759 pha ;save result in stack for now 8760 cmp #$04 ;was byte in $3b-$3e range? 8761 bcs SnglID ;if so, branch 8762 pha ;save another copy to stack 8763 ldy #Goomba ;load value for goomba enemy 8764 lda PrimaryHardMode ;if primary hard mode flag not set, 8765 beq PullID ;branch, otherwise change to value 8766 ldy #BuzzyBeetle ;for buzzy beetle 8767 PullID: pla ;get second copy from stack 8768 SnglID: sty $01 ;save enemy id here 8769 ldy #$b0 ;load default y coordinate 8770 and #$02 ;check to see if d1 was set 8771 beq SetYGp ;if so, move y coordinate up, 8772 ldy #$70 ;otherwise branch and use default 8773 SetYGp: sty $00 ;save y coordinate here 8774 lda ScreenRight_PageLoc ;get page number of right edge of screen 8775 sta $02 ;save here 8776 lda ScreenRight_X_Pos ;get pixel coordinate of right edge 8777 sta $03 ;save here 8778 ldy #$02 ;load two enemies by default 8779 pla ;get first copy from stack 8780 lsr ;check to see if d0 was set 8781 bcc CntGrp ;if not, use default value 8782 iny ;otherwise increment to three enemies 8783 CntGrp: sty NumberofGroupEnemies ;save number of enemies here 8784 GrLoop: ldx #$ff ;start at beginning of enemy buffers 8785 GSltLp: inx ;increment and branch if past 8786 cpx #$05 ;end of buffers 8787 bcs NextED 8788 lda Enemy_Flag,x ;check to see if enemy is already 8789 bne GSltLp ;stored in buffer, and branch if so 8790 lda $01 8791 sta Enemy_ID,x ;store enemy object identifier 8792 lda $02 8793 sta Enemy_PageLoc,x ;store page location for enemy object 8794 lda $03 8795 sta Enemy_X_Position,x ;store x coordinate for enemy object 8796 clc 8797 adc #$18 ;add 24 pixels for next enemy 8798 sta $03 8799 lda $02 ;add carry to page location for 8800 adc #$00 ;next enemy 8801 sta $02 8802 lda $00 ;store y coordinate for enemy object 8803 sta Enemy_Y_Position,x 8804 lda #$01 ;activate flag for buffer, and 8805 sta Enemy_Y_HighPos,x ;put enemy within the screen vertically 8806 sta Enemy_Flag,x 8807 jsr CheckpointEnemyID ;process each enemy object separately 8808 dec NumberofGroupEnemies ;do this until we run out of enemy objects 8809 bne GrLoop 8810 NextED: jmp Inc2B ;jump to increment data offset and leave 8811 8812 ;-------------------------------- 8813 8814 InitPiranhaPlant: 8815 lda #$01 ;set initial speed 8816 sta PiranhaPlant_Y_Speed,x 8817 lsr 8818 sta Enemy_State,x ;initialize enemy state and what would normally 8819 sta PiranhaPlant_MoveFlag,x ;be used as vertical speed, but not in this case 8820 lda Enemy_Y_Position,x 8821 sta PiranhaPlantDownYPos,x ;save original vertical coordinate here 8822 sec 8823 sbc #$18 8824 sta PiranhaPlantUpYPos,x ;save original vertical coordinate - 24 pixels here 8825 lda #$09 8826 jmp SetBBox2 ;set specific value for bounding box control 8827 8828 ;-------------------------------- 8829 8830 InitEnemyFrenzy: 8831 lda Enemy_ID,x ;load enemy identifier 8832 sta EnemyFrenzyBuffer ;save in enemy frenzy buffer 8833 sec 8834 sbc #$12 ;subtract 12 and use as offset for jump engine 8835 jsr JumpEngine 8836 8837 ;frenzy object jump table 8838 .dw LakituAndSpinyHandler 8839 .dw NoFrenzyCode 8840 .dw InitFlyingCheepCheep 8841 .dw InitBowserFlame 8842 .dw InitFireworks 8843 .dw BulletBillCheepCheep 8844 8845 ;-------------------------------- 8846 8847 NoFrenzyCode: 8848 rts 8849 8850 ;-------------------------------- 8851 8852 EndFrenzy: 8853 ldy #$05 ;start at last slot 8854 LakituChk: lda Enemy_ID,y ;check enemy identifiers 8855 cmp #Lakitu ;for lakitu 8856 bne NextFSlot 8857 lda #$01 ;if found, set state 8858 sta Enemy_State,y 8859 NextFSlot: dey ;move onto the next slot 8860 bpl LakituChk ;do this until all slots are checked 8861 lda #$00 8862 sta EnemyFrenzyBuffer ;empty enemy frenzy buffer 8863 sta Enemy_Flag,x ;disable enemy buffer flag for this object 8864 rts 8865 8866 ;-------------------------------- 8867 8868 InitJumpGPTroopa: 8869 lda #$02 ;set for movement to the left 8870 sta Enemy_MovingDir,x 8871 lda #$f8 ;set horizontal speed 8872 sta Enemy_X_Speed,x 8873 TallBBox2: lda #$03 ;set specific value for bounding box control 8874 SetBBox2: sta Enemy_BoundBoxCtrl,x ;set bounding box control then leave 8875 rts 8876 8877 ;-------------------------------- 8878 8879 InitBalPlatform: 8880 dec Enemy_Y_Position,x ;raise vertical position by two pixels 8881 dec Enemy_Y_Position,x 8882 ldy SecondaryHardMode ;if secondary hard mode flag not set, 8883 bne AlignP ;branch ahead 8884 ldy #$02 ;otherwise set value here 8885 jsr PosPlatform ;do a sub to add or subtract pixels 8886 AlignP: ldy #$ff ;set default value here for now 8887 lda BalPlatformAlignment ;get current balance platform alignment 8888 sta Enemy_State,x ;set platform alignment to object state here 8889 bpl SetBPA ;if old alignment $ff, put $ff as alignment for negative 8890 txa ;if old contents already $ff, put 8891 tay ;object offset as alignment to make next positive 8892 SetBPA: sty BalPlatformAlignment ;store whatever value's in Y here 8893 lda #$00 8894 sta Enemy_MovingDir,x ;init moving direction 8895 tay ;init Y 8896 jsr PosPlatform ;do a sub to add 8 pixels, then run shared code here 8897 8898 ;-------------------------------- 8899 8900 InitDropPlatform: 8901 lda #$ff 8902 sta PlatformCollisionFlag,x ;set some value here 8903 jmp CommonPlatCode ;then jump ahead to execute more code 8904 8905 ;-------------------------------- 8906 8907 InitHoriPlatform: 8908 lda #$00 8909 sta XMoveSecondaryCounter,x ;init one of the moving counters 8910 jmp CommonPlatCode ;jump ahead to execute more code 8911 8912 ;-------------------------------- 8913 8914 InitVertPlatform: 8915 ldy #$40 ;set default value here 8916 lda Enemy_Y_Position,x ;check vertical position 8917 bpl SetYO ;if above a certain point, skip this part 8918 eor #$ff 8919 clc ;otherwise get two's compliment 8920 adc #$01 8921 ldy #$c0 ;get alternate value to add to vertical position 8922 SetYO: sta YPlatformTopYPos,x ;save as top vertical position 8923 tya 8924 clc ;load value from earlier, add number of pixels 8925 adc Enemy_Y_Position,x ;to vertical position 8926 sta YPlatformCenterYPos,x ;save result as central vertical position 8927 8928 ;-------------------------------- 8929 8930 CommonPlatCode: 8931 jsr InitVStf ;do a sub to init certain other values 8932 SPBBox: lda #$05 ;set default bounding box size control 8933 ldy AreaType 8934 cpy #$03 ;check for castle-type level 8935 beq CasPBB ;use default value if found 8936 ldy SecondaryHardMode ;otherwise check for secondary hard mode flag 8937 bne CasPBB ;if set, use default value 8938 lda #$06 ;use alternate value if not castle or secondary not set 8939 CasPBB: sta Enemy_BoundBoxCtrl,x ;set bounding box size control here and leave 8940 rts 8941 8942 ;-------------------------------- 8943 8944 LargeLiftUp: 8945 jsr PlatLiftUp ;execute code for platforms going up 8946 jmp LargeLiftBBox ;overwrite bounding box for large platforms 8947 8948 LargeLiftDown: 8949 jsr PlatLiftDown ;execute code for platforms going down 8950 8951 LargeLiftBBox: 8952 jmp SPBBox ;jump to overwrite bounding box size control 8953 8954 ;-------------------------------- 8955 8956 PlatLiftUp: 8957 lda #$10 ;set movement amount here 8958 sta Enemy_Y_MoveForce,x 8959 lda #$ff ;set moving speed for platforms going up 8960 sta Enemy_Y_Speed,x 8961 jmp CommonSmallLift ;skip ahead to part we should be executing 8962 8963 ;-------------------------------- 8964 8965 PlatLiftDown: 8966 lda #$f0 ;set movement amount here 8967 sta Enemy_Y_MoveForce,x 8968 lda #$00 ;set moving speed for platforms going down 8969 sta Enemy_Y_Speed,x 8970 8971 ;-------------------------------- 8972 8973 CommonSmallLift: 8974 ldy #$01 8975 jsr PosPlatform ;do a sub to add 12 pixels due to preset value 8976 lda #$04 8977 sta Enemy_BoundBoxCtrl,x ;set bounding box control for small platforms 8978 rts 8979 8980 ;-------------------------------- 8981 8982 PlatPosDataLow: 8983 .db $08,$0c,$f8 8984 8985 PlatPosDataHigh: 8986 .db $00,$00,$ff 8987 8988 PosPlatform: 8989 lda Enemy_X_Position,x ;get horizontal coordinate 8990 clc 8991 adc PlatPosDataLow,y ;add or subtract pixels depending on offset 8992 sta Enemy_X_Position,x ;store as new horizontal coordinate 8993 lda Enemy_PageLoc,x 8994 adc PlatPosDataHigh,y ;add or subtract page location depending on offset 8995 sta Enemy_PageLoc,x ;store as new page location 8996 rts ;and go back 8997 8998 ;-------------------------------- 8999 9000 EndOfEnemyInitCode: 9001 rts 9002 9003 ;------------------------------------------------------------------------------------- 9004 9005 RunEnemyObjectsCore: 9006 ldx ObjectOffset ;get offset for enemy object buffer 9007 lda #$00 ;load value 0 for jump engine by default 9008 ldy Enemy_ID,x 9009 cpy #$15 ;if enemy object < $15, use default value 9010 bcc JmpEO 9011 tya ;otherwise subtract $14 from the value and use 9012 sbc #$14 ;as value for jump engine 9013 JmpEO: jsr JumpEngine 9014 9015 .dw RunNormalEnemies ;for objects $00-$14 9016 9017 .dw RunBowserFlame ;for objects $15-$1f 9018 .dw RunFireworks 9019 .dw NoRunCode 9020 .dw NoRunCode 9021 .dw NoRunCode 9022 .dw NoRunCode 9023 .dw RunFirebarObj 9024 .dw RunFirebarObj 9025 .dw RunFirebarObj 9026 .dw RunFirebarObj 9027 .dw RunFirebarObj 9028 9029 .dw RunFirebarObj ;for objects $20-$2f 9030 .dw RunFirebarObj 9031 .dw RunFirebarObj 9032 .dw NoRunCode 9033 .dw RunLargePlatform 9034 .dw RunLargePlatform 9035 .dw RunLargePlatform 9036 .dw RunLargePlatform 9037 .dw RunLargePlatform 9038 .dw RunLargePlatform 9039 .dw RunLargePlatform 9040 .dw RunSmallPlatform 9041 .dw RunSmallPlatform 9042 .dw RunBowser 9043 .dw PowerUpObjHandler 9044 .dw VineObjectHandler 9045 9046 .dw NoRunCode ;for objects $30-$35 9047 .dw RunStarFlagObj 9048 .dw JumpspringHandler 9049 .dw NoRunCode 9050 .dw WarpZoneObject 9051 .dw RunRetainerObj 9052 9053 ;-------------------------------- 9054 9055 NoRunCode: 9056 rts 9057 9058 ;-------------------------------- 9059 9060 RunRetainerObj: 9061 jsr GetEnemyOffscreenBits 9062 jsr RelativeEnemyPosition 9063 jmp EnemyGfxHandler 9064 9065 ;-------------------------------- 9066 9067 RunNormalEnemies: 9068 lda #$00 ;init sprite attributes 9069 sta Enemy_SprAttrib,x 9070 jsr GetEnemyOffscreenBits 9071 jsr RelativeEnemyPosition 9072 jsr EnemyGfxHandler 9073 jsr GetEnemyBoundBox 9074 jsr EnemyToBGCollisionDet 9075 jsr EnemiesCollision 9076 jsr PlayerEnemyCollision 9077 ldy TimerControl ;if master timer control set, skip to last routine 9078 bne SkipMove 9079 jsr EnemyMovementSubs 9080 SkipMove: jmp OffscreenBoundsCheck 9081 9082 EnemyMovementSubs: 9083 lda Enemy_ID,x 9084 jsr JumpEngine 9085 9086 .dw MoveNormalEnemy ;only objects $00-$14 use this table 9087 .dw MoveNormalEnemy 9088 .dw MoveNormalEnemy 9089 .dw MoveNormalEnemy 9090 .dw MoveNormalEnemy 9091 .dw ProcHammerBro 9092 .dw MoveNormalEnemy 9093 .dw MoveBloober 9094 .dw MoveBulletBill 9095 .dw NoMoveCode 9096 .dw MoveSwimmingCheepCheep 9097 .dw MoveSwimmingCheepCheep 9098 .dw MovePodoboo 9099 .dw MovePiranhaPlant 9100 .dw MoveJumpingEnemy 9101 .dw ProcMoveRedPTroopa 9102 .dw MoveFlyGreenPTroopa 9103 .dw MoveLakitu 9104 .dw MoveNormalEnemy 9105 .dw NoMoveCode ;dummy 9106 .dw MoveFlyingCheepCheep 9107 9108 ;-------------------------------- 9109 9110 NoMoveCode: 9111 rts 9112 9113 ;-------------------------------- 9114 9115 RunBowserFlame: 9116 jsr ProcBowserFlame 9117 jsr GetEnemyOffscreenBits 9118 jsr RelativeEnemyPosition 9119 jsr GetEnemyBoundBox 9120 jsr PlayerEnemyCollision 9121 jmp OffscreenBoundsCheck 9122 9123 ;-------------------------------- 9124 9125 RunFirebarObj: 9126 jsr ProcFirebar 9127 jmp OffscreenBoundsCheck 9128 9129 ;-------------------------------- 9130 9131 RunSmallPlatform: 9132 jsr GetEnemyOffscreenBits 9133 jsr RelativeEnemyPosition 9134 jsr SmallPlatformBoundBox 9135 jsr SmallPlatformCollision 9136 jsr RelativeEnemyPosition 9137 jsr DrawSmallPlatform 9138 jsr MoveSmallPlatform 9139 jmp OffscreenBoundsCheck 9140 9141 ;-------------------------------- 9142 9143 RunLargePlatform: 9144 jsr GetEnemyOffscreenBits 9145 jsr RelativeEnemyPosition 9146 jsr LargePlatformBoundBox 9147 jsr LargePlatformCollision 9148 lda TimerControl ;if master timer control set, 9149 bne SkipPT ;skip subroutine tree 9150 jsr LargePlatformSubroutines 9151 SkipPT: jsr RelativeEnemyPosition 9152 jsr DrawLargePlatform 9153 jmp OffscreenBoundsCheck 9154 9155 ;-------------------------------- 9156 9157 LargePlatformSubroutines: 9158 lda Enemy_ID,x ;subtract $24 to get proper offset for jump table 9159 sec 9160 sbc #$24 9161 jsr JumpEngine 9162 9163 .dw BalancePlatform ;table used by objects $24-$2a 9164 .dw YMovingPlatform 9165 .dw MoveLargeLiftPlat 9166 .dw MoveLargeLiftPlat 9167 .dw XMovingPlatform 9168 .dw DropPlatform 9169 .dw RightPlatform 9170 9171 ;------------------------------------------------------------------------------------- 9172 9173 EraseEnemyObject: 9174 lda #$00 ;clear all enemy object variables 9175 sta Enemy_Flag,x 9176 sta Enemy_ID,x 9177 sta Enemy_State,x 9178 sta FloateyNum_Control,x 9179 sta EnemyIntervalTimer,x 9180 sta ShellChainCounter,x 9181 sta Enemy_SprAttrib,x 9182 sta EnemyFrameTimer,x 9183 rts 9184 9185 ;------------------------------------------------------------------------------------- 9186 9187 MovePodoboo: 9188 lda EnemyIntervalTimer,x ;check enemy timer 9189 bne PdbM ;branch to move enemy if not expired 9190 jsr InitPodoboo ;otherwise set up podoboo again 9191 lda PseudoRandomBitReg+1,x ;get part of LSFR 9192 ora #%10000000 ;set d7 9193 sta Enemy_Y_MoveForce,x ;store as movement force 9194 and #%00001111 ;mask out high nybble 9195 ora #$06 ;set for at least six intervals 9196 sta EnemyIntervalTimer,x ;store as new enemy timer 9197 lda #$f9 9198 sta Enemy_Y_Speed,x ;set vertical speed to move podoboo upwards 9199 PdbM: jmp MoveJ_EnemyVertically ;branch to impose gravity on podoboo 9200 9201 ;-------------------------------- 9202 ;$00 - used in HammerBroJumpCode as bitmask 9203 9204 HammerThrowTmrData: 9205 .db $30, $1c 9206 9207 XSpeedAdderData: 9208 .db $00, $e8, $00, $18 9209 9210 RevivedXSpeed: 9211 .db $08, $f8, $0c, $f4 9212 9213 ProcHammerBro: 9214 lda Enemy_State,x ;check hammer bro's enemy state for d5 set 9215 and #%00100000 9216 beq ChkJH ;if not set, go ahead with code 9217 jmp MoveDefeatedEnemy ;otherwise jump to something else 9218 ChkJH: lda HammerBroJumpTimer,x ;check jump timer 9219 beq HammerBroJumpCode ;if expired, branch to jump 9220 dec HammerBroJumpTimer,x ;otherwise decrement jump timer 9221 lda Enemy_OffscreenBits 9222 and #%00001100 ;check offscreen bits 9223 bne MoveHammerBroXDir ;if hammer bro a little offscreen, skip to movement code 9224 lda HammerThrowingTimer,x ;check hammer throwing timer 9225 bne DecHT ;if not expired, skip ahead, do not throw hammer 9226 ldy SecondaryHardMode ;otherwise get secondary hard mode flag 9227 lda HammerThrowTmrData,y ;get timer data using flag as offset 9228 sta HammerThrowingTimer,x ;set as new timer 9229 jsr SpawnHammerObj ;do a sub here to spawn hammer object 9230 bcc DecHT ;if carry clear, hammer not spawned, skip to decrement timer 9231 lda Enemy_State,x 9232 ora #%00001000 ;set d3 in enemy state for hammer throw 9233 sta Enemy_State,x 9234 jmp MoveHammerBroXDir ;jump to move hammer bro 9235 DecHT: dec HammerThrowingTimer,x ;decrement timer 9236 jmp MoveHammerBroXDir ;jump to move hammer bro 9237 9238 HammerBroJumpLData: 9239 .db $20, $37 9240 9241 HammerBroJumpCode: 9242 lda Enemy_State,x ;get hammer bro's enemy state 9243 and #%00000111 ;mask out all but 3 LSB 9244 cmp #$01 ;check for d0 set (for jumping) 9245 beq MoveHammerBroXDir ;if set, branch ahead to moving code 9246 lda #$00 ;load default value here 9247 sta $00 ;save into temp variable for now 9248 ldy #$fa ;set default vertical speed 9249 lda Enemy_Y_Position,x ;check hammer bro's vertical coordinate 9250 bmi SetHJ ;if on the bottom half of the screen, use current speed 9251 ldy #$fd ;otherwise set alternate vertical speed 9252 cmp #$70 ;check to see if hammer bro is above the middle of screen 9253 inc $00 ;increment preset value to $01 9254 bcc SetHJ ;if above the middle of the screen, use current speed and $01 9255 dec $00 ;otherwise return value to $00 9256 lda PseudoRandomBitReg+1,x ;get part of LSFR, mask out all but LSB 9257 and #$01 9258 bne SetHJ ;if d0 of LSFR set, branch and use current speed and $00 9259 ldy #$fa ;otherwise reset to default vertical speed 9260 SetHJ: sty Enemy_Y_Speed,x ;set vertical speed for jumping 9261 lda Enemy_State,x ;set d0 in enemy state for jumping 9262 ora #$01 9263 sta Enemy_State,x 9264 lda $00 ;load preset value here to use as bitmask 9265 and PseudoRandomBitReg+2,x ;and do bit-wise comparison with part of LSFR 9266 tay ;then use as offset 9267 lda SecondaryHardMode ;check secondary hard mode flag 9268 bne HJump 9269 tay ;if secondary hard mode flag clear, set offset to 0 9270 HJump: lda HammerBroJumpLData,y ;get jump length timer data using offset from before 9271 sta EnemyFrameTimer,x ;save in enemy timer 9272 lda PseudoRandomBitReg+1,x 9273 ora #%11000000 ;get contents of part of LSFR, set d7 and d6, then 9274 sta HammerBroJumpTimer,x ;store in jump timer 9275 9276 MoveHammerBroXDir: 9277 ldy #$fc ;move hammer bro a little to the left 9278 lda FrameCounter 9279 and #%01000000 ;change hammer bro's direction every 64 frames 9280 bne Shimmy 9281 ldy #$04 ;if d6 set in counter, move him a little to the right 9282 Shimmy: sty Enemy_X_Speed,x ;store horizontal speed 9283 ldy #$01 ;set to face right by default 9284 jsr PlayerEnemyDiff ;get horizontal difference between player and hammer bro 9285 bmi SetShim ;if enemy to the left of player, skip this part 9286 iny ;set to face left 9287 lda EnemyIntervalTimer,x ;check walking timer 9288 bne SetShim ;if not yet expired, skip to set moving direction 9289 lda #$f8 9290 sta Enemy_X_Speed,x ;otherwise, make the hammer bro walk left towards player 9291 SetShim: sty Enemy_MovingDir,x ;set moving direction 9292 9293 MoveNormalEnemy: 9294 ldy #$00 ;init Y to leave horizontal movement as-is 9295 lda Enemy_State,x 9296 and #%01000000 ;check enemy state for d6 set, if set skip 9297 bne FallE ;to move enemy vertically, then horizontally if necessary 9298 lda Enemy_State,x 9299 asl ;check enemy state for d7 set 9300 bcs SteadM ;if set, branch to move enemy horizontally 9301 lda Enemy_State,x 9302 and #%00100000 ;check enemy state for d5 set 9303 bne MoveDefeatedEnemy ;if set, branch to move defeated enemy object 9304 lda Enemy_State,x 9305 and #%00000111 ;check d2-d0 of enemy state for any set bits 9306 beq SteadM ;if enemy in normal state, branch to move enemy horizontally 9307 cmp #$05 9308 beq FallE ;if enemy in state used by spiny's egg, go ahead here 9309 cmp #$03 9310 bcs ReviveStunned ;if enemy in states $03 or $04, skip ahead to yet another part 9311 FallE: jsr MoveD_EnemyVertically ;do a sub here to move enemy downwards 9312 ldy #$00 9313 lda Enemy_State,x ;check for enemy state $02 9314 cmp #$02 9315 beq MEHor ;if found, branch to move enemy horizontally 9316 and #%01000000 ;check for d6 set 9317 beq SteadM ;if not set, branch to something else 9318 lda Enemy_ID,x 9319 cmp #PowerUpObject ;check for power-up object 9320 beq SteadM 9321 bne SlowM ;if any other object where d6 set, jump to set Y 9322 MEHor: jmp MoveEnemyHorizontally ;jump here to move enemy horizontally for <> $2e and d6 set 9323 9324 SlowM: ldy #$01 ;if branched here, increment Y to slow horizontal movement 9325 SteadM: lda Enemy_X_Speed,x ;get current horizontal speed 9326 pha ;save to stack 9327 bpl AddHS ;if not moving or moving right, skip, leave Y alone 9328 iny 9329 iny ;otherwise increment Y to next data 9330 AddHS: clc 9331 adc XSpeedAdderData,y ;add value here to slow enemy down if necessary 9332 sta Enemy_X_Speed,x ;save as horizontal speed temporarily 9333 jsr MoveEnemyHorizontally ;then do a sub to move horizontally 9334 pla 9335 sta Enemy_X_Speed,x ;get old horizontal speed from stack and return to 9336 rts ;original memory location, then leave 9337 9338 ReviveStunned: 9339 lda EnemyIntervalTimer,x ;if enemy timer not expired yet, 9340 bne ChkKillGoomba ;skip ahead to something else 9341 sta Enemy_State,x ;otherwise initialize enemy state to normal 9342 lda FrameCounter 9343 and #$01 ;get d0 of frame counter 9344 tay ;use as Y and increment for movement direction 9345 iny 9346 sty Enemy_MovingDir,x ;store as pseudorandom movement direction 9347 dey ;decrement for use as pointer 9348 lda PrimaryHardMode ;check primary hard mode flag 9349 beq SetRSpd ;if not set, use pointer as-is 9350 iny 9351 iny ;otherwise increment 2 bytes to next data 9352 SetRSpd: lda RevivedXSpeed,y ;load and store new horizontal speed 9353 sta Enemy_X_Speed,x ;and leave 9354 rts 9355 9356 MoveDefeatedEnemy: 9357 jsr MoveD_EnemyVertically ;execute sub to move defeated enemy downwards 9358 jmp MoveEnemyHorizontally ;now move defeated enemy horizontally 9359 9360 ChkKillGoomba: 9361 cmp #$0e ;check to see if enemy timer has reached 9362 bne NKGmba ;a certain point, and branch to leave if not 9363 lda Enemy_ID,x 9364 cmp #Goomba ;check for goomba object 9365 bne NKGmba ;branch if not found 9366 jsr EraseEnemyObject ;otherwise, kill this goomba object 9367 NKGmba: rts ;leave! 9368 9369 ;-------------------------------- 9370 9371 MoveJumpingEnemy: 9372 jsr MoveJ_EnemyVertically ;do a sub to impose gravity on green paratroopa 9373 jmp MoveEnemyHorizontally ;jump to move enemy horizontally 9374 9375 ;-------------------------------- 9376 9377 ProcMoveRedPTroopa: 9378 lda Enemy_Y_Speed,x 9379 ora Enemy_Y_MoveForce,x ;check for any vertical force or speed 9380 bne MoveRedPTUpOrDown ;branch if any found 9381 sta Enemy_YMF_Dummy,x ;initialize something here 9382 lda Enemy_Y_Position,x ;check current vs. original vertical coordinate 9383 cmp RedPTroopaOrigXPos,x 9384 bcs MoveRedPTUpOrDown ;if current => original, skip ahead to more code 9385 lda FrameCounter ;get frame counter 9386 and #%00000111 ;mask out all but 3 LSB 9387 bne NoIncPT ;if any bits set, branch to leave 9388 inc Enemy_Y_Position,x ;otherwise increment red paratroopa's vertical position 9389 NoIncPT: rts ;leave 9390 9391 MoveRedPTUpOrDown: 9392 lda Enemy_Y_Position,x ;check current vs. central vertical coordinate 9393 cmp RedPTroopaCenterYPos,x 9394 bcc MovPTDwn ;if current < central, jump to move downwards 9395 jmp MoveRedPTroopaUp ;otherwise jump to move upwards 9396 MovPTDwn: jmp MoveRedPTroopaDown ;move downwards 9397 9398 ;-------------------------------- 9399 ;$00 - used to store adder for movement, also used as adder for platform 9400 ;$01 - used to store maximum value for secondary counter 9401 9402 MoveFlyGreenPTroopa: 9403 jsr XMoveCntr_GreenPTroopa ;do sub to increment primary and secondary counters 9404 jsr MoveWithXMCntrs ;do sub to move green paratroopa accordingly, and horizontally 9405 ldy #$01 ;set Y to move green paratroopa down 9406 lda FrameCounter 9407 and #%00000011 ;check frame counter 2 LSB for any bits set 9408 bne NoMGPT ;branch to leave if set to move up/down every fourth frame 9409 lda FrameCounter 9410 and #%01000000 ;check frame counter for d6 set 9411 bne YSway ;branch to move green paratroopa down if set 9412 ldy #$ff ;otherwise set Y to move green paratroopa up 9413 YSway: sty $00 ;store adder here 9414 lda Enemy_Y_Position,x 9415 clc ;add or subtract from vertical position 9416 adc $00 ;to give green paratroopa a wavy flight 9417 sta Enemy_Y_Position,x 9418 NoMGPT: rts ;leave! 9419 9420 XMoveCntr_GreenPTroopa: 9421 lda #$13 ;load preset maximum value for secondary counter 9422 9423 XMoveCntr_Platform: 9424 sta $01 ;store value here 9425 lda FrameCounter 9426 and #%00000011 ;branch to leave if not on 9427 bne NoIncXM ;every fourth frame 9428 ldy XMoveSecondaryCounter,x ;get secondary counter 9429 lda XMovePrimaryCounter,x ;get primary counter 9430 lsr 9431 bcs DecSeXM ;if d0 of primary counter set, branch elsewhere 9432 cpy $01 ;compare secondary counter to preset maximum value 9433 beq IncPXM ;if equal, branch ahead of this part 9434 inc XMoveSecondaryCounter,x ;increment secondary counter and leave 9435 NoIncXM: rts 9436 IncPXM: inc XMovePrimaryCounter,x ;increment primary counter and leave 9437 rts 9438 DecSeXM: tya ;put secondary counter in A 9439 beq IncPXM ;if secondary counter at zero, branch back 9440 dec XMoveSecondaryCounter,x ;otherwise decrement secondary counter and leave 9441 rts 9442 9443 MoveWithXMCntrs: 9444 lda XMoveSecondaryCounter,x ;save secondary counter to stack 9445 pha 9446 ldy #$01 ;set value here by default 9447 lda XMovePrimaryCounter,x 9448 and #%00000010 ;if d1 of primary counter is 9449 bne XMRight ;set, branch ahead of this part here 9450 lda XMoveSecondaryCounter,x 9451 eor #$ff ;otherwise change secondary 9452 clc ;counter to two's compliment 9453 adc #$01 9454 sta XMoveSecondaryCounter,x 9455 ldy #$02 ;load alternate value here 9456 XMRight: sty Enemy_MovingDir,x ;store as moving direction 9457 jsr MoveEnemyHorizontally 9458 sta $00 ;save value obtained from sub here 9459 pla ;get secondary counter from stack 9460 sta XMoveSecondaryCounter,x ;and return to original place 9461 rts 9462 9463 ;-------------------------------- 9464 9465 BlooberBitmasks: 9466 .db %00111111, %00000011 9467 9468 MoveBloober: 9469 lda Enemy_State,x 9470 and #%00100000 ;check enemy state for d5 set 9471 bne MoveDefeatedBloober ;branch if set to move defeated bloober 9472 ldy SecondaryHardMode ;use secondary hard mode flag as offset 9473 lda PseudoRandomBitReg+1,x ;get LSFR 9474 and BlooberBitmasks,y ;mask out bits in LSFR using bitmask loaded with offset 9475 bne BlooberSwim ;if any bits set, skip ahead to make swim 9476 txa 9477 lsr ;check to see if on second or fourth slot (1 or 3) 9478 bcc FBLeft ;if not, branch to figure out moving direction 9479 ldy Player_MovingDir ;otherwise, load player's moving direction and 9480 bcs SBMDir ;do an unconditional branch to set 9481 FBLeft: ldy #$02 ;set left moving direction by default 9482 jsr PlayerEnemyDiff ;get horizontal difference between player and bloober 9483 bpl SBMDir ;if enemy to the right of player, keep left 9484 dey ;otherwise decrement to set right moving direction 9485 SBMDir: sty Enemy_MovingDir,x ;set moving direction of bloober, then continue on here 9486 9487 BlooberSwim: 9488 jsr ProcSwimmingB ;execute sub to make bloober swim characteristically 9489 lda Enemy_Y_Position,x ;get vertical coordinate 9490 sec 9491 sbc Enemy_Y_MoveForce,x ;subtract movement force 9492 cmp #$20 ;check to see if position is above edge of status bar 9493 bcc SwimX ;if so, don't do it 9494 sta Enemy_Y_Position,x ;otherwise, set new vertical position, make bloober swim 9495 SwimX: ldy Enemy_MovingDir,x ;check moving direction 9496 dey 9497 bne LeftSwim ;if moving to the left, branch to second part 9498 lda Enemy_X_Position,x 9499 clc ;add movement speed to horizontal coordinate 9500 adc BlooperMoveSpeed,x 9501 sta Enemy_X_Position,x ;store result as new horizontal coordinate 9502 lda Enemy_PageLoc,x 9503 adc #$00 ;add carry to page location 9504 sta Enemy_PageLoc,x ;store as new page location and leave 9505 rts 9506 9507 LeftSwim: 9508 lda Enemy_X_Position,x 9509 sec ;subtract movement speed from horizontal coordinate 9510 sbc BlooperMoveSpeed,x 9511 sta Enemy_X_Position,x ;store result as new horizontal coordinate 9512 lda Enemy_PageLoc,x 9513 sbc #$00 ;subtract borrow from page location 9514 sta Enemy_PageLoc,x ;store as new page location and leave 9515 rts 9516 9517 MoveDefeatedBloober: 9518 jmp MoveEnemySlowVert ;jump to move defeated bloober downwards 9519 9520 ProcSwimmingB: 9521 lda BlooperMoveCounter,x ;get enemy's movement counter 9522 and #%00000010 ;check for d1 set 9523 bne ChkForFloatdown ;branch if set 9524 lda FrameCounter 9525 and #%00000111 ;get 3 LSB of frame counter 9526 pha ;and save it to the stack 9527 lda BlooperMoveCounter,x ;get enemy's movement counter 9528 lsr ;check for d0 set 9529 bcs SlowSwim ;branch if set 9530 pla ;pull 3 LSB of frame counter from the stack 9531 bne BSwimE ;branch to leave, execute code only every eighth frame 9532 lda Enemy_Y_MoveForce,x 9533 clc ;add to movement force to speed up swim 9534 adc #$01 9535 sta Enemy_Y_MoveForce,x ;set movement force 9536 sta BlooperMoveSpeed,x ;set as movement speed 9537 cmp #$02 9538 bne BSwimE ;if certain horizontal speed, branch to leave 9539 inc BlooperMoveCounter,x ;otherwise increment movement counter 9540 BSwimE: rts 9541 9542 SlowSwim: 9543 pla ;pull 3 LSB of frame counter from the stack 9544 bne NoSSw ;branch to leave, execute code only every eighth frame 9545 lda Enemy_Y_MoveForce,x 9546 sec ;subtract from movement force to slow swim 9547 sbc #$01 9548 sta Enemy_Y_MoveForce,x ;set movement force 9549 sta BlooperMoveSpeed,x ;set as movement speed 9550 bne NoSSw ;if any speed, branch to leave 9551 inc BlooperMoveCounter,x ;otherwise increment movement counter 9552 lda #$02 9553 sta EnemyIntervalTimer,x ;set enemy's timer 9554 NoSSw: rts ;leave 9555 9556 ChkForFloatdown: 9557 lda EnemyIntervalTimer,x ;get enemy timer 9558 beq ChkNearPlayer ;branch if expired 9559 9560 Floatdown: 9561 lda FrameCounter ;get frame counter 9562 lsr ;check for d0 set 9563 bcs NoFD ;branch to leave on every other frame 9564 inc Enemy_Y_Position,x ;otherwise increment vertical coordinate 9565 NoFD: rts ;leave 9566 9567 ChkNearPlayer: 9568 lda Enemy_Y_Position,x ;get vertical coordinate 9569 adc #$10 ;add sixteen pixels 9570 cmp Player_Y_Position ;compare result with player's vertical coordinate 9571 bcc Floatdown ;if modified vertical less than player's, branch 9572 lda #$00 9573 sta BlooperMoveCounter,x ;otherwise nullify movement counter 9574 rts 9575 9576 ;-------------------------------- 9577 9578 MoveBulletBill: 9579 lda Enemy_State,x ;check bullet bill's enemy object state for d5 set 9580 and #%00100000 9581 beq NotDefB ;if not set, continue with movement code 9582 jmp MoveJ_EnemyVertically ;otherwise jump to move defeated bullet bill downwards 9583 NotDefB: lda #$e8 ;set bullet bill's horizontal speed 9584 sta Enemy_X_Speed,x ;and move it accordingly (note: this bullet bill 9585 jmp MoveEnemyHorizontally ;object occurs in frenzy object $17, not from cannons) 9586 9587 ;-------------------------------- 9588 ;$02 - used to hold preset values 9589 ;$03 - used to hold enemy state 9590 9591 SwimCCXMoveData: 9592 .db $40, $80 9593 .db $04, $04 ;residual data, not used 9594 9595 MoveSwimmingCheepCheep: 9596 lda Enemy_State,x ;check cheep-cheep's enemy object state 9597 and #%00100000 ;for d5 set 9598 beq CCSwim ;if not set, continue with movement code 9599 jmp MoveEnemySlowVert ;otherwise jump to move defeated cheep-cheep downwards 9600 CCSwim: sta $03 ;save enemy state in $03 9601 lda Enemy_ID,x ;get enemy identifier 9602 sec 9603 sbc #$0a ;subtract ten for cheep-cheep identifiers 9604 tay ;use as offset 9605 lda SwimCCXMoveData,y ;load value here 9606 sta $02 9607 lda Enemy_X_MoveForce,x ;load horizontal force 9608 sec 9609 sbc $02 ;subtract preset value from horizontal force 9610 sta Enemy_X_MoveForce,x ;store as new horizontal force 9611 lda Enemy_X_Position,x ;get horizontal coordinate 9612 sbc #$00 ;subtract borrow (thus moving it slowly) 9613 sta Enemy_X_Position,x ;and save as new horizontal coordinate 9614 lda Enemy_PageLoc,x 9615 sbc #$00 ;subtract borrow again, this time from the 9616 sta Enemy_PageLoc,x ;page location, then save 9617 lda #$20 9618 sta $02 ;save new value here 9619 cpx #$02 ;check enemy object offset 9620 bcc ExSwCC ;if in first or second slot, branch to leave 9621 lda CheepCheepMoveMFlag,x ;check movement flag 9622 cmp #$10 ;if movement speed set to $00, 9623 bcc CCSwimUpwards ;branch to move upwards 9624 lda Enemy_YMF_Dummy,x 9625 clc 9626 adc $02 ;add preset value to dummy variable to get carry 9627 sta Enemy_YMF_Dummy,x ;and save dummy 9628 lda Enemy_Y_Position,x ;get vertical coordinate 9629 adc $03 ;add carry to it plus enemy state to slowly move it downwards 9630 sta Enemy_Y_Position,x ;save as new vertical coordinate 9631 lda Enemy_Y_HighPos,x 9632 adc #$00 ;add carry to page location and 9633 jmp ChkSwimYPos ;jump to end of movement code 9634 9635 CCSwimUpwards: 9636 lda Enemy_YMF_Dummy,x 9637 sec 9638 sbc $02 ;subtract preset value to dummy variable to get borrow 9639 sta Enemy_YMF_Dummy,x ;and save dummy 9640 lda Enemy_Y_Position,x ;get vertical coordinate 9641 sbc $03 ;subtract borrow to it plus enemy state to slowly move it upwards 9642 sta Enemy_Y_Position,x ;save as new vertical coordinate 9643 lda Enemy_Y_HighPos,x 9644 sbc #$00 ;subtract borrow from page location 9645 9646 ChkSwimYPos: 9647 sta Enemy_Y_HighPos,x ;save new page location here 9648 ldy #$00 ;load movement speed to upwards by default 9649 lda Enemy_Y_Position,x ;get vertical coordinate 9650 sec 9651 sbc CheepCheepOrigYPos,x ;subtract original coordinate from current 9652 bpl YPDiff ;if result positive, skip to next part 9653 ldy #$10 ;otherwise load movement speed to downwards 9654 eor #$ff 9655 clc ;get two's compliment of result 9656 adc #$01 ;to obtain total difference of original vs. current 9657 YPDiff: cmp #$0f ;if difference between original vs. current vertical 9658 bcc ExSwCC ;coordinates < 15 pixels, leave movement speed alone 9659 tya 9660 sta CheepCheepMoveMFlag,x ;otherwise change movement speed 9661 ExSwCC: rts ;leave 9662 9663 ;-------------------------------- 9664 ;$00 - used as counter for firebar parts 9665 ;$01 - used for oscillated high byte of spin state or to hold horizontal adder 9666 ;$02 - used for oscillated high byte of spin state or to hold vertical adder 9667 ;$03 - used for mirror data 9668 ;$04 - used to store player's sprite 1 X coordinate 9669 ;$05 - used to evaluate mirror data 9670 ;$06 - used to store either screen X coordinate or sprite data offset 9671 ;$07 - used to store screen Y coordinate 9672 ;$ed - used to hold maximum length of firebar 9673 ;$ef - used to hold high byte of spinstate 9674 9675 ;horizontal adder is at first byte + high byte of spinstate, 9676 ;vertical adder is same + 8 bytes, two's compliment 9677 ;if greater than $08 for proper oscillation 9678 FirebarPosLookupTbl: 9679 .db $00, $01, $03, $04, $05, $06, $07, $07, $08 9680 .db $00, $03, $06, $09, $0b, $0d, $0e, $0f, $10 9681 .db $00, $04, $09, $0d, $10, $13, $16, $17, $18 9682 .db $00, $06, $0c, $12, $16, $1a, $1d, $1f, $20 9683 .db $00, $07, $0f, $16, $1c, $21, $25, $27, $28 9684 .db $00, $09, $12, $1b, $21, $27, $2c, $2f, $30 9685 .db $00, $0b, $15, $1f, $27, $2e, $33, $37, $38 9686 .db $00, $0c, $18, $24, $2d, $35, $3b, $3e, $40 9687 .db $00, $0e, $1b, $28, $32, $3b, $42, $46, $48 9688 .db $00, $0f, $1f, $2d, $38, $42, $4a, $4e, $50 9689 .db $00, $11, $22, $31, $3e, $49, $51, $56, $58 9690 9691 FirebarMirrorData: 9692 .db $01, $03, $02, $00 9693 9694 FirebarTblOffsets: 9695 .db $00, $09, $12, $1b, $24, $2d 9696 .db $36, $3f, $48, $51, $5a, $63 9697 9698 FirebarYPos: 9699 .db $0c, $18 9700 9701 ProcFirebar: 9702 jsr GetEnemyOffscreenBits ;get offscreen information 9703 lda Enemy_OffscreenBits ;check for d3 set 9704 and #%00001000 ;if so, branch to leave 9705 bne SkipFBar 9706 lda TimerControl ;if master timer control set, branch 9707 bne SusFbar ;ahead of this part 9708 lda FirebarSpinSpeed,x ;load spinning speed of firebar 9709 jsr FirebarSpin ;modify current spinstate 9710 and #%00011111 ;mask out all but 5 LSB 9711 sta FirebarSpinState_High,x ;and store as new high byte of spinstate 9712 SusFbar: lda FirebarSpinState_High,x ;get high byte of spinstate 9713 ldy Enemy_ID,x ;check enemy identifier 9714 cpy #$1f 9715 bcc SetupGFB ;if < $1f (long firebar), branch 9716 cmp #$08 ;check high byte of spinstate 9717 beq SkpFSte ;if eight, branch to change 9718 cmp #$18 9719 bne SetupGFB ;if not at twenty-four branch to not change 9720 SkpFSte: clc 9721 adc #$01 ;add one to spinning thing to avoid horizontal state 9722 sta FirebarSpinState_High,x 9723 SetupGFB: sta $ef ;save high byte of spinning thing, modified or otherwise 9724 jsr RelativeEnemyPosition ;get relative coordinates to screen 9725 jsr GetFirebarPosition ;do a sub here (residual, too early to be used now) 9726 ldy Enemy_SprDataOffset,x ;get OAM data offset 9727 lda Enemy_Rel_YPos ;get relative vertical coordinate 9728 sta Sprite_Y_Position,y ;store as Y in OAM data 9729 sta $07 ;also save here 9730 lda Enemy_Rel_XPos ;get relative horizontal coordinate 9731 sta Sprite_X_Position,y ;store as X in OAM data 9732 sta $06 ;also save here 9733 lda #$01 9734 sta $00 ;set $01 value here (not necessary) 9735 jsr FirebarCollision ;draw fireball part and do collision detection 9736 ldy #$05 ;load value for short firebars by default 9737 lda Enemy_ID,x 9738 cmp #$1f ;are we doing a long firebar? 9739 bcc SetMFbar ;no, branch then 9740 ldy #$0b ;otherwise load value for long firebars 9741 SetMFbar: sty $ed ;store maximum value for length of firebars 9742 lda #$00 9743 sta $00 ;initialize counter here 9744 DrawFbar: lda $ef ;load high byte of spinstate 9745 jsr GetFirebarPosition ;get fireball position data depending on firebar part 9746 jsr DrawFirebar_Collision ;position it properly, draw it and do collision detection 9747 lda $00 ;check which firebar part 9748 cmp #$04 9749 bne NextFbar 9750 ldy DuplicateObj_Offset ;if we arrive at fifth firebar part, 9751 lda Enemy_SprDataOffset,y ;get offset from long firebar and load OAM data offset 9752 sta $06 ;using long firebar offset, then store as new one here 9753 NextFbar: inc $00 ;move onto the next firebar part 9754 lda $00 9755 cmp $ed ;if we end up at the maximum part, go on and leave 9756 bcc DrawFbar ;otherwise go back and do another 9757 SkipFBar: rts 9758 9759 DrawFirebar_Collision: 9760 lda $03 ;store mirror data elsewhere 9761 sta $05 9762 ldy $06 ;load OAM data offset for firebar 9763 lda $01 ;load horizontal adder we got from position loader 9764 lsr $05 ;shift LSB of mirror data 9765 bcs AddHA ;if carry was set, skip this part 9766 eor #$ff 9767 adc #$01 ;otherwise get two's compliment of horizontal adder 9768 AddHA: clc ;add horizontal coordinate relative to screen to 9769 adc Enemy_Rel_XPos ;horizontal adder, modified or otherwise 9770 sta Sprite_X_Position,y ;store as X coordinate here 9771 sta $06 ;store here for now, note offset is saved in Y still 9772 cmp Enemy_Rel_XPos ;compare X coordinate of sprite to original X of firebar 9773 bcs SubtR1 ;if sprite coordinate => original coordinate, branch 9774 lda Enemy_Rel_XPos 9775 sec ;otherwise subtract sprite X from the 9776 sbc $06 ;original one and skip this part 9777 jmp ChkFOfs 9778 SubtR1: sec ;subtract original X from the 9779 sbc Enemy_Rel_XPos ;current sprite X 9780 ChkFOfs: cmp #$59 ;if difference of coordinates within a certain range, 9781 bcc VAHandl ;continue by handling vertical adder 9782 lda #$f8 ;otherwise, load offscreen Y coordinate 9783 bne SetVFbr ;and unconditionally branch to move sprite offscreen 9784 VAHandl: lda Enemy_Rel_YPos ;if vertical relative coordinate offscreen, 9785 cmp #$f8 ;skip ahead of this part and write into sprite Y coordinate 9786 beq SetVFbr 9787 lda $02 ;load vertical adder we got from position loader 9788 lsr $05 ;shift LSB of mirror data one more time 9789 bcs AddVA ;if carry was set, skip this part 9790 eor #$ff 9791 adc #$01 ;otherwise get two's compliment of second part 9792 AddVA: clc ;add vertical coordinate relative to screen to 9793 adc Enemy_Rel_YPos ;the second data, modified or otherwise 9794 SetVFbr: sta Sprite_Y_Position,y ;store as Y coordinate here 9795 sta $07 ;also store here for now 9796 9797 FirebarCollision: 9798 jsr DrawFirebar ;run sub here to draw current tile of firebar 9799 tya ;return OAM data offset and save 9800 pha ;to the stack for now 9801 lda StarInvincibleTimer ;if star mario invincibility timer 9802 ora TimerControl ;or master timer controls set 9803 bne NoColFB ;then skip all of this 9804 sta $05 ;otherwise initialize counter 9805 ldy Player_Y_HighPos 9806 dey ;if player's vertical high byte offscreen, 9807 bne NoColFB ;skip all of this 9808 ldy Player_Y_Position ;get player's vertical position 9809 lda PlayerSize ;get player's size 9810 bne AdjSm ;if player small, branch to alter variables 9811 lda CrouchingFlag 9812 beq BigJp ;if player big and not crouching, jump ahead 9813 AdjSm: inc $05 ;if small or big but crouching, execute this part 9814 inc $05 ;first increment our counter twice (setting $02 as flag) 9815 tya 9816 clc ;then add 24 pixels to the player's 9817 adc #$18 ;vertical coordinate 9818 tay 9819 BigJp: tya ;get vertical coordinate, altered or otherwise, from Y 9820 FBCLoop: sec ;subtract vertical position of firebar 9821 sbc $07 ;from the vertical coordinate of the player 9822 bpl ChkVFBD ;if player lower on the screen than firebar, 9823 eor #$ff ;skip two's compliment part 9824 clc ;otherwise get two's compliment 9825 adc #$01 9826 ChkVFBD: cmp #$08 ;if difference => 8 pixels, skip ahead of this part 9827 bcs Chk2Ofs 9828 lda $06 ;if firebar on far right on the screen, skip this, 9829 cmp #$f0 ;because, really, what's the point? 9830 bcs Chk2Ofs 9831 lda Sprite_X_Position+4 ;get OAM X coordinate for sprite #1 9832 clc 9833 adc #$04 ;add four pixels 9834 sta $04 ;store here 9835 sec ;subtract horizontal coordinate of firebar 9836 sbc $06 ;from the X coordinate of player's sprite 1 9837 bpl ChkFBCl ;if modded X coordinate to the right of firebar 9838 eor #$ff ;skip two's compliment part 9839 clc ;otherwise get two's compliment 9840 adc #$01 9841 ChkFBCl: cmp #$08 ;if difference < 8 pixels, collision, thus branch 9842 bcc ChgSDir ;to process 9843 Chk2Ofs: lda $05 ;if value of $02 was set earlier for whatever reason, 9844 cmp #$02 ;branch to increment OAM offset and leave, no collision 9845 beq NoColFB 9846 ldy $05 ;otherwise get temp here and use as offset 9847 lda Player_Y_Position 9848 clc 9849 adc FirebarYPos,y ;add value loaded with offset to player's vertical coordinate 9850 inc $05 ;then increment temp and jump back 9851 jmp FBCLoop 9852 ChgSDir: ldx #$01 ;set movement direction by default 9853 lda $04 ;if OAM X coordinate of player's sprite 1 9854 cmp $06 ;is greater than horizontal coordinate of firebar 9855 bcs SetSDir ;then do not alter movement direction 9856 inx ;otherwise increment it 9857 SetSDir: stx Enemy_MovingDir ;store movement direction here 9858 ldx #$00 9859 lda $00 ;save value written to $00 to stack 9860 pha 9861 jsr InjurePlayer ;perform sub to hurt or kill player 9862 pla 9863 sta $00 ;get value of $00 from stack 9864 NoColFB: pla ;get OAM data offset 9865 clc ;add four to it and save 9866 adc #$04 9867 sta $06 9868 ldx ObjectOffset ;get enemy object buffer offset and leave 9869 rts 9870 9871 GetFirebarPosition: 9872 pha ;save high byte of spinstate to the stack 9873 and #%00001111 ;mask out low nybble 9874 cmp #$09 9875 bcc GetHAdder ;if lower than $09, branch ahead 9876 eor #%00001111 ;otherwise get two's compliment to oscillate 9877 clc 9878 adc #$01 9879 GetHAdder: sta $01 ;store result, modified or not, here 9880 ldy $00 ;load number of firebar ball where we're at 9881 lda FirebarTblOffsets,y ;load offset to firebar position data 9882 clc 9883 adc $01 ;add oscillated high byte of spinstate 9884 tay ;to offset here and use as new offset 9885 lda FirebarPosLookupTbl,y ;get data here and store as horizontal adder 9886 sta $01 9887 pla ;pull whatever was in A from the stack 9888 pha ;save it again because we still need it 9889 clc 9890 adc #$08 ;add eight this time, to get vertical adder 9891 and #%00001111 ;mask out high nybble 9892 cmp #$09 ;if lower than $09, branch ahead 9893 bcc GetVAdder 9894 eor #%00001111 ;otherwise get two's compliment 9895 clc 9896 adc #$01 9897 GetVAdder: sta $02 ;store result here 9898 ldy $00 9899 lda FirebarTblOffsets,y ;load offset to firebar position data again 9900 clc 9901 adc $02 ;this time add value in $02 to offset here and use as offset 9902 tay 9903 lda FirebarPosLookupTbl,y ;get data here and store as vertica adder 9904 sta $02 9905 pla ;pull out whatever was in A one last time 9906 lsr ;divide by eight or shift three to the right 9907 lsr 9908 lsr 9909 tay ;use as offset 9910 lda FirebarMirrorData,y ;load mirroring data here 9911 sta $03 ;store 9912 rts 9913 9914 ;-------------------------------- 9915 9916 PRandomSubtracter: 9917 .db $f8, $a0, $70, $bd, $00 9918 9919 FlyCCBPriority: 9920 .db $20, $20, $20, $00, $00 9921 9922 MoveFlyingCheepCheep: 9923 lda Enemy_State,x ;check cheep-cheep's enemy state 9924 and #%00100000 ;for d5 set 9925 beq FlyCC ;branch to continue code if not set 9926 lda #$00 9927 sta Enemy_SprAttrib,x ;otherwise clear sprite attributes 9928 jmp MoveJ_EnemyVertically ;and jump to move defeated cheep-cheep downwards 9929 FlyCC: jsr MoveEnemyHorizontally ;move cheep-cheep horizontally based on speed and force 9930 ldy #$0d ;set vertical movement amount 9931 lda #$05 ;set maximum speed 9932 jsr SetXMoveAmt ;branch to impose gravity on flying cheep-cheep 9933 lda Enemy_Y_MoveForce,x 9934 lsr ;get vertical movement force and 9935 lsr ;move high nybble to low 9936 lsr 9937 lsr 9938 tay ;save as offset (note this tends to go into reach of code) 9939 lda Enemy_Y_Position,x ;get vertical position 9940 sec ;subtract pseudorandom value based on offset from position 9941 sbc PRandomSubtracter,y 9942 bpl AddCCF ;if result within top half of screen, skip this part 9943 eor #$ff 9944 clc ;otherwise get two's compliment 9945 adc #$01 9946 AddCCF: cmp #$08 ;if result or two's compliment greater than eight, 9947 bcs BPGet ;skip to the end without changing movement force 9948 lda Enemy_Y_MoveForce,x 9949 clc 9950 adc #$10 ;otherwise add to it 9951 sta Enemy_Y_MoveForce,x 9952 lsr ;move high nybble to low again 9953 lsr 9954 lsr 9955 lsr 9956 tay 9957 BPGet: lda FlyCCBPriority,y ;load bg priority data and store (this is very likely 9958 sta Enemy_SprAttrib,x ;broken or residual code, value is overwritten before 9959 rts ;drawing it next frame), then leave 9960 9961 ;-------------------------------- 9962 ;$00 - used to hold horizontal difference 9963 ;$01-$03 - used to hold difference adjusters 9964 9965 LakituDiffAdj: 9966 .db $15, $30, $40 9967 9968 MoveLakitu: 9969 lda Enemy_State,x ;check lakitu's enemy state 9970 and #%00100000 ;for d5 set 9971 beq ChkLS ;if not set, continue with code 9972 jmp MoveD_EnemyVertically ;otherwise jump to move defeated lakitu downwards 9973 ChkLS: lda Enemy_State,x ;if lakitu's enemy state not set at all, 9974 beq Fr12S ;go ahead and continue with code 9975 lda #$00 9976 sta LakituMoveDirection,x ;otherwise initialize moving direction to move to left 9977 sta EnemyFrenzyBuffer ;initialize frenzy buffer 9978 lda #$10 9979 bne SetLSpd ;load horizontal speed and do unconditional branch 9980 Fr12S: lda #Spiny 9981 sta EnemyFrenzyBuffer ;set spiny identifier in frenzy buffer 9982 ldy #$02 9983 LdLDa: lda LakituDiffAdj,y ;load values 9984 sta $0001,y ;store in zero page 9985 dey 9986 bpl LdLDa ;do this until all values are stired 9987 jsr PlayerLakituDiff ;execute sub to set speed and create spinys 9988 SetLSpd: sta LakituMoveSpeed,x ;set movement speed returned from sub 9989 ldy #$01 ;set moving direction to right by default 9990 lda LakituMoveDirection,x 9991 and #$01 ;get LSB of moving direction 9992 bne SetLMov ;if set, branch to the end to use moving direction 9993 lda LakituMoveSpeed,x 9994 eor #$ff ;get two's compliment of moving speed 9995 clc 9996 adc #$01 9997 sta LakituMoveSpeed,x ;store as new moving speed 9998 iny ;increment moving direction to left 9999 SetLMov: sty Enemy_MovingDir,x ;store moving direction 10000 jmp MoveEnemyHorizontally ;move lakitu horizontally 10001 10002 PlayerLakituDiff: 10003 ldy #$00 ;set Y for default value 10004 jsr PlayerEnemyDiff ;get horizontal difference between enemy and player 10005 bpl ChkLakDif ;branch if enemy is to the right of the player 10006 iny ;increment Y for left of player 10007 lda $00 10008 eor #$ff ;get two's compliment of low byte of horizontal difference 10009 clc 10010 adc #$01 ;store two's compliment as horizontal difference 10011 sta $00 10012 ChkLakDif: lda $00 ;get low byte of horizontal difference 10013 cmp #$3c ;if within a certain distance of player, branch 10014 bcc ChkPSpeed 10015 lda #$3c ;otherwise set maximum distance 10016 sta $00 10017 lda Enemy_ID,x ;check if lakitu is in our current enemy slot 10018 cmp #Lakitu 10019 bne ChkPSpeed ;if not, branch elsewhere 10020 tya ;compare contents of Y, now in A 10021 cmp LakituMoveDirection,x ;to what is being used as horizontal movement direction 10022 beq ChkPSpeed ;if moving toward the player, branch, do not alter 10023 lda LakituMoveDirection,x ;if moving to the left beyond maximum distance, 10024 beq SetLMovD ;branch and alter without delay 10025 dec LakituMoveSpeed,x ;decrement horizontal speed 10026 lda LakituMoveSpeed,x ;if horizontal speed not yet at zero, branch to leave 10027 bne ExMoveLak 10028 SetLMovD: tya ;set horizontal direction depending on horizontal 10029 sta LakituMoveDirection,x ;difference between enemy and player if necessary 10030 ChkPSpeed: lda $00 10031 and #%00111100 ;mask out all but four bits in the middle 10032 lsr ;divide masked difference by four 10033 lsr 10034 sta $00 ;store as new value 10035 ldy #$00 ;init offset 10036 lda Player_X_Speed 10037 beq SubDifAdj ;if player not moving horizontally, branch 10038 lda ScrollAmount 10039 beq SubDifAdj ;if scroll speed not set, branch to same place 10040 iny ;otherwise increment offset 10041 lda Player_X_Speed 10042 cmp #$19 ;if player not running, branch 10043 bcc ChkSpinyO 10044 lda ScrollAmount 10045 cmp #$02 ;if scroll speed below a certain amount, branch 10046 bcc ChkSpinyO ;to same place 10047 iny ;otherwise increment once more 10048 ChkSpinyO: lda Enemy_ID,x ;check for spiny object 10049 cmp #Spiny 10050 bne ChkEmySpd ;branch if not found 10051 lda Player_X_Speed ;if player not moving, skip this part 10052 bne SubDifAdj 10053 ChkEmySpd: lda Enemy_Y_Speed,x ;check vertical speed 10054 bne SubDifAdj ;branch if nonzero 10055 ldy #$00 ;otherwise reinit offset 10056 SubDifAdj: lda $0001,y ;get one of three saved values from earlier 10057 ldy $00 ;get saved horizontal difference 10058 SPixelLak: sec ;subtract one for each pixel of horizontal difference 10059 sbc #$01 ;from one of three saved values 10060 dey 10061 bpl SPixelLak ;branch until all pixels are subtracted, to adjust difference 10062 ExMoveLak: rts ;leave!!! 10063 10064 ;------------------------------------------------------------------------------------- 10065 ;$04-$05 - used to store name table address in little endian order 10066 10067 BridgeCollapseData: 10068 .db $1a ;axe 10069 .db $58 ;chain 10070 .db $98, $96, $94, $92, $90, $8e, $8c ;bridge 10071 .db $8a, $88, $86, $84, $82, $80 10072 10073 BridgeCollapse: 10074 ldx BowserFront_Offset ;get enemy offset for bowser 10075 lda Enemy_ID,x ;check enemy object identifier for bowser 10076 cmp #Bowser ;if not found, branch ahead, 10077 bne SetM2 ;metatile removal not necessary 10078 stx ObjectOffset ;store as enemy offset here 10079 lda Enemy_State,x ;if bowser in normal state, skip all of this 10080 beq RemoveBridge 10081 and #%01000000 ;if bowser's state has d6 clear, skip to silence music 10082 beq SetM2 10083 lda Enemy_Y_Position,x ;check bowser's vertical coordinate 10084 cmp #$e0 ;if bowser not yet low enough, skip this part ahead 10085 bcc MoveD_Bowser 10086 SetM2: lda #Silence ;silence music 10087 sta EventMusicQueue 10088 inc OperMode_Task ;move onto next secondary mode in autoctrl mode 10089 jmp KillAllEnemies ;jump to empty all enemy slots and then leave 10090 10091 MoveD_Bowser: 10092 jsr MoveEnemySlowVert ;do a sub to move bowser downwards 10093 jmp BowserGfxHandler ;jump to draw bowser's front and rear, then leave 10094 10095 RemoveBridge: 10096 dec BowserFeetCounter ;decrement timer to control bowser's feet 10097 bne NoBFall ;if not expired, skip all of this 10098 lda #$04 10099 sta BowserFeetCounter ;otherwise, set timer now 10100 lda BowserBodyControls 10101 eor #$01 ;invert bit to control bowser's feet 10102 sta BowserBodyControls 10103 lda #$22 ;put high byte of name table address here for now 10104 sta $05 10105 ldy BridgeCollapseOffset ;get bridge collapse offset here 10106 lda BridgeCollapseData,y ;load low byte of name table address and store here 10107 sta $04 10108 ldy VRAM_Buffer1_Offset ;increment vram buffer offset 10109 iny 10110 ldx #$0c ;set offset for tile data for sub to draw blank metatile 10111 jsr RemBridge ;do sub here to remove bowser's bridge metatiles 10112 ldx ObjectOffset ;get enemy offset 10113 jsr MoveVOffset ;set new vram buffer offset 10114 lda #Sfx_Blast ;load the fireworks/gunfire sound into the square 2 sfx 10115 sta Square2SoundQueue ;queue while at the same time loading the brick 10116 lda #Sfx_BrickShatter ;shatter sound into the noise sfx queue thus 10117 sta NoiseSoundQueue ;producing the unique sound of the bridge collapsing 10118 inc BridgeCollapseOffset ;increment bridge collapse offset 10119 lda BridgeCollapseOffset 10120 cmp #$0f ;if bridge collapse offset has not yet reached 10121 bne NoBFall ;the end, go ahead and skip this part 10122 jsr InitVStf ;initialize whatever vertical speed bowser has 10123 lda #%01000000 10124 sta Enemy_State,x ;set bowser's state to one of defeated states (d6 set) 10125 lda #Sfx_BowserFall 10126 sta Square2SoundQueue ;play bowser defeat sound 10127 NoBFall: jmp BowserGfxHandler ;jump to code that draws bowser 10128 10129 ;-------------------------------- 10130 10131 PRandomRange: 10132 .db $21, $41, $11, $31 10133 10134 RunBowser: 10135 lda Enemy_State,x ;if d5 in enemy state is not set 10136 and #%00100000 ;then branch elsewhere to run bowser 10137 beq BowserControl 10138 lda Enemy_Y_Position,x ;otherwise check vertical position 10139 cmp #$e0 ;if above a certain point, branch to move defeated bowser 10140 bcc MoveD_Bowser ;otherwise proceed to KillAllEnemies 10141 10142 KillAllEnemies: 10143 ldx #$04 ;start with last enemy slot 10144 KillLoop: jsr EraseEnemyObject ;branch to kill enemy objects 10145 dex ;move onto next enemy slot 10146 bpl KillLoop ;do this until all slots are emptied 10147 sta EnemyFrenzyBuffer ;empty frenzy buffer 10148 ldx ObjectOffset ;get enemy object offset and leave 10149 rts 10150 10151 BowserControl: 10152 lda #$00 10153 sta EnemyFrenzyBuffer ;empty frenzy buffer 10154 lda TimerControl ;if master timer control not set, 10155 beq ChkMouth ;skip jump and execute code here 10156 jmp SkipToFB ;otherwise, jump over a bunch of code 10157 ChkMouth: lda BowserBodyControls ;check bowser's mouth 10158 bpl FeetTmr ;if bit clear, go ahead with code here 10159 jmp HammerChk ;otherwise skip a whole section starting here 10160 FeetTmr: dec BowserFeetCounter ;decrement timer to control bowser's feet 10161 bne ResetMDr ;if not expired, skip this part 10162 lda #$20 ;otherwise, reset timer 10163 sta BowserFeetCounter 10164 lda BowserBodyControls ;and invert bit used 10165 eor #%00000001 ;to control bowser's feet 10166 sta BowserBodyControls 10167 ResetMDr: lda FrameCounter ;check frame counter 10168 and #%00001111 ;if not on every sixteenth frame, skip 10169 bne B_FaceP ;ahead to continue code 10170 lda #$02 ;otherwise reset moving/facing direction every 10171 sta Enemy_MovingDir,x ;sixteen frames 10172 B_FaceP: lda EnemyFrameTimer,x ;if timer set here expired, 10173 beq GetPRCmp ;branch to next section 10174 jsr PlayerEnemyDiff ;get horizontal difference between player and bowser, 10175 bpl GetPRCmp ;and branch if bowser to the right of the player 10176 lda #$01 10177 sta Enemy_MovingDir,x ;set bowser to move and face to the right 10178 lda #$02 10179 sta BowserMovementSpeed ;set movement speed 10180 lda #$20 10181 sta EnemyFrameTimer,x ;set timer here 10182 sta BowserFireBreathTimer ;set timer used for bowser's flame 10183 lda Enemy_X_Position,x 10184 cmp #$c8 ;if bowser to the right past a certain point, 10185 bcs HammerChk ;skip ahead to some other section 10186 GetPRCmp: lda FrameCounter ;get frame counter 10187 and #%00000011 10188 bne HammerChk ;execute this code every fourth frame, otherwise branch 10189 lda Enemy_X_Position,x 10190 cmp BowserOrigXPos ;if bowser not at original horizontal position, 10191 bne GetDToO ;branch to skip this part 10192 lda PseudoRandomBitReg,x 10193 and #%00000011 ;get pseudorandom offset 10194 tay 10195 lda PRandomRange,y ;load value using pseudorandom offset 10196 sta MaxRangeFromOrigin ;and store here 10197 GetDToO: lda Enemy_X_Position,x 10198 clc ;add movement speed to bowser's horizontal 10199 adc BowserMovementSpeed ;coordinate and save as new horizontal position 10200 sta Enemy_X_Position,x 10201 ldy Enemy_MovingDir,x 10202 cpy #$01 ;if bowser moving and facing to the right, skip ahead 10203 beq HammerChk 10204 ldy #$ff ;set default movement speed here (move left) 10205 sec ;get difference of current vs. original 10206 sbc BowserOrigXPos ;horizontal position 10207 bpl CompDToO ;if current position to the right of original, skip ahead 10208 eor #$ff 10209 clc ;get two's compliment 10210 adc #$01 10211 ldy #$01 ;set alternate movement speed here (move right) 10212 CompDToO: cmp MaxRangeFromOrigin ;compare difference with pseudorandom value 10213 bcc HammerChk ;if difference < pseudorandom value, leave speed alone 10214 sty BowserMovementSpeed ;otherwise change bowser's movement speed 10215 HammerChk: lda EnemyFrameTimer,x ;if timer set here not expired yet, skip ahead to 10216 bne MakeBJump ;some other section of code 10217 jsr MoveEnemySlowVert ;otherwise start by moving bowser downwards 10218 lda WorldNumber ;check world number 10219 cmp #World6 10220 bcc SetHmrTmr ;if world 1-5, skip this part (not time to throw hammers yet) 10221 lda FrameCounter 10222 and #%00000011 ;check to see if it's time to execute sub 10223 bne SetHmrTmr ;if not, skip sub, otherwise 10224 jsr SpawnHammerObj ;execute sub on every fourth frame to spawn misc object (hammer) 10225 SetHmrTmr: lda Enemy_Y_Position,x ;get current vertical position 10226 cmp #$80 ;if still above a certain point 10227 bcc ChkFireB ;then skip to world number check for flames 10228 lda PseudoRandomBitReg,x 10229 and #%00000011 ;get pseudorandom offset 10230 tay 10231 lda PRandomRange,y ;get value using pseudorandom offset 10232 sta EnemyFrameTimer,x ;set for timer here 10233 SkipToFB: jmp ChkFireB ;jump to execute flames code 10234 MakeBJump: cmp #$01 ;if timer not yet about to expire, 10235 bne ChkFireB ;skip ahead to next part 10236 dec Enemy_Y_Position,x ;otherwise decrement vertical coordinate 10237 jsr InitVStf ;initialize movement amount 10238 lda #$fe 10239 sta Enemy_Y_Speed,x ;set vertical speed to move bowser upwards 10240 ChkFireB: lda WorldNumber ;check world number here 10241 cmp #World8 ;world 8? 10242 beq SpawnFBr ;if so, execute this part here 10243 cmp #World6 ;world 6-7? 10244 bcs BowserGfxHandler ;if so, skip this part here 10245 SpawnFBr: lda BowserFireBreathTimer ;check timer here 10246 bne BowserGfxHandler ;if not expired yet, skip all of this 10247 lda #$20 10248 sta BowserFireBreathTimer ;set timer here 10249 lda BowserBodyControls 10250 eor #%10000000 ;invert bowser's mouth bit to open 10251 sta BowserBodyControls ;and close bowser's mouth 10252 bmi ChkFireB ;if bowser's mouth open, loop back 10253 jsr SetFlameTimer ;get timing for bowser's flame 10254 ldy SecondaryHardMode 10255 beq SetFBTmr ;if secondary hard mode flag not set, skip this 10256 sec 10257 sbc #$10 ;otherwise subtract from value in A 10258 SetFBTmr: sta BowserFireBreathTimer ;set value as timer here 10259 lda #BowserFlame ;put bowser's flame identifier 10260 sta EnemyFrenzyBuffer ;in enemy frenzy buffer 10261 10262 ;-------------------------------- 10263 10264 BowserGfxHandler: 10265 jsr ProcessBowserHalf ;do a sub here to process bowser's front 10266 ldy #$10 ;load default value here to position bowser's rear 10267 lda Enemy_MovingDir,x ;check moving direction 10268 lsr 10269 bcc CopyFToR ;if moving left, use default 10270 ldy #$f0 ;otherwise load alternate positioning value here 10271 CopyFToR: tya ;move bowser's rear object position value to A 10272 clc 10273 adc Enemy_X_Position,x ;add to bowser's front object horizontal coordinate 10274 ldy DuplicateObj_Offset ;get bowser's rear object offset 10275 sta Enemy_X_Position,y ;store A as bowser's rear horizontal coordinate 10276 lda Enemy_Y_Position,x 10277 clc ;add eight pixels to bowser's front object 10278 adc #$08 ;vertical coordinate and store as vertical coordinate 10279 sta Enemy_Y_Position,y ;for bowser's rear 10280 lda Enemy_State,x 10281 sta Enemy_State,y ;copy enemy state directly from front to rear 10282 lda Enemy_MovingDir,x 10283 sta Enemy_MovingDir,y ;copy moving direction also 10284 lda ObjectOffset ;save enemy object offset of front to stack 10285 pha 10286 ldx DuplicateObj_Offset ;put enemy object offset of rear as current 10287 stx ObjectOffset 10288 lda #Bowser ;set bowser's enemy identifier 10289 sta Enemy_ID,x ;store in bowser's rear object 10290 jsr ProcessBowserHalf ;do a sub here to process bowser's rear 10291 pla 10292 sta ObjectOffset ;get original enemy object offset 10293 tax 10294 lda #$00 ;nullify bowser's front/rear graphics flag 10295 sta BowserGfxFlag 10296 ExBGfxH: rts ;leave! 10297 10298 ProcessBowserHalf: 10299 inc BowserGfxFlag ;increment bowser's graphics flag, then run subroutines 10300 jsr RunRetainerObj ;to get offscreen bits, relative position and draw bowser (finally!) 10301 lda Enemy_State,x 10302 bne ExBGfxH ;if either enemy object not in normal state, branch to leave 10303 lda #$0a 10304 sta Enemy_BoundBoxCtrl,x ;set bounding box size control 10305 jsr GetEnemyBoundBox ;get bounding box coordinates 10306 jmp PlayerEnemyCollision ;do player-to-enemy collision detection 10307 10308 ;------------------------------------------------------------------------------------- 10309 ;$00 - used to hold movement force and tile number 10310 ;$01 - used to hold sprite attribute data 10311 10312 FlameTimerData: 10313 .db $bf, $40, $bf, $bf, $bf, $40, $40, $bf 10314 10315 SetFlameTimer: 10316 ldy BowserFlameTimerCtrl ;load counter as offset 10317 inc BowserFlameTimerCtrl ;increment 10318 lda BowserFlameTimerCtrl ;mask out all but 3 LSB 10319 and #%00000111 ;to keep in range of 0-7 10320 sta BowserFlameTimerCtrl 10321 lda FlameTimerData,y ;load value to be used then leave 10322 ExFl: rts 10323 10324 ProcBowserFlame: 10325 lda TimerControl ;if master timer control flag set, 10326 bne SetGfxF ;skip all of this 10327 lda #$40 ;load default movement force 10328 ldy SecondaryHardMode 10329 beq SFlmX ;if secondary hard mode flag not set, use default 10330 lda #$60 ;otherwise load alternate movement force to go faster 10331 SFlmX: sta $00 ;store value here 10332 lda Enemy_X_MoveForce,x 10333 sec ;subtract value from movement force 10334 sbc $00 10335 sta Enemy_X_MoveForce,x ;save new value 10336 lda Enemy_X_Position,x 10337 sbc #$01 ;subtract one from horizontal position to move 10338 sta Enemy_X_Position,x ;to the left 10339 lda Enemy_PageLoc,x 10340 sbc #$00 ;subtract borrow from page location 10341 sta Enemy_PageLoc,x 10342 ldy BowserFlamePRandomOfs,x ;get some value here and use as offset 10343 lda Enemy_Y_Position,x ;load vertical coordinate 10344 cmp FlameYPosData,y ;compare against coordinate data using $0417,x as offset 10345 beq SetGfxF ;if equal, branch and do not modify coordinate 10346 clc 10347 adc Enemy_Y_MoveForce,x ;otherwise add value here to coordinate and store 10348 sta Enemy_Y_Position,x ;as new vertical coordinate 10349 SetGfxF: jsr RelativeEnemyPosition ;get new relative coordinates 10350 lda Enemy_State,x ;if bowser's flame not in normal state, 10351 bne ExFl ;branch to leave 10352 lda #$51 ;otherwise, continue 10353 sta $00 ;write first tile number 10354 ldy #$02 ;load attributes without vertical flip by default 10355 lda FrameCounter 10356 and #%00000010 ;invert vertical flip bit every 2 frames 10357 beq FlmeAt ;if d1 not set, write default value 10358 ldy #$82 ;otherwise write value with vertical flip bit set 10359 FlmeAt: sty $01 ;set bowser's flame sprite attributes here 10360 ldy Enemy_SprDataOffset,x ;get OAM data offset 10361 ldx #$00 10362 10363 DrawFlameLoop: 10364 lda Enemy_Rel_YPos ;get Y relative coordinate of current enemy object 10365 sta Sprite_Y_Position,y ;write into Y coordinate of OAM data 10366 lda $00 10367 sta Sprite_Tilenumber,y ;write current tile number into OAM data 10368 inc $00 ;increment tile number to draw more bowser's flame 10369 lda $01 10370 sta Sprite_Attributes,y ;write saved attributes into OAM data 10371 lda Enemy_Rel_XPos 10372 sta Sprite_X_Position,y ;write X relative coordinate of current enemy object 10373 clc 10374 adc #$08 10375 sta Enemy_Rel_XPos ;then add eight to it and store 10376 iny 10377 iny 10378 iny 10379 iny ;increment Y four times to move onto the next OAM 10380 inx ;move onto the next OAM, and branch if three 10381 cpx #$03 ;have not yet been done 10382 bcc DrawFlameLoop 10383 ldx ObjectOffset ;reload original enemy offset 10384 jsr GetEnemyOffscreenBits ;get offscreen information 10385 ldy Enemy_SprDataOffset,x ;get OAM data offset 10386 lda Enemy_OffscreenBits ;get enemy object offscreen bits 10387 lsr ;move d0 to carry and result to stack 10388 pha 10389 bcc M3FOfs ;branch if carry not set 10390 lda #$f8 ;otherwise move sprite offscreen, this part likely 10391 sta Sprite_Y_Position+12,y ;residual since flame is only made of three sprites 10392 M3FOfs: pla ;get bits from stack 10393 lsr ;move d1 to carry and move bits back to stack 10394 pha 10395 bcc M2FOfs ;branch if carry not set again 10396 lda #$f8 ;otherwise move third sprite offscreen 10397 sta Sprite_Y_Position+8,y 10398 M2FOfs: pla ;get bits from stack again 10399 lsr ;move d2 to carry and move bits back to stack again 10400 pha 10401 bcc M1FOfs ;branch if carry not set yet again 10402 lda #$f8 ;otherwise move second sprite offscreen 10403 sta Sprite_Y_Position+4,y 10404 M1FOfs: pla ;get bits from stack one last time 10405 lsr ;move d3 to carry 10406 bcc ExFlmeD ;branch if carry not set one last time 10407 lda #$f8 10408 sta Sprite_Y_Position,y ;otherwise move first sprite offscreen 10409 ExFlmeD: rts ;leave 10410 10411 ;-------------------------------- 10412 10413 RunFireworks: 10414 dec ExplosionTimerCounter,x ;decrement explosion timing counter here 10415 bne SetupExpl ;if not expired, skip this part 10416 lda #$08 10417 sta ExplosionTimerCounter,x ;reset counter 10418 inc ExplosionGfxCounter,x ;increment explosion graphics counter 10419 lda ExplosionGfxCounter,x 10420 cmp #$03 ;check explosion graphics counter 10421 bcs FireworksSoundScore ;if at a certain point, branch to kill this object 10422 SetupExpl: jsr RelativeEnemyPosition ;get relative coordinates of explosion 10423 lda Enemy_Rel_YPos ;copy relative coordinates 10424 sta Fireball_Rel_YPos ;from the enemy object to the fireball object 10425 lda Enemy_Rel_XPos ;first vertical, then horizontal 10426 sta Fireball_Rel_XPos 10427 ldy Enemy_SprDataOffset,x ;get OAM data offset 10428 lda ExplosionGfxCounter,x ;get explosion graphics counter 10429 jsr DrawExplosion_Fireworks ;do a sub to draw the explosion then leave 10430 rts 10431 10432 FireworksSoundScore: 10433 lda #$00 ;disable enemy buffer flag 10434 sta Enemy_Flag,x 10435 lda #Sfx_Blast ;play fireworks/gunfire sound 10436 sta Square2SoundQueue 10437 lda #$05 ;set part of score modifier for 500 points 10438 sta DigitModifier+4 10439 jmp EndAreaPoints ;jump to award points accordingly then leave 10440 10441 ;-------------------------------- 10442 10443 StarFlagYPosAdder: 10444 .db $00, $00, $08, $08 10445 10446 StarFlagXPosAdder: 10447 .db $00, $08, $00, $08 10448 10449 StarFlagTileData: 10450 .db $54, $55, $56, $57 10451 10452 RunStarFlagObj: 10453 lda #$00 ;initialize enemy frenzy buffer 10454 sta EnemyFrenzyBuffer 10455 lda StarFlagTaskControl ;check star flag object task number here 10456 cmp #$05 ;if greater than 5, branch to exit 10457 bcs StarFlagExit 10458 jsr JumpEngine ;otherwise jump to appropriate sub 10459 10460 .dw StarFlagExit 10461 .dw GameTimerFireworks 10462 .dw AwardGameTimerPoints 10463 .dw RaiseFlagSetoffFWorks 10464 .dw DelayToAreaEnd 10465 10466 GameTimerFireworks: 10467 ldy #$05 ;set default state for star flag object 10468 lda GameTimerDisplay+2 ;get game timer's last digit 10469 cmp #$01 10470 beq SetFWC ;if last digit of game timer set to 1, skip ahead 10471 ldy #$03 ;otherwise load new value for state 10472 cmp #$03 10473 beq SetFWC ;if last digit of game timer set to 3, skip ahead 10474 ldy #$00 ;otherwise load one more potential value for state 10475 cmp #$06 10476 beq SetFWC ;if last digit of game timer set to 6, skip ahead 10477 lda #$ff ;otherwise set value for no fireworks 10478 SetFWC: sta FireworksCounter ;set fireworks counter here 10479 sty Enemy_State,x ;set whatever state we have in star flag object 10480 10481 IncrementSFTask1: 10482 inc StarFlagTaskControl ;increment star flag object task number 10483 10484 StarFlagExit: 10485 rts ;leave 10486 10487 AwardGameTimerPoints: 10488 lda GameTimerDisplay ;check all game timer digits for any intervals left 10489 ora GameTimerDisplay+1 10490 ora GameTimerDisplay+2 10491 beq IncrementSFTask1 ;if no time left on game timer at all, branch to next task 10492 lda FrameCounter 10493 and #%00000100 ;check frame counter for d2 set (skip ahead 10494 beq NoTTick ;for four frames every four frames) branch if not set 10495 lda #Sfx_TimerTick 10496 sta Square2SoundQueue ;load timer tick sound 10497 NoTTick: ldy #$23 ;set offset here to subtract from game timer's last digit 10498 lda #$ff ;set adder here to $ff, or -1, to subtract one 10499 sta DigitModifier+5 ;from the last digit of the game timer 10500 jsr DigitsMathRoutine ;subtract digit 10501 lda #$05 ;set now to add 50 points 10502 sta DigitModifier+5 ;per game timer interval subtracted 10503 10504 EndAreaPoints: 10505 ldy #$0b ;load offset for mario's score by default 10506 lda CurrentPlayer ;check player on the screen 10507 beq ELPGive ;if mario, do not change 10508 ldy #$11 ;otherwise load offset for luigi's score 10509 ELPGive: jsr DigitsMathRoutine ;award 50 points per game timer interval 10510 lda CurrentPlayer ;get player on the screen (or 500 points per 10511 asl ;fireworks explosion if branched here from there) 10512 asl ;shift to high nybble 10513 asl 10514 asl 10515 ora #%00000100 ;add four to set nybble for game timer 10516 jmp UpdateNumber ;jump to print the new score and game timer 10517 10518 RaiseFlagSetoffFWorks: 10519 lda Enemy_Y_Position,x ;check star flag's vertical position 10520 cmp #$72 ;against preset value 10521 bcc SetoffF ;if star flag higher vertically, branch to other code 10522 dec Enemy_Y_Position,x ;otherwise, raise star flag by one pixel 10523 jmp DrawStarFlag ;and skip this part here 10524 SetoffF: lda FireworksCounter ;check fireworks counter 10525 beq DrawFlagSetTimer ;if no fireworks left to go off, skip this part 10526 bmi DrawFlagSetTimer ;if no fireworks set to go off, skip this part 10527 lda #Fireworks 10528 sta EnemyFrenzyBuffer ;otherwise set fireworks object in frenzy queue 10529 10530 DrawStarFlag: 10531 jsr RelativeEnemyPosition ;get relative coordinates of star flag 10532 ldy Enemy_SprDataOffset,x ;get OAM data offset 10533 ldx #$03 ;do four sprites 10534 DSFLoop: lda Enemy_Rel_YPos ;get relative vertical coordinate 10535 clc 10536 adc StarFlagYPosAdder,x ;add Y coordinate adder data 10537 sta Sprite_Y_Position,y ;store as Y coordinate 10538 lda StarFlagTileData,x ;get tile number 10539 sta Sprite_Tilenumber,y ;store as tile number 10540 lda #$22 ;set palette and background priority bits 10541 sta Sprite_Attributes,y ;store as attributes 10542 lda Enemy_Rel_XPos ;get relative horizontal coordinate 10543 clc 10544 adc StarFlagXPosAdder,x ;add X coordinate adder data 10545 sta Sprite_X_Position,y ;store as X coordinate 10546 iny 10547 iny ;increment OAM data offset four bytes 10548 iny ;for next sprite 10549 iny 10550 dex ;move onto next sprite 10551 bpl DSFLoop ;do this until all sprites are done 10552 ldx ObjectOffset ;get enemy object offset and leave 10553 rts 10554 10555 DrawFlagSetTimer: 10556 jsr DrawStarFlag ;do sub to draw star flag 10557 lda #$06 10558 sta EnemyIntervalTimer,x ;set interval timer here 10559 10560 IncrementSFTask2: 10561 inc StarFlagTaskControl ;move onto next task 10562 rts 10563 10564 DelayToAreaEnd: 10565 jsr DrawStarFlag ;do sub to draw star flag 10566 lda EnemyIntervalTimer,x ;if interval timer set in previous task 10567 bne StarFlagExit2 ;not yet expired, branch to leave 10568 lda EventMusicBuffer ;if event music buffer empty, 10569 beq IncrementSFTask2 ;branch to increment task 10570 10571 StarFlagExit2: 10572 rts ;otherwise leave 10573 10574 ;-------------------------------- 10575 ;$00 - used to store horizontal difference between player and piranha plant 10576 10577 MovePiranhaPlant: 10578 lda Enemy_State,x ;check enemy state 10579 bne PutinPipe ;if set at all, branch to leave 10580 lda EnemyFrameTimer,x ;check enemy's timer here 10581 bne PutinPipe ;branch to end if not yet expired 10582 lda PiranhaPlant_MoveFlag,x ;check movement flag 10583 bne SetupToMovePPlant ;if moving, skip to part ahead 10584 lda PiranhaPlant_Y_Speed,x ;if currently rising, branch 10585 bmi ReversePlantSpeed ;to move enemy upwards out of pipe 10586 jsr PlayerEnemyDiff ;get horizontal difference between player and 10587 bpl ChkPlayerNearPipe ;piranha plant, and branch if enemy to right of player 10588 lda $00 ;otherwise get saved horizontal difference 10589 eor #$ff 10590 clc ;and change to two's compliment 10591 adc #$01 10592 sta $00 ;save as new horizontal difference 10593 10594 ChkPlayerNearPipe: 10595 lda $00 ;get saved horizontal difference 10596 cmp #$21 10597 bcc PutinPipe ;if player within a certain distance, branch to leave 10598 10599 ReversePlantSpeed: 10600 lda PiranhaPlant_Y_Speed,x ;get vertical speed 10601 eor #$ff 10602 clc ;change to two's compliment 10603 adc #$01 10604 sta PiranhaPlant_Y_Speed,x ;save as new vertical speed 10605 inc PiranhaPlant_MoveFlag,x ;increment to set movement flag 10606 10607 SetupToMovePPlant: 10608 lda PiranhaPlantDownYPos,x ;get original vertical coordinate (lowest point) 10609 ldy PiranhaPlant_Y_Speed,x ;get vertical speed 10610 bpl RiseFallPiranhaPlant ;branch if moving downwards 10611 lda PiranhaPlantUpYPos,x ;otherwise get other vertical coordinate (highest point) 10612 10613 RiseFallPiranhaPlant: 10614 sta $00 ;save vertical coordinate here 10615 lda FrameCounter ;get frame counter 10616 lsr 10617 bcc PutinPipe ;branch to leave if d0 set (execute code every other frame) 10618 lda TimerControl ;get master timer control 10619 bne PutinPipe ;branch to leave if set (likely not necessary) 10620 lda Enemy_Y_Position,x ;get current vertical coordinate 10621 clc 10622 adc PiranhaPlant_Y_Speed,x ;add vertical speed to move up or down 10623 sta Enemy_Y_Position,x ;save as new vertical coordinate 10624 cmp $00 ;compare against low or high coordinate 10625 bne PutinPipe ;branch to leave if not yet reached 10626 lda #$00 10627 sta PiranhaPlant_MoveFlag,x ;otherwise clear movement flag 10628 lda #$40 10629 sta EnemyFrameTimer,x ;set timer to delay piranha plant movement 10630 10631 PutinPipe: 10632 lda #%00100000 ;set background priority bit in sprite 10633 sta Enemy_SprAttrib,x ;attributes to give illusion of being inside pipe 10634 rts ;then leave 10635 10636 ;------------------------------------------------------------------------------------- 10637 ;$07 - spinning speed 10638 10639 FirebarSpin: 10640 sta $07 ;save spinning speed here 10641 lda FirebarSpinDirection,x ;check spinning direction 10642 bne SpinCounterClockwise ;if moving counter-clockwise, branch to other part 10643 ldy #$18 ;possibly residual ldy 10644 lda FirebarSpinState_Low,x 10645 clc ;add spinning speed to what would normally be 10646 adc $07 ;the horizontal speed 10647 sta FirebarSpinState_Low,x 10648 lda FirebarSpinState_High,x ;add carry to what would normally be the vertical speed 10649 adc #$00 10650 rts 10651 10652 SpinCounterClockwise: 10653 ldy #$08 ;possibly residual ldy 10654 lda FirebarSpinState_Low,x 10655 sec ;subtract spinning speed to what would normally be 10656 sbc $07 ;the horizontal speed 10657 sta FirebarSpinState_Low,x 10658 lda FirebarSpinState_High,x ;add carry to what would normally be the vertical speed 10659 sbc #$00 10660 rts 10661 10662 ;------------------------------------------------------------------------------------- 10663 ;$00 - used to hold collision flag, Y movement force + 5 or low byte of name table for rope 10664 ;$01 - used to hold high byte of name table for rope 10665 ;$02 - used to hold page location of rope 10666 10667 BalancePlatform: 10668 lda Enemy_Y_HighPos,x ;check high byte of vertical position 10669 cmp #$03 10670 bne DoBPl 10671 jmp EraseEnemyObject ;if far below screen, kill the object 10672 DoBPl: lda Enemy_State,x ;get object's state (set to $ff or other platform offset) 10673 bpl CheckBalPlatform ;if doing other balance platform, branch to leave 10674 rts 10675 10676 CheckBalPlatform: 10677 tay ;save offset from state as Y 10678 lda PlatformCollisionFlag,x ;get collision flag of platform 10679 sta $00 ;store here 10680 lda Enemy_MovingDir,x ;get moving direction 10681 beq ChkForFall 10682 jmp PlatformFall ;if set, jump here 10683 10684 ChkForFall: 10685 lda #$2d ;check if platform is above a certain point 10686 cmp Enemy_Y_Position,x 10687 bcc ChkOtherForFall ;if not, branch elsewhere 10688 cpy $00 ;if collision flag is set to same value as 10689 beq MakePlatformFall ;enemy state, branch to make platforms fall 10690 clc 10691 adc #$02 ;otherwise add 2 pixels to vertical position 10692 sta Enemy_Y_Position,x ;of current platform and branch elsewhere 10693 jmp StopPlatforms ;to make platforms stop 10694 10695 MakePlatformFall: 10696 jmp InitPlatformFall ;make platforms fall 10697 10698 ChkOtherForFall: 10699 cmp Enemy_Y_Position,y ;check if other platform is above a certain point 10700 bcc ChkToMoveBalPlat ;if not, branch elsewhere 10701 cpx $00 ;if collision flag is set to same value as 10702 beq MakePlatformFall ;enemy state, branch to make platforms fall 10703 clc 10704 adc #$02 ;otherwise add 2 pixels to vertical position 10705 sta Enemy_Y_Position,y ;of other platform and branch elsewhere 10706 jmp StopPlatforms ;jump to stop movement and do not return 10707 10708 ChkToMoveBalPlat: 10709 lda Enemy_Y_Position,x ;save vertical position to stack 10710 pha 10711 lda PlatformCollisionFlag,x ;get collision flag 10712 bpl ColFlg ;branch if collision 10713 lda Enemy_Y_MoveForce,x 10714 clc ;add $05 to contents of moveforce, whatever they be 10715 adc #$05 10716 sta $00 ;store here 10717 lda Enemy_Y_Speed,x 10718 adc #$00 ;add carry to vertical speed 10719 bmi PlatDn ;branch if moving downwards 10720 bne PlatUp ;branch elsewhere if moving upwards 10721 lda $00 10722 cmp #$0b ;check if there's still a little force left 10723 bcc PlatSt ;if not enough, branch to stop movement 10724 bcs PlatUp ;otherwise keep branch to move upwards 10725 ColFlg: cmp ObjectOffset ;if collision flag matches 10726 beq PlatDn ;current enemy object offset, branch 10727 PlatUp: jsr MovePlatformUp ;do a sub to move upwards 10728 jmp DoOtherPlatform ;jump ahead to remaining code 10729 PlatSt: jsr StopPlatforms ;do a sub to stop movement 10730 jmp DoOtherPlatform ;jump ahead to remaining code 10731 PlatDn: jsr MovePlatformDown ;do a sub to move downwards 10732 10733 DoOtherPlatform: 10734 ldy Enemy_State,x ;get offset of other platform 10735 pla ;get old vertical coordinate from stack 10736 sec 10737 sbc Enemy_Y_Position,x ;get difference of old vs. new coordinate 10738 clc 10739 adc Enemy_Y_Position,y ;add difference to vertical coordinate of other 10740 sta Enemy_Y_Position,y ;platform to move it in the opposite direction 10741 lda PlatformCollisionFlag,x ;if no collision, skip this part here 10742 bmi DrawEraseRope 10743 tax ;put offset which collision occurred here 10744 jsr PositionPlayerOnVPlat ;and use it to position player accordingly 10745 10746 DrawEraseRope: 10747 ldy ObjectOffset ;get enemy object offset 10748 lda Enemy_Y_Speed,y ;check to see if current platform is 10749 ora Enemy_Y_MoveForce,y ;moving at all 10750 beq ExitRp ;if not, skip all of this and branch to leave 10751 ldx VRAM_Buffer1_Offset ;get vram buffer offset 10752 cpx #$20 ;if offset beyond a certain point, go ahead 10753 bcs ExitRp ;and skip this, branch to leave 10754 lda Enemy_Y_Speed,y 10755 pha ;save two copies of vertical speed to stack 10756 pha 10757 jsr SetupPlatformRope ;do a sub to figure out where to put new bg tiles 10758 lda $01 ;write name table address to vram buffer 10759 sta VRAM_Buffer1,x ;first the high byte, then the low 10760 lda $00 10761 sta VRAM_Buffer1+1,x 10762 lda #$02 ;set length for 2 bytes 10763 sta VRAM_Buffer1+2,x 10764 lda Enemy_Y_Speed,y ;if platform moving upwards, branch 10765 bmi EraseR1 ;to do something else 10766 lda #$a2 10767 sta VRAM_Buffer1+3,x ;otherwise put tile numbers for left 10768 lda #$a3 ;and right sides of rope in vram buffer 10769 sta VRAM_Buffer1+4,x 10770 jmp OtherRope ;jump to skip this part 10771 EraseR1: lda #$24 ;put blank tiles in vram buffer 10772 sta VRAM_Buffer1+3,x ;to erase rope 10773 sta VRAM_Buffer1+4,x 10774 10775 OtherRope: 10776 lda Enemy_State,y ;get offset of other platform from state 10777 tay ;use as Y here 10778 pla ;pull second copy of vertical speed from stack 10779 eor #$ff ;invert bits to reverse speed 10780 jsr SetupPlatformRope ;do sub again to figure out where to put bg tiles 10781 lda $01 ;write name table address to vram buffer 10782 sta VRAM_Buffer1+5,x ;this time we're doing putting tiles for 10783 lda $00 ;the other platform 10784 sta VRAM_Buffer1+6,x 10785 lda #$02 10786 sta VRAM_Buffer1+7,x ;set length again for 2 bytes 10787 pla ;pull first copy of vertical speed from stack 10788 bpl EraseR2 ;if moving upwards (note inversion earlier), skip this 10789 lda #$a2 10790 sta VRAM_Buffer1+8,x ;otherwise put tile numbers for left 10791 lda #$a3 ;and right sides of rope in vram 10792 sta VRAM_Buffer1+9,x ;transfer buffer 10793 jmp EndRp ;jump to skip this part 10794 EraseR2: lda #$24 ;put blank tiles in vram buffer 10795 sta VRAM_Buffer1+8,x ;to erase rope 10796 sta VRAM_Buffer1+9,x 10797 EndRp: lda #$00 ;put null terminator at the end 10798 sta VRAM_Buffer1+10,x 10799 lda VRAM_Buffer1_Offset ;add ten bytes to the vram buffer offset 10800 clc ;and store 10801 adc #10 10802 sta VRAM_Buffer1_Offset 10803 ExitRp: ldx ObjectOffset ;get enemy object buffer offset and leave 10804 rts 10805 10806 SetupPlatformRope: 10807 pha ;save second/third copy to stack 10808 lda Enemy_X_Position,y ;get horizontal coordinate 10809 clc 10810 adc #$08 ;add eight pixels 10811 ldx SecondaryHardMode ;if secondary hard mode flag set, 10812 bne GetLRp ;use coordinate as-is 10813 clc 10814 adc #$10 ;otherwise add sixteen more pixels 10815 GetLRp: pha ;save modified horizontal coordinate to stack 10816 lda Enemy_PageLoc,y 10817 adc #$00 ;add carry to page location 10818 sta $02 ;and save here 10819 pla ;pull modified horizontal coordinate 10820 and #%11110000 ;from the stack, mask out low nybble 10821 lsr ;and shift three bits to the right 10822 lsr 10823 lsr 10824 sta $00 ;store result here as part of name table low byte 10825 ldx Enemy_Y_Position,y ;get vertical coordinate 10826 pla ;get second/third copy of vertical speed from stack 10827 bpl GetHRp ;skip this part if moving downwards or not at all 10828 txa 10829 clc 10830 adc #$08 ;add eight to vertical coordinate and 10831 tax ;save as X 10832 GetHRp: txa ;move vertical coordinate to A 10833 ldx VRAM_Buffer1_Offset ;get vram buffer offset 10834 asl 10835 rol ;rotate d7 to d0 and d6 into carry 10836 pha ;save modified vertical coordinate to stack 10837 rol ;rotate carry to d0, thus d7 and d6 are at 2 LSB 10838 and #%00000011 ;mask out all bits but d7 and d6, then set 10839 ora #%00100000 ;d5 to get appropriate high byte of name table 10840 sta $01 ;address, then store 10841 lda $02 ;get saved page location from earlier 10842 and #$01 ;mask out all but LSB 10843 asl 10844 asl ;shift twice to the left and save with the 10845 ora $01 ;rest of the bits of the high byte, to get 10846 sta $01 ;the proper name table and the right place on it 10847 pla ;get modified vertical coordinate from stack 10848 and #%11100000 ;mask out low nybble and LSB of high nybble 10849 clc 10850 adc $00 ;add to horizontal part saved here 10851 sta $00 ;save as name table low byte 10852 lda Enemy_Y_Position,y 10853 cmp #$e8 ;if vertical position not below the 10854 bcc ExPRp ;bottom of the screen, we're done, branch to leave 10855 lda $00 10856 and #%10111111 ;mask out d6 of low byte of name table address 10857 sta $00 10858 ExPRp: rts ;leave! 10859 10860 InitPlatformFall: 10861 tya ;move offset of other platform from Y to X 10862 tax 10863 jsr GetEnemyOffscreenBits ;get offscreen bits 10864 lda #$06 10865 jsr SetupFloateyNumber ;award 1000 points to player 10866 lda Player_Rel_XPos 10867 sta FloateyNum_X_Pos,x ;put floatey number coordinates where player is 10868 lda Player_Y_Position 10869 sta FloateyNum_Y_Pos,x 10870 lda #$01 ;set moving direction as flag for 10871 sta Enemy_MovingDir,x ;falling platforms 10872 10873 StopPlatforms: 10874 jsr InitVStf ;initialize vertical speed and low byte 10875 sta Enemy_Y_Speed,y ;for both platforms and leave 10876 sta Enemy_Y_MoveForce,y 10877 rts 10878 10879 PlatformFall: 10880 tya ;save offset for other platform to stack 10881 pha 10882 jsr MoveFallingPlatform ;make current platform fall 10883 pla 10884 tax ;pull offset from stack and save to X 10885 jsr MoveFallingPlatform ;make other platform fall 10886 ldx ObjectOffset 10887 lda PlatformCollisionFlag,x ;if player not standing on either platform, 10888 bmi ExPF ;skip this part 10889 tax ;transfer collision flag offset as offset to X 10890 jsr PositionPlayerOnVPlat ;and position player appropriately 10891 ExPF: ldx ObjectOffset ;get enemy object buffer offset and leave 10892 rts 10893 10894 ;-------------------------------- 10895 10896 YMovingPlatform: 10897 lda Enemy_Y_Speed,x ;if platform moving up or down, skip ahead to 10898 ora Enemy_Y_MoveForce,x ;check on other position 10899 bne ChkYCenterPos 10900 sta Enemy_YMF_Dummy,x ;initialize dummy variable 10901 lda Enemy_Y_Position,x 10902 cmp YPlatformTopYPos,x ;if current vertical position => top position, branch 10903 bcs ChkYCenterPos ;ahead of all this 10904 lda FrameCounter 10905 and #%00000111 ;check for every eighth frame 10906 bne SkipIY 10907 inc Enemy_Y_Position,x ;increase vertical position every eighth frame 10908 SkipIY: jmp ChkYPCollision ;skip ahead to last part 10909 10910 ChkYCenterPos: 10911 lda Enemy_Y_Position,x ;if current vertical position < central position, branch 10912 cmp YPlatformCenterYPos,x ;to slow ascent/move downwards 10913 bcc YMDown 10914 jsr MovePlatformUp ;otherwise start slowing descent/moving upwards 10915 jmp ChkYPCollision 10916 YMDown: jsr MovePlatformDown ;start slowing ascent/moving downwards 10917 10918 ChkYPCollision: 10919 lda PlatformCollisionFlag,x ;if collision flag not set here, branch 10920 bmi ExYPl ;to leave 10921 jsr PositionPlayerOnVPlat ;otherwise position player appropriately 10922 ExYPl: rts ;leave 10923 10924 ;-------------------------------- 10925 ;$00 - used as adder to position player hotizontally 10926 10927 XMovingPlatform: 10928 lda #$0e ;load preset maximum value for secondary counter 10929 jsr XMoveCntr_Platform ;do a sub to increment counters for movement 10930 jsr MoveWithXMCntrs ;do a sub to move platform accordingly, and return value 10931 lda PlatformCollisionFlag,x ;if no collision with player, 10932 bmi ExXMP ;branch ahead to leave 10933 10934 PositionPlayerOnHPlat: 10935 lda Player_X_Position 10936 clc ;add saved value from second subroutine to 10937 adc $00 ;current player's position to position 10938 sta Player_X_Position ;player accordingly in horizontal position 10939 lda Player_PageLoc ;get player's page location 10940 ldy $00 ;check to see if saved value here is positive or negative 10941 bmi PPHSubt ;if negative, branch to subtract 10942 adc #$00 ;otherwise add carry to page location 10943 jmp SetPVar ;jump to skip subtraction 10944 PPHSubt: sbc #$00 ;subtract borrow from page location 10945 SetPVar: sta Player_PageLoc ;save result to player's page location 10946 sty Platform_X_Scroll ;put saved value from second sub here to be used later 10947 jsr PositionPlayerOnVPlat ;position player vertically and appropriately 10948 ExXMP: rts ;and we are done here 10949 10950 ;-------------------------------- 10951 10952 DropPlatform: 10953 lda PlatformCollisionFlag,x ;if no collision between platform and player 10954 bmi ExDPl ;occurred, just leave without moving anything 10955 jsr MoveDropPlatform ;otherwise do a sub to move platform down very quickly 10956 jsr PositionPlayerOnVPlat ;do a sub to position player appropriately 10957 ExDPl: rts ;leave 10958 10959 ;-------------------------------- 10960 ;$00 - residual value from sub 10961 10962 RightPlatform: 10963 jsr MoveEnemyHorizontally ;move platform with current horizontal speed, if any 10964 sta $00 ;store saved value here (residual code) 10965 lda PlatformCollisionFlag,x ;check collision flag, if no collision between player 10966 bmi ExRPl ;and platform, branch ahead, leave speed unaltered 10967 lda #$10 10968 sta Enemy_X_Speed,x ;otherwise set new speed (gets moving if motionless) 10969 jsr PositionPlayerOnHPlat ;use saved value from earlier sub to position player 10970 ExRPl: rts ;then leave 10971 10972 ;-------------------------------- 10973 10974 MoveLargeLiftPlat: 10975 jsr MoveLiftPlatforms ;execute common to all large and small lift platforms 10976 jmp ChkYPCollision ;branch to position player correctly 10977 10978 MoveSmallPlatform: 10979 jsr MoveLiftPlatforms ;execute common to all large and small lift platforms 10980 jmp ChkSmallPlatCollision ;branch to position player correctly 10981 10982 MoveLiftPlatforms: 10983 lda TimerControl ;if master timer control set, skip all of this 10984 bne ExLiftP ;and branch to leave 10985 lda Enemy_YMF_Dummy,x 10986 clc ;add contents of movement amount to whatever's here 10987 adc Enemy_Y_MoveForce,x 10988 sta Enemy_YMF_Dummy,x 10989 lda Enemy_Y_Position,x ;add whatever vertical speed is set to current 10990 adc Enemy_Y_Speed,x ;vertical position plus carry to move up or down 10991 sta Enemy_Y_Position,x ;and then leave 10992 rts 10993 10994 ChkSmallPlatCollision: 10995 lda PlatformCollisionFlag,x ;get bounding box counter saved in collision flag 10996 beq ExLiftP ;if none found, leave player position alone 10997 jsr PositionPlayerOnS_Plat ;use to position player correctly 10998 ExLiftP: rts ;then leave 10999 11000 ;------------------------------------------------------------------------------------- 11001 ;$00 - page location of extended left boundary 11002 ;$01 - extended left boundary position 11003 ;$02 - page location of extended right boundary 11004 ;$03 - extended right boundary position 11005 11006 OffscreenBoundsCheck: 11007 lda Enemy_ID,x ;check for cheep-cheep object 11008 cmp #FlyingCheepCheep ;branch to leave if found 11009 beq ExScrnBd 11010 lda ScreenLeft_X_Pos ;get horizontal coordinate for left side of screen 11011 ldy Enemy_ID,x 11012 cpy #HammerBro ;check for hammer bro object 11013 beq LimitB 11014 cpy #PiranhaPlant ;check for piranha plant object 11015 bne ExtendLB ;these two will be erased sooner than others if too far left 11016 LimitB: adc #$38 ;add 56 pixels to coordinate if hammer bro or piranha plant 11017 ExtendLB: sbc #$48 ;subtract 72 pixels regardless of enemy object 11018 sta $01 ;store result here 11019 lda ScreenLeft_PageLoc 11020 sbc #$00 ;subtract borrow from page location of left side 11021 sta $00 ;store result here 11022 lda ScreenRight_X_Pos ;add 72 pixels to the right side horizontal coordinate 11023 adc #$48 11024 sta $03 ;store result here 11025 lda ScreenRight_PageLoc 11026 adc #$00 ;then add the carry to the page location 11027 sta $02 ;and store result here 11028 lda Enemy_X_Position,x ;compare horizontal coordinate of the enemy object 11029 cmp $01 ;to modified horizontal left edge coordinate to get carry 11030 lda Enemy_PageLoc,x 11031 sbc $00 ;then subtract it from the page coordinate of the enemy object 11032 bmi TooFar ;if enemy object is too far left, branch to erase it 11033 lda Enemy_X_Position,x ;compare horizontal coordinate of the enemy object 11034 cmp $03 ;to modified horizontal right edge coordinate to get carry 11035 lda Enemy_PageLoc,x 11036 sbc $02 ;then subtract it from the page coordinate of the enemy object 11037 bmi ExScrnBd ;if enemy object is on the screen, leave, do not erase enemy 11038 lda Enemy_State,x ;if at this point, enemy is offscreen to the right, so check 11039 cmp #HammerBro ;if in state used by spiny's egg, do not erase 11040 beq ExScrnBd 11041 cpy #PiranhaPlant ;if piranha plant, do not erase 11042 beq ExScrnBd 11043 cpy #FlagpoleFlagObject ;if flagpole flag, do not erase 11044 beq ExScrnBd 11045 cpy #StarFlagObject ;if star flag, do not erase 11046 beq ExScrnBd 11047 cpy #JumpspringObject ;if jumpspring, do not erase 11048 beq ExScrnBd ;erase all others too far to the right 11049 TooFar: jsr EraseEnemyObject ;erase object if necessary 11050 ExScrnBd: rts ;leave 11051 11052 ;------------------------------------------------------------------------------------- 11053 11054 ;some unused space 11055 .db $ff, $ff, $ff 11056 11057 ;------------------------------------------------------------------------------------- 11058 ;$01 - enemy buffer offset 11059 11060 FireballEnemyCollision: 11061 lda Fireball_State,x ;check to see if fireball state is set at all 11062 beq ExitFBallEnemy ;branch to leave if not 11063 asl 11064 bcs ExitFBallEnemy ;branch to leave also if d7 in state is set 11065 lda FrameCounter 11066 lsr ;get LSB of frame counter 11067 bcs ExitFBallEnemy ;branch to leave if set (do routine every other frame) 11068 txa 11069 asl ;multiply fireball offset by four 11070 asl 11071 clc 11072 adc #$1c ;then add $1c or 28 bytes to it 11073 tay ;to use fireball's bounding box coordinates 11074 ldx #$04 11075 11076 FireballEnemyCDLoop: 11077 stx $01 ;store enemy object offset here 11078 tya 11079 pha ;push fireball offset to the stack 11080 lda Enemy_State,x 11081 and #%00100000 ;check to see if d5 is set in enemy state 11082 bne NoFToECol ;if so, skip to next enemy slot 11083 lda Enemy_Flag,x ;check to see if buffer flag is set 11084 beq NoFToECol ;if not, skip to next enemy slot 11085 lda Enemy_ID,x ;check enemy identifier 11086 cmp #$24 11087 bcc GoombaDie ;if < $24, branch to check further 11088 cmp #$2b 11089 bcc NoFToECol ;if in range $24-$2a, skip to next enemy slot 11090 GoombaDie: cmp #Goomba ;check for goomba identifier 11091 bne NotGoomba ;if not found, continue with code 11092 lda Enemy_State,x ;otherwise check for defeated state 11093 cmp #$02 ;if stomped or otherwise defeated, 11094 bcs NoFToECol ;skip to next enemy slot 11095 NotGoomba: lda EnemyOffscrBitsMasked,x ;if any masked offscreen bits set, 11096 bne NoFToECol ;skip to next enemy slot 11097 txa 11098 asl ;otherwise multiply enemy offset by four 11099 asl 11100 clc 11101 adc #$04 ;add 4 bytes to it 11102 tax ;to use enemy's bounding box coordinates 11103 jsr SprObjectCollisionCore ;do fireball-to-enemy collision detection 11104 ldx ObjectOffset ;return fireball's original offset 11105 bcc NoFToECol ;if carry clear, no collision, thus do next enemy slot 11106 lda #%10000000 11107 sta Fireball_State,x ;set d7 in enemy state 11108 ldx $01 ;get enemy offset 11109 jsr HandleEnemyFBallCol ;jump to handle fireball to enemy collision 11110 NoFToECol: pla ;pull fireball offset from stack 11111 tay ;put it in Y 11112 ldx $01 ;get enemy object offset 11113 dex ;decrement it 11114 bpl FireballEnemyCDLoop ;loop back until collision detection done on all enemies 11115 11116 ExitFBallEnemy: 11117 ldx ObjectOffset ;get original fireball offset and leave 11118 rts 11119 11120 BowserIdentities: 11121 .db Goomba, GreenKoopa, BuzzyBeetle, Spiny, Lakitu, Bloober, HammerBro, Bowser 11122 11123 HandleEnemyFBallCol: 11124 jsr RelativeEnemyPosition ;get relative coordinate of enemy 11125 ldx $01 ;get current enemy object offset 11126 lda Enemy_Flag,x ;check buffer flag for d7 set 11127 bpl ChkBuzzyBeetle ;branch if not set to continue 11128 and #%00001111 ;otherwise mask out high nybble and 11129 tax ;use low nybble as enemy offset 11130 lda Enemy_ID,x 11131 cmp #Bowser ;check enemy identifier for bowser 11132 beq HurtBowser ;branch if found 11133 ldx $01 ;otherwise retrieve current enemy offset 11134 11135 ChkBuzzyBeetle: 11136 lda Enemy_ID,x 11137 cmp #BuzzyBeetle ;check for buzzy beetle 11138 beq ExHCF ;branch if found to leave (buzzy beetles fireproof) 11139 cmp #Bowser ;check for bowser one more time (necessary if d7 of flag was clear) 11140 bne ChkOtherEnemies ;if not found, branch to check other enemies 11141 11142 HurtBowser: 11143 dec BowserHitPoints ;decrement bowser's hit points 11144 bne ExHCF ;if bowser still has hit points, branch to leave 11145 jsr InitVStf ;otherwise do sub to init vertical speed and movement force 11146 sta Enemy_X_Speed,x ;initialize horizontal speed 11147 sta EnemyFrenzyBuffer ;init enemy frenzy buffer 11148 lda #$fe 11149 sta Enemy_Y_Speed,x ;set vertical speed to make defeated bowser jump a little 11150 ldy WorldNumber ;use world number as offset 11151 lda BowserIdentities,y ;get enemy identifier to replace bowser with 11152 sta Enemy_ID,x ;set as new enemy identifier 11153 lda #$20 ;set A to use starting value for state 11154 cpy #$03 ;check to see if using offset of 3 or more 11155 bcs SetDBSte ;branch if so 11156 ora #$03 ;otherwise add 3 to enemy state 11157 SetDBSte: sta Enemy_State,x ;set defeated enemy state 11158 lda #Sfx_BowserFall 11159 sta Square2SoundQueue ;load bowser defeat sound 11160 ldx $01 ;get enemy offset 11161 lda #$09 ;award 5000 points to player for defeating bowser 11162 bne EnemySmackScore ;unconditional branch to award points 11163 11164 ChkOtherEnemies: 11165 cmp #BulletBill_FrenzyVar 11166 beq ExHCF ;branch to leave if bullet bill (frenzy variant) 11167 cmp #Podoboo 11168 beq ExHCF ;branch to leave if podoboo 11169 cmp #$15 11170 bcs ExHCF ;branch to leave if identifier => $15 11171 11172 ShellOrBlockDefeat: 11173 lda Enemy_ID,x ;check for piranha plant 11174 cmp #PiranhaPlant 11175 bne StnE ;branch if not found 11176 lda Enemy_Y_Position,x 11177 adc #$18 ;add 24 pixels to enemy object's vertical position 11178 sta Enemy_Y_Position,x 11179 StnE: jsr ChkToStunEnemies ;do yet another sub 11180 lda Enemy_State,x 11181 and #%00011111 ;mask out 2 MSB of enemy object's state 11182 ora #%00100000 ;set d5 to defeat enemy and save as new state 11183 sta Enemy_State,x 11184 lda #$02 ;award 200 points by default 11185 ldy Enemy_ID,x ;check for hammer bro 11186 cpy #HammerBro 11187 bne GoombaPoints ;branch if not found 11188 lda #$06 ;award 1000 points for hammer bro 11189 11190 GoombaPoints: 11191 cpy #Goomba ;check for goomba 11192 bne EnemySmackScore ;branch if not found 11193 lda #$01 ;award 100 points for goomba 11194 11195 EnemySmackScore: 11196 jsr SetupFloateyNumber ;update necessary score variables 11197 lda #Sfx_EnemySmack ;play smack enemy sound 11198 sta Square1SoundQueue 11199 ExHCF: rts ;and now let's leave 11200 11201 ;------------------------------------------------------------------------------------- 11202 11203 PlayerHammerCollision: 11204 lda FrameCounter ;get frame counter 11205 lsr ;shift d0 into carry 11206 bcc ExPHC ;branch to leave if d0 not set to execute every other frame 11207 lda TimerControl ;if either master timer control 11208 ora Misc_OffscreenBits ;or any offscreen bits for hammer are set, 11209 bne ExPHC ;branch to leave 11210 txa 11211 asl ;multiply misc object offset by four 11212 asl 11213 clc 11214 adc #$24 ;add 36 or $24 bytes to get proper offset 11215 tay ;for misc object bounding box coordinates 11216 jsr PlayerCollisionCore ;do player-to-hammer collision detection 11217 ldx ObjectOffset ;get misc object offset 11218 bcc ClHCol ;if no collision, then branch 11219 lda Misc_Collision_Flag,x ;otherwise read collision flag 11220 bne ExPHC ;if collision flag already set, branch to leave 11221 lda #$01 11222 sta Misc_Collision_Flag,x ;otherwise set collision flag now 11223 lda Misc_X_Speed,x 11224 eor #$ff ;get two's compliment of 11225 clc ;hammer's horizontal speed 11226 adc #$01 11227 sta Misc_X_Speed,x ;set to send hammer flying the opposite direction 11228 lda StarInvincibleTimer ;if star mario invincibility timer set, 11229 bne ExPHC ;branch to leave 11230 jmp InjurePlayer ;otherwise jump to hurt player, do not return 11231 ClHCol: lda #$00 ;clear collision flag 11232 sta Misc_Collision_Flag,x 11233 ExPHC: rts 11234 11235 ;------------------------------------------------------------------------------------- 11236 11237 HandlePowerUpCollision: 11238 jsr EraseEnemyObject ;erase the power-up object 11239 lda #$06 11240 jsr SetupFloateyNumber ;award 1000 points to player by default 11241 lda #Sfx_PowerUpGrab 11242 sta Square2SoundQueue ;play the power-up sound 11243 lda PowerUpType ;check power-up type 11244 cmp #$02 11245 bcc Shroom_Flower_PUp ;if mushroom or fire flower, branch 11246 cmp #$03 11247 beq SetFor1Up ;if 1-up mushroom, branch 11248 lda #$23 ;otherwise set star mario invincibility 11249 sta StarInvincibleTimer ;timer, and load the star mario music 11250 lda #StarPowerMusic ;into the area music queue, then leave 11251 sta AreaMusicQueue 11252 rts 11253 11254 Shroom_Flower_PUp: 11255 lda PlayerStatus ;if player status = small, branch 11256 beq UpToSuper 11257 cmp #$01 ;if player status not super, leave 11258 bne NoPUp 11259 ldx ObjectOffset ;get enemy offset, not necessary 11260 lda #$02 ;set player status to fiery 11261 sta PlayerStatus 11262 jsr GetPlayerColors ;run sub to change colors of player 11263 ldx ObjectOffset ;get enemy offset again, and again not necessary 11264 lda #$0c ;set value to be used by subroutine tree (fiery) 11265 jmp UpToFiery ;jump to set values accordingly 11266 11267 SetFor1Up: 11268 lda #$0b ;change 1000 points into 1-up instead 11269 sta FloateyNum_Control,x ;and then leave 11270 rts 11271 11272 UpToSuper: 11273 lda #$01 ;set player status to super 11274 sta PlayerStatus 11275 lda #$09 ;set value to be used by subroutine tree (super) 11276 11277 UpToFiery: 11278 ldy #$00 ;set value to be used as new player state 11279 jsr SetPRout ;set values to stop certain things in motion 11280 NoPUp: rts 11281 11282 ;-------------------------------- 11283 11284 ResidualXSpdData: 11285 .db $18, $e8 11286 11287 KickedShellXSpdData: 11288 .db $30, $d0 11289 11290 DemotedKoopaXSpdData: 11291 .db $08, $f8 11292 11293 PlayerEnemyCollision: 11294 lda FrameCounter ;check counter for d0 set 11295 lsr 11296 bcs NoPUp ;if set, branch to leave 11297 jsr CheckPlayerVertical ;if player object is completely offscreen or 11298 bcs NoPECol ;if down past 224th pixel row, branch to leave 11299 lda EnemyOffscrBitsMasked,x ;if current enemy is offscreen by any amount, 11300 bne NoPECol ;go ahead and branch to leave 11301 lda GameEngineSubroutine 11302 cmp #$08 ;if not set to run player control routine 11303 bne NoPECol ;on next frame, branch to leave 11304 lda Enemy_State,x 11305 and #%00100000 ;if enemy state has d5 set, branch to leave 11306 bne NoPECol 11307 jsr GetEnemyBoundBoxOfs ;get bounding box offset for current enemy object 11308 jsr PlayerCollisionCore ;do collision detection on player vs. enemy 11309 ldx ObjectOffset ;get enemy object buffer offset 11310 bcs CheckForPUpCollision ;if collision, branch past this part here 11311 lda Enemy_CollisionBits,x 11312 and #%11111110 ;otherwise, clear d0 of current enemy object's 11313 sta Enemy_CollisionBits,x ;collision bit 11314 NoPECol: rts 11315 11316 CheckForPUpCollision: 11317 ldy Enemy_ID,x 11318 cpy #PowerUpObject ;check for power-up object 11319 bne EColl ;if not found, branch to next part 11320 jmp HandlePowerUpCollision ;otherwise, unconditional jump backwards 11321 EColl: lda StarInvincibleTimer ;if star mario invincibility timer expired, 11322 beq HandlePECollisions ;perform task here, otherwise kill enemy like 11323 jmp ShellOrBlockDefeat ;hit with a shell, or from beneath 11324 11325 KickedShellPtsData: 11326 .db $0a, $06, $04 11327 11328 HandlePECollisions: 11329 lda Enemy_CollisionBits,x ;check enemy collision bits for d0 set 11330 and #%00000001 ;or for being offscreen at all 11331 ora EnemyOffscrBitsMasked,x 11332 bne ExPEC ;branch to leave if either is true 11333 lda #$01 11334 ora Enemy_CollisionBits,x ;otherwise set d0 now 11335 sta Enemy_CollisionBits,x 11336 cpy #Spiny ;branch if spiny 11337 beq ChkForPlayerInjury 11338 cpy #PiranhaPlant ;branch if piranha plant 11339 beq InjurePlayer 11340 cpy #Podoboo ;branch if podoboo 11341 beq InjurePlayer 11342 cpy #BulletBill_CannonVar ;branch if bullet bill 11343 beq ChkForPlayerInjury 11344 cpy #$15 ;branch if object => $15 11345 bcs InjurePlayer 11346 lda AreaType ;branch if water type level 11347 beq InjurePlayer 11348 lda Enemy_State,x ;branch if d7 of enemy state was set 11349 asl 11350 bcs ChkForPlayerInjury 11351 lda Enemy_State,x ;mask out all but 3 LSB of enemy state 11352 and #%00000111 11353 cmp #$02 ;branch if enemy is in normal or falling state 11354 bcc ChkForPlayerInjury 11355 lda Enemy_ID,x ;branch to leave if goomba in defeated state 11356 cmp #Goomba 11357 beq ExPEC 11358 lda #Sfx_EnemySmack ;play smack enemy sound 11359 sta Square1SoundQueue 11360 lda Enemy_State,x ;set d7 in enemy state, thus become moving shell 11361 ora #%10000000 11362 sta Enemy_State,x 11363 jsr EnemyFacePlayer ;set moving direction and get offset 11364 lda KickedShellXSpdData,y ;load and set horizontal speed data with offset 11365 sta Enemy_X_Speed,x 11366 lda #$03 ;add three to whatever the stomp counter contains 11367 clc ;to give points for kicking the shell 11368 adc StompChainCounter 11369 ldy EnemyIntervalTimer,x ;check shell enemy's timer 11370 cpy #$03 ;if above a certain point, branch using the points 11371 bcs KSPts ;data obtained from the stomp counter + 3 11372 lda KickedShellPtsData,y ;otherwise, set points based on proximity to timer expiration 11373 KSPts: jsr SetupFloateyNumber ;set values for floatey number now 11374 ExPEC: rts ;leave!!! 11375 11376 ChkForPlayerInjury: 11377 lda Player_Y_Speed ;check player's vertical speed 11378 bmi ChkInj ;perform procedure below if player moving upwards 11379 bne EnemyStomped ;or not at all, and branch elsewhere if moving downwards 11380 ChkInj: lda Enemy_ID,x ;branch if enemy object < $07 11381 cmp #Bloober 11382 bcc ChkETmrs 11383 lda Player_Y_Position ;add 12 pixels to player's vertical position 11384 clc 11385 adc #$0c 11386 cmp Enemy_Y_Position,x ;compare modified player's position to enemy's position 11387 bcc EnemyStomped ;branch if this player's position above (less than) enemy's 11388 ChkETmrs: lda StompTimer ;check stomp timer 11389 bne EnemyStomped ;branch if set 11390 lda InjuryTimer ;check to see if injured invincibility timer still 11391 bne ExInjColRoutines ;counting down, and branch elsewhere to leave if so 11392 lda Player_Rel_XPos 11393 cmp Enemy_Rel_XPos ;if player's relative position to the left of enemy's 11394 bcc TInjE ;relative position, branch here 11395 jmp ChkEnemyFaceRight ;otherwise do a jump here 11396 TInjE: lda Enemy_MovingDir,x ;if enemy moving towards the left, 11397 cmp #$01 ;branch, otherwise do a jump here 11398 bne InjurePlayer ;to turn the enemy around 11399 jmp LInj 11400 11401 InjurePlayer: 11402 lda InjuryTimer ;check again to see if injured invincibility timer is 11403 bne ExInjColRoutines ;at zero, and branch to leave if so 11404 11405 ForceInjury: 11406 ldx PlayerStatus ;check player's status 11407 beq KillPlayer ;branch if small 11408 sta PlayerStatus ;otherwise set player's status to small 11409 lda #$08 11410 sta InjuryTimer ;set injured invincibility timer 11411 asl 11412 sta Square1SoundQueue ;play pipedown/injury sound 11413 jsr GetPlayerColors ;change player's palette if necessary 11414 lda #$0a ;set subroutine to run on next frame 11415 SetKRout: ldy #$01 ;set new player state 11416 SetPRout: sta GameEngineSubroutine ;load new value to run subroutine on next frame 11417 sty Player_State ;store new player state 11418 ldy #$ff 11419 sty TimerControl ;set master timer control flag to halt timers 11420 iny 11421 sty ScrollAmount ;initialize scroll speed 11422 11423 ExInjColRoutines: 11424 ldx ObjectOffset ;get enemy offset and leave 11425 rts 11426 11427 KillPlayer: 11428 stx Player_X_Speed ;halt player's horizontal movement by initializing speed 11429 inx 11430 stx EventMusicQueue ;set event music queue to death music 11431 lda #$fc 11432 sta Player_Y_Speed ;set new vertical speed 11433 lda #$0b ;set subroutine to run on next frame 11434 bne SetKRout ;branch to set player's state and other things 11435 11436 StompedEnemyPtsData: 11437 .db $02, $06, $05, $06 11438 11439 EnemyStomped: 11440 lda Enemy_ID,x ;check for spiny, branch to hurt player 11441 cmp #Spiny ;if found 11442 beq InjurePlayer 11443 lda #Sfx_EnemyStomp ;otherwise play stomp/swim sound 11444 sta Square1SoundQueue 11445 lda Enemy_ID,x 11446 ldy #$00 ;initialize points data offset for stomped enemies 11447 cmp #FlyingCheepCheep ;branch for cheep-cheep 11448 beq EnemyStompedPts 11449 cmp #BulletBill_FrenzyVar ;branch for either bullet bill object 11450 beq EnemyStompedPts 11451 cmp #BulletBill_CannonVar 11452 beq EnemyStompedPts 11453 cmp #Podoboo ;branch for podoboo (this branch is logically impossible 11454 beq EnemyStompedPts ;for cpu to take due to earlier checking of podoboo) 11455 iny ;increment points data offset 11456 cmp #HammerBro ;branch for hammer bro 11457 beq EnemyStompedPts 11458 iny ;increment points data offset 11459 cmp #Lakitu ;branch for lakitu 11460 beq EnemyStompedPts 11461 iny ;increment points data offset 11462 cmp #Bloober ;branch if NOT bloober 11463 bne ChkForDemoteKoopa 11464 11465 EnemyStompedPts: 11466 lda StompedEnemyPtsData,y ;load points data using offset in Y 11467 jsr SetupFloateyNumber ;run sub to set floatey number controls 11468 lda Enemy_MovingDir,x 11469 pha ;save enemy movement direction to stack 11470 jsr SetStun ;run sub to kill enemy 11471 pla 11472 sta Enemy_MovingDir,x ;return enemy movement direction from stack 11473 lda #%00100000 11474 sta Enemy_State,x ;set d5 in enemy state 11475 jsr InitVStf ;nullify vertical speed, physics-related thing, 11476 sta Enemy_X_Speed,x ;and horizontal speed 11477 lda #$fd ;set player's vertical speed, to give bounce 11478 sta Player_Y_Speed 11479 rts 11480 11481 ChkForDemoteKoopa: 11482 cmp #$09 ;branch elsewhere if enemy object < $09 11483 bcc HandleStompedShellE 11484 and #%00000001 ;demote koopa paratroopas to ordinary troopas 11485 sta Enemy_ID,x 11486 ldy #$00 ;return enemy to normal state 11487 sty Enemy_State,x 11488 lda #$03 ;award 400 points to the player 11489 jsr SetupFloateyNumber 11490 jsr InitVStf ;nullify physics-related thing and vertical speed 11491 jsr EnemyFacePlayer ;turn enemy around if necessary 11492 lda DemotedKoopaXSpdData,y 11493 sta Enemy_X_Speed,x ;set appropriate moving speed based on direction 11494 jmp SBnce ;then move onto something else 11495 11496 RevivalRateData: 11497 .db $10, $0b 11498 11499 HandleStompedShellE: 11500 lda #$04 ;set defeated state for enemy 11501 sta Enemy_State,x 11502 inc StompChainCounter ;increment the stomp counter 11503 lda StompChainCounter ;add whatever is in the stomp counter 11504 clc ;to whatever is in the stomp timer 11505 adc StompTimer 11506 jsr SetupFloateyNumber ;award points accordingly 11507 inc StompTimer ;increment stomp timer of some sort 11508 ldy PrimaryHardMode ;check primary hard mode flag 11509 lda RevivalRateData,y ;load timer setting according to flag 11510 sta EnemyIntervalTimer,x ;set as enemy timer to revive stomped enemy 11511 SBnce: lda #$fc ;set player's vertical speed for bounce 11512 sta Player_Y_Speed ;and then leave!!! 11513 rts 11514 11515 ChkEnemyFaceRight: 11516 lda Enemy_MovingDir,x ;check to see if enemy is moving to the right 11517 cmp #$01 11518 bne LInj ;if not, branch 11519 jmp InjurePlayer ;otherwise go back to hurt player 11520 LInj: jsr EnemyTurnAround ;turn the enemy around, if necessary 11521 jmp InjurePlayer ;go back to hurt player 11522 11523 11524 EnemyFacePlayer: 11525 ldy #$01 ;set to move right by default 11526 jsr PlayerEnemyDiff ;get horizontal difference between player and enemy 11527 bpl SFcRt ;if enemy is to the right of player, do not increment 11528 iny ;otherwise, increment to set to move to the left 11529 SFcRt: sty Enemy_MovingDir,x ;set moving direction here 11530 dey ;then decrement to use as a proper offset 11531 rts 11532 11533 SetupFloateyNumber: 11534 sta FloateyNum_Control,x ;set number of points control for floatey numbers 11535 lda #$30 11536 sta FloateyNum_Timer,x ;set timer for floatey numbers 11537 lda Enemy_Y_Position,x 11538 sta FloateyNum_Y_Pos,x ;set vertical coordinate 11539 lda Enemy_Rel_XPos 11540 sta FloateyNum_X_Pos,x ;set horizontal coordinate and leave 11541 ExSFN: rts 11542 11543 ;------------------------------------------------------------------------------------- 11544 ;$01 - used to hold enemy offset for second enemy 11545 11546 SetBitsMask: 11547 .db %10000000, %01000000, %00100000, %00010000, %00001000, %00000100, %00000010 11548 11549 ClearBitsMask: 11550 .db %01111111, %10111111, %11011111, %11101111, %11110111, %11111011, %11111101 11551 11552 EnemiesCollision: 11553 lda FrameCounter ;check counter for d0 set 11554 lsr 11555 bcc ExSFN ;if d0 not set, leave 11556 lda AreaType 11557 beq ExSFN ;if water area type, leave 11558 lda Enemy_ID,x 11559 cmp #$15 ;if enemy object => $15, branch to leave 11560 bcs ExitECRoutine 11561 cmp #Lakitu ;if lakitu, branch to leave 11562 beq ExitECRoutine 11563 cmp #PiranhaPlant ;if piranha plant, branch to leave 11564 beq ExitECRoutine 11565 lda EnemyOffscrBitsMasked,x ;if masked offscreen bits nonzero, branch to leave 11566 bne ExitECRoutine 11567 jsr GetEnemyBoundBoxOfs ;otherwise, do sub, get appropriate bounding box offset for 11568 dex ;first enemy we're going to compare, then decrement for second 11569 bmi ExitECRoutine ;branch to leave if there are no other enemies 11570 ECLoop: stx $01 ;save enemy object buffer offset for second enemy here 11571 tya ;save first enemy's bounding box offset to stack 11572 pha 11573 lda Enemy_Flag,x ;check enemy object enable flag 11574 beq ReadyNextEnemy ;branch if flag not set 11575 lda Enemy_ID,x 11576 cmp #$15 ;check for enemy object => $15 11577 bcs ReadyNextEnemy ;branch if true 11578 cmp #Lakitu 11579 beq ReadyNextEnemy ;branch if enemy object is lakitu 11580 cmp #PiranhaPlant 11581 beq ReadyNextEnemy ;branch if enemy object is piranha plant 11582 lda EnemyOffscrBitsMasked,x 11583 bne ReadyNextEnemy ;branch if masked offscreen bits set 11584 txa ;get second enemy object's bounding box offset 11585 asl ;multiply by four, then add four 11586 asl 11587 clc 11588 adc #$04 11589 tax ;use as new contents of X 11590 jsr SprObjectCollisionCore ;do collision detection using the two enemies here 11591 ldx ObjectOffset ;use first enemy offset for X 11592 ldy $01 ;use second enemy offset for Y 11593 bcc NoEnemyCollision ;if carry clear, no collision, branch ahead of this 11594 lda Enemy_State,x 11595 ora Enemy_State,y ;check both enemy states for d7 set 11596 and #%10000000 11597 bne YesEC ;branch if at least one of them is set 11598 lda Enemy_CollisionBits,y ;load first enemy's collision-related bits 11599 and SetBitsMask,x ;check to see if bit connected to second enemy is 11600 bne ReadyNextEnemy ;already set, and move onto next enemy slot if set 11601 lda Enemy_CollisionBits,y 11602 ora SetBitsMask,x ;if the bit is not set, set it now 11603 sta Enemy_CollisionBits,y 11604 YesEC: jsr ProcEnemyCollisions ;react according to the nature of collision 11605 jmp ReadyNextEnemy ;move onto next enemy slot 11606 11607 NoEnemyCollision: 11608 lda Enemy_CollisionBits,y ;load first enemy's collision-related bits 11609 and ClearBitsMask,x ;clear bit connected to second enemy 11610 sta Enemy_CollisionBits,y ;then move onto next enemy slot 11611 11612 ReadyNextEnemy: 11613 pla ;get first enemy's bounding box offset from the stack 11614 tay ;use as Y again 11615 ldx $01 ;get and decrement second enemy's object buffer offset 11616 dex 11617 bpl ECLoop ;loop until all enemy slots have been checked 11618 11619 ExitECRoutine: 11620 ldx ObjectOffset ;get enemy object buffer offset 11621 rts ;leave 11622 11623 ProcEnemyCollisions: 11624 lda Enemy_State,y ;check both enemy states for d5 set 11625 ora Enemy_State,x 11626 and #%00100000 ;if d5 is set in either state, or both, branch 11627 bne ExitProcessEColl ;to leave and do nothing else at this point 11628 lda Enemy_State,x 11629 cmp #$06 ;if second enemy state < $06, branch elsewhere 11630 bcc ProcSecondEnemyColl 11631 lda Enemy_ID,x ;check second enemy identifier for hammer bro 11632 cmp #HammerBro ;if hammer bro found in alt state, branch to leave 11633 beq ExitProcessEColl 11634 lda Enemy_State,y ;check first enemy state for d7 set 11635 asl 11636 bcc ShellCollisions ;branch if d7 is clear 11637 lda #$06 11638 jsr SetupFloateyNumber ;award 1000 points for killing enemy 11639 jsr ShellOrBlockDefeat ;then kill enemy, then load 11640 ldy $01 ;original offset of second enemy 11641 11642 ShellCollisions: 11643 tya ;move Y to X 11644 tax 11645 jsr ShellOrBlockDefeat ;kill second enemy 11646 ldx ObjectOffset 11647 lda ShellChainCounter,x ;get chain counter for shell 11648 clc 11649 adc #$04 ;add four to get appropriate point offset 11650 ldx $01 11651 jsr SetupFloateyNumber ;award appropriate number of points for second enemy 11652 ldx ObjectOffset ;load original offset of first enemy 11653 inc ShellChainCounter,x ;increment chain counter for additional enemies 11654 11655 ExitProcessEColl: 11656 rts ;leave!!! 11657 11658 ProcSecondEnemyColl: 11659 lda Enemy_State,y ;if first enemy state < $06, branch elsewhere 11660 cmp #$06 11661 bcc MoveEOfs 11662 lda Enemy_ID,y ;check first enemy identifier for hammer bro 11663 cmp #HammerBro ;if hammer bro found in alt state, branch to leave 11664 beq ExitProcessEColl 11665 jsr ShellOrBlockDefeat ;otherwise, kill first enemy 11666 ldy $01 11667 lda ShellChainCounter,y ;get chain counter for shell 11668 clc 11669 adc #$04 ;add four to get appropriate point offset 11670 ldx ObjectOffset 11671 jsr SetupFloateyNumber ;award appropriate number of points for first enemy 11672 ldx $01 ;load original offset of second enemy 11673 inc ShellChainCounter,x ;increment chain counter for additional enemies 11674 rts ;leave!!! 11675 11676 MoveEOfs: 11677 tya ;move Y ($01) to X 11678 tax 11679 jsr EnemyTurnAround ;do the sub here using value from $01 11680 ldx ObjectOffset ;then do it again using value from $08 11681 11682 EnemyTurnAround: 11683 lda Enemy_ID,x ;check for specific enemies 11684 cmp #PiranhaPlant 11685 beq ExTA ;if piranha plant, leave 11686 cmp #Lakitu 11687 beq ExTA ;if lakitu, leave 11688 cmp #HammerBro 11689 beq ExTA ;if hammer bro, leave 11690 cmp #Spiny 11691 beq RXSpd ;if spiny, turn it around 11692 cmp #GreenParatroopaJump 11693 beq RXSpd ;if green paratroopa, turn it around 11694 cmp #$07 11695 bcs ExTA ;if any OTHER enemy object => $07, leave 11696 RXSpd: lda Enemy_X_Speed,x ;load horizontal speed 11697 eor #$ff ;get two's compliment for horizontal speed 11698 tay 11699 iny 11700 sty Enemy_X_Speed,x ;store as new horizontal speed 11701 lda Enemy_MovingDir,x 11702 eor #%00000011 ;invert moving direction and store, then leave 11703 sta Enemy_MovingDir,x ;thus effectively turning the enemy around 11704 ExTA: rts ;leave!!! 11705 11706 ;------------------------------------------------------------------------------------- 11707 ;$00 - vertical position of platform 11708 11709 LargePlatformCollision: 11710 lda #$ff ;save value here 11711 sta PlatformCollisionFlag,x 11712 lda TimerControl ;check master timer control 11713 bne ExLPC ;if set, branch to leave 11714 lda Enemy_State,x ;if d7 set in object state, 11715 bmi ExLPC ;branch to leave 11716 lda Enemy_ID,x 11717 cmp #$24 ;check enemy object identifier for 11718 bne ChkForPlayerC_LargeP ;balance platform, branch if not found 11719 lda Enemy_State,x 11720 tax ;set state as enemy offset here 11721 jsr ChkForPlayerC_LargeP ;perform code with state offset, then original offset, in X 11722 11723 ChkForPlayerC_LargeP: 11724 jsr CheckPlayerVertical ;figure out if player is below a certain point 11725 bcs ExLPC ;or offscreen, branch to leave if true 11726 txa 11727 jsr GetEnemyBoundBoxOfsArg ;get bounding box offset in Y 11728 lda Enemy_Y_Position,x ;store vertical coordinate in 11729 sta $00 ;temp variable for now 11730 txa ;send offset we're on to the stack 11731 pha 11732 jsr PlayerCollisionCore ;do player-to-platform collision detection 11733 pla ;retrieve offset from the stack 11734 tax 11735 bcc ExLPC ;if no collision, branch to leave 11736 jsr ProcLPlatCollisions ;otherwise collision, perform sub 11737 ExLPC: ldx ObjectOffset ;get enemy object buffer offset and leave 11738 rts 11739 11740 ;-------------------------------- 11741 ;$00 - counter for bounding boxes 11742 11743 SmallPlatformCollision: 11744 lda TimerControl ;if master timer control set, 11745 bne ExSPC ;branch to leave 11746 sta PlatformCollisionFlag,x ;otherwise initialize collision flag 11747 jsr CheckPlayerVertical ;do a sub to see if player is below a certain point 11748 bcs ExSPC ;or entirely offscreen, and branch to leave if true 11749 lda #$02 11750 sta $00 ;load counter here for 2 bounding boxes 11751 11752 ChkSmallPlatLoop: 11753 ldx ObjectOffset ;get enemy object offset 11754 jsr GetEnemyBoundBoxOfs ;get bounding box offset in Y 11755 and #%00000010 ;if d1 of offscreen lower nybble bits was set 11756 bne ExSPC ;then branch to leave 11757 lda BoundingBox_UL_YPos,y ;check top of platform's bounding box for being 11758 cmp #$20 ;above a specific point 11759 bcc MoveBoundBox ;if so, branch, don't do collision detection 11760 jsr PlayerCollisionCore ;otherwise, perform player-to-platform collision detection 11761 bcs ProcSPlatCollisions ;skip ahead if collision 11762 11763 MoveBoundBox: 11764 lda BoundingBox_UL_YPos,y ;move bounding box vertical coordinates 11765 clc ;128 pixels downwards 11766 adc #$80 11767 sta BoundingBox_UL_YPos,y 11768 lda BoundingBox_DR_YPos,y 11769 clc 11770 adc #$80 11771 sta BoundingBox_DR_YPos,y 11772 dec $00 ;decrement counter we set earlier 11773 bne ChkSmallPlatLoop ;loop back until both bounding boxes are checked 11774 ExSPC: ldx ObjectOffset ;get enemy object buffer offset, then leave 11775 rts 11776 11777 ;-------------------------------- 11778 11779 ProcSPlatCollisions: 11780 ldx ObjectOffset ;return enemy object buffer offset to X, then continue 11781 11782 ProcLPlatCollisions: 11783 lda BoundingBox_DR_YPos,y ;get difference by subtracting the top 11784 sec ;of the player's bounding box from the bottom 11785 sbc BoundingBox_UL_YPos ;of the platform's bounding box 11786 cmp #$04 ;if difference too large or negative, 11787 bcs ChkForTopCollision ;branch, do not alter vertical speed of player 11788 lda Player_Y_Speed ;check to see if player's vertical speed is moving down 11789 bpl ChkForTopCollision ;if so, don't mess with it 11790 lda #$01 ;otherwise, set vertical 11791 sta Player_Y_Speed ;speed of player to kill jump 11792 11793 ChkForTopCollision: 11794 lda BoundingBox_DR_YPos ;get difference by subtracting the top 11795 sec ;of the platform's bounding box from the bottom 11796 sbc BoundingBox_UL_YPos,y ;of the player's bounding box 11797 cmp #$06 11798 bcs PlatformSideCollisions ;if difference not close enough, skip all of this 11799 lda Player_Y_Speed 11800 bmi PlatformSideCollisions ;if player's vertical speed moving upwards, skip this 11801 lda $00 ;get saved bounding box counter from earlier 11802 ldy Enemy_ID,x 11803 cpy #$2b ;if either of the two small platform objects are found, 11804 beq SetCollisionFlag ;regardless of which one, branch to use bounding box counter 11805 cpy #$2c ;as contents of collision flag 11806 beq SetCollisionFlag 11807 txa ;otherwise use enemy object buffer offset 11808 11809 SetCollisionFlag: 11810 ldx ObjectOffset ;get enemy object buffer offset 11811 sta PlatformCollisionFlag,x ;save either bounding box counter or enemy offset here 11812 lda #$00 11813 sta Player_State ;set player state to normal then leave 11814 rts 11815 11816 PlatformSideCollisions: 11817 lda #$01 ;set value here to indicate possible horizontal 11818 sta $00 ;collision on left side of platform 11819 lda BoundingBox_DR_XPos ;get difference by subtracting platform's left edge 11820 sec ;from player's right edge 11821 sbc BoundingBox_UL_XPos,y 11822 cmp #$08 ;if difference close enough, skip all of this 11823 bcc SideC 11824 inc $00 ;otherwise increment value set here for right side collision 11825 lda BoundingBox_DR_XPos,y ;get difference by subtracting player's left edge 11826 clc ;from platform's right edge 11827 sbc BoundingBox_UL_XPos 11828 cmp #$09 ;if difference not close enough, skip subroutine 11829 bcs NoSideC ;and instead branch to leave (no collision) 11830 SideC: jsr ImpedePlayerMove ;deal with horizontal collision 11831 NoSideC: ldx ObjectOffset ;return with enemy object buffer offset 11832 rts 11833 11834 ;------------------------------------------------------------------------------------- 11835 11836 PlayerPosSPlatData: 11837 .db $80, $00 11838 11839 PositionPlayerOnS_Plat: 11840 tay ;use bounding box counter saved in collision flag 11841 lda Enemy_Y_Position,x ;for offset 11842 clc ;add positioning data using offset to the vertical 11843 adc PlayerPosSPlatData-1,y ;coordinate 11844 .db $2c ;BIT instruction opcode 11845 11846 PositionPlayerOnVPlat: 11847 lda Enemy_Y_Position,x ;get vertical coordinate 11848 ldy GameEngineSubroutine 11849 cpy #$0b ;if certain routine being executed on this frame, 11850 beq ExPlPos ;skip all of this 11851 ldy Enemy_Y_HighPos,x 11852 cpy #$01 ;if vertical high byte offscreen, skip this 11853 bne ExPlPos 11854 sec ;subtract 32 pixels from vertical coordinate 11855 sbc #$20 ;for the player object's height 11856 sta Player_Y_Position ;save as player's new vertical coordinate 11857 tya 11858 sbc #$00 ;subtract borrow and store as player's 11859 sta Player_Y_HighPos ;new vertical high byte 11860 lda #$00 11861 sta Player_Y_Speed ;initialize vertical speed and low byte of force 11862 sta Player_Y_MoveForce ;and then leave 11863 ExPlPos: rts 11864 11865 ;------------------------------------------------------------------------------------- 11866 11867 CheckPlayerVertical: 11868 lda Player_OffscreenBits ;if player object is completely offscreen 11869 cmp #$f0 ;vertically, leave this routine 11870 bcs ExCPV 11871 ldy Player_Y_HighPos ;if player high vertical byte is not 11872 dey ;within the screen, leave this routine 11873 bne ExCPV 11874 lda Player_Y_Position ;if on the screen, check to see how far down 11875 cmp #$d0 ;the player is vertically 11876 ExCPV: rts 11877 11878 ;------------------------------------------------------------------------------------- 11879 11880 GetEnemyBoundBoxOfs: 11881 lda ObjectOffset ;get enemy object buffer offset 11882 11883 GetEnemyBoundBoxOfsArg: 11884 asl ;multiply A by four, then add four 11885 asl ;to skip player's bounding box 11886 clc 11887 adc #$04 11888 tay ;send to Y 11889 lda Enemy_OffscreenBits ;get offscreen bits for enemy object 11890 and #%00001111 ;save low nybble 11891 cmp #%00001111 ;check for all bits set 11892 rts 11893 11894 ;------------------------------------------------------------------------------------- 11895 ;$00-$01 - used to hold many values, essentially temp variables 11896 ;$04 - holds lower nybble of vertical coordinate from block buffer routine 11897 ;$eb - used to hold block buffer adder 11898 11899 PlayerBGUpperExtent: 11900 .db $20, $10 11901 11902 PlayerBGCollision: 11903 lda DisableCollisionDet ;if collision detection disabled flag set, 11904 bne ExPBGCol ;branch to leave 11905 lda GameEngineSubroutine 11906 cmp #$0b ;if running routine #11 or $0b 11907 beq ExPBGCol ;branch to leave 11908 cmp #$04 11909 bcc ExPBGCol ;if running routines $00-$03 branch to leave 11910 lda #$01 ;load default player state for swimming 11911 ldy SwimmingFlag ;if swimming flag set, 11912 bne SetPSte ;branch ahead to set default state 11913 lda Player_State ;if player in normal state, 11914 beq SetFallS ;branch to set default state for falling 11915 cmp #$03 11916 bne ChkOnScr ;if in any other state besides climbing, skip to next part 11917 SetFallS: lda #$02 ;load default player state for falling 11918 SetPSte: sta Player_State ;set whatever player state is appropriate 11919 ChkOnScr: lda Player_Y_HighPos 11920 cmp #$01 ;check player's vertical high byte for still on the screen 11921 bne ExPBGCol ;branch to leave if not 11922 lda #$ff 11923 sta Player_CollisionBits ;initialize player's collision flag 11924 lda Player_Y_Position 11925 cmp #$cf ;check player's vertical coordinate 11926 bcc ChkCollSize ;if not too close to the bottom of screen, continue 11927 ExPBGCol: rts ;otherwise leave 11928 11929 ChkCollSize: 11930 ldy #$02 ;load default offset 11931 lda CrouchingFlag 11932 bne GBBAdr ;if player crouching, skip ahead 11933 lda PlayerSize 11934 bne GBBAdr ;if player small, skip ahead 11935 dey ;otherwise decrement offset for big player not crouching 11936 lda SwimmingFlag 11937 bne GBBAdr ;if swimming flag set, skip ahead 11938 dey ;otherwise decrement offset 11939 GBBAdr: lda BlockBufferAdderData,y ;get value using offset 11940 sta $eb ;store value here 11941 tay ;put value into Y, as offset for block buffer routine 11942 ldx PlayerSize ;get player's size as offset 11943 lda CrouchingFlag 11944 beq HeadChk ;if player not crouching, branch ahead 11945 inx ;otherwise increment size as offset 11946 HeadChk: lda Player_Y_Position ;get player's vertical coordinate 11947 cmp PlayerBGUpperExtent,x ;compare with upper extent value based on offset 11948 bcc DoFootCheck ;if player is too high, skip this part 11949 jsr BlockBufferColli_Head ;do player-to-bg collision detection on top of 11950 beq DoFootCheck ;player, and branch if nothing above player's head 11951 jsr CheckForCoinMTiles ;check to see if player touched coin with their head 11952 bcs AwardTouchedCoin ;if so, branch to some other part of code 11953 ldy Player_Y_Speed ;check player's vertical speed 11954 bpl DoFootCheck ;if player not moving upwards, branch elsewhere 11955 ldy $04 ;check lower nybble of vertical coordinate returned 11956 cpy #$04 ;from collision detection routine 11957 bcc DoFootCheck ;if low nybble < 4, branch 11958 jsr CheckForSolidMTiles ;check to see what player's head bumped on 11959 bcs SolidOrClimb ;if player collided with solid metatile, branch 11960 ldy AreaType ;otherwise check area type 11961 beq NYSpd ;if water level, branch ahead 11962 ldy BlockBounceTimer ;if block bounce timer not expired, 11963 bne NYSpd ;branch ahead, do not process collision 11964 jsr PlayerHeadCollision ;otherwise do a sub to process collision 11965 jmp DoFootCheck ;jump ahead to skip these other parts here 11966 11967 SolidOrClimb: 11968 cmp #$26 ;if climbing metatile, 11969 beq NYSpd ;branch ahead and do not play sound 11970 lda #Sfx_Bump 11971 sta Square1SoundQueue ;otherwise load bump sound 11972 NYSpd: lda #$01 ;set player's vertical speed to nullify 11973 sta Player_Y_Speed ;jump or swim 11974 11975 DoFootCheck: 11976 ldy $eb ;get block buffer adder offset 11977 lda Player_Y_Position 11978 cmp #$cf ;check to see how low player is 11979 bcs DoPlayerSideCheck ;if player is too far down on screen, skip all of this 11980 jsr BlockBufferColli_Feet ;do player-to-bg collision detection on bottom left of player 11981 jsr CheckForCoinMTiles ;check to see if player touched coin with their left foot 11982 bcs AwardTouchedCoin ;if so, branch to some other part of code 11983 pha ;save bottom left metatile to stack 11984 jsr BlockBufferColli_Feet ;do player-to-bg collision detection on bottom right of player 11985 sta $00 ;save bottom right metatile here 11986 pla 11987 sta $01 ;pull bottom left metatile and save here 11988 bne ChkFootMTile ;if anything here, skip this part 11989 lda $00 ;otherwise check for anything in bottom right metatile 11990 beq DoPlayerSideCheck ;and skip ahead if not 11991 jsr CheckForCoinMTiles ;check to see if player touched coin with their right foot 11992 bcc ChkFootMTile ;if not, skip unconditional jump and continue code 11993 11994 AwardTouchedCoin: 11995 jmp HandleCoinMetatile ;follow the code to erase coin and award to player 1 coin 11996 11997 ChkFootMTile: 11998 jsr CheckForClimbMTiles ;check to see if player landed on climbable metatiles 11999 bcs DoPlayerSideCheck ;if so, branch 12000 ldy Player_Y_Speed ;check player's vertical speed 12001 bmi DoPlayerSideCheck ;if player moving upwards, branch 12002 cmp #$c5 12003 bne ContChk ;if player did not touch axe, skip ahead 12004 jmp HandleAxeMetatile ;otherwise jump to set modes of operation 12005 ContChk: jsr ChkInvisibleMTiles ;do sub to check for hidden coin or 1-up blocks 12006 beq DoPlayerSideCheck ;if either found, branch 12007 ldy JumpspringAnimCtrl ;if jumpspring animating right now, 12008 bne InitSteP ;branch ahead 12009 ldy $04 ;check lower nybble of vertical coordinate returned 12010 cpy #$05 ;from collision detection routine 12011 bcc LandPlyr ;if lower nybble < 5, branch 12012 lda Player_MovingDir 12013 sta $00 ;use player's moving direction as temp variable 12014 jmp ImpedePlayerMove ;jump to impede player's movement in that direction 12015 LandPlyr: jsr ChkForLandJumpSpring ;do sub to check for jumpspring metatiles and deal with it 12016 lda #$f0 12017 and Player_Y_Position ;mask out lower nybble of player's vertical position 12018 sta Player_Y_Position ;and store as new vertical position to land player properly 12019 jsr HandlePipeEntry ;do sub to process potential pipe entry 12020 lda #$00 12021 sta Player_Y_Speed ;initialize vertical speed and fractional 12022 sta Player_Y_MoveForce ;movement force to stop player's vertical movement 12023 sta StompChainCounter ;initialize enemy stomp counter 12024 InitSteP: lda #$00 12025 sta Player_State ;set player's state to normal 12026 12027 DoPlayerSideCheck: 12028 ldy $eb ;get block buffer adder offset 12029 iny 12030 iny ;increment offset 2 bytes to use adders for side collisions 12031 lda #$02 ;set value here to be used as counter 12032 sta $00 12033 12034 SideCheckLoop: 12035 iny ;move onto the next one 12036 sty $eb ;store it 12037 lda Player_Y_Position 12038 cmp #$20 ;check player's vertical position 12039 bcc BHalf ;if player is in status bar area, branch ahead to skip this part 12040 cmp #$e4 12041 bcs ExSCH ;branch to leave if player is too far down 12042 jsr BlockBufferColli_Side ;do player-to-bg collision detection on one half of player 12043 beq BHalf ;branch ahead if nothing found 12044 cmp #$1c ;otherwise check for pipe metatiles 12045 beq BHalf ;if collided with sideways pipe (top), branch ahead 12046 cmp #$6b 12047 beq BHalf ;if collided with water pipe (top), branch ahead 12048 jsr CheckForClimbMTiles ;do sub to see if player bumped into anything climbable 12049 bcc CheckSideMTiles ;if not, branch to alternate section of code 12050 BHalf: ldy $eb ;load block adder offset 12051 iny ;increment it 12052 lda Player_Y_Position ;get player's vertical position 12053 cmp #$08 12054 bcc ExSCH ;if too high, branch to leave 12055 cmp #$d0 12056 bcs ExSCH ;if too low, branch to leave 12057 jsr BlockBufferColli_Side ;do player-to-bg collision detection on other half of player 12058 bne CheckSideMTiles ;if something found, branch 12059 dec $00 ;otherwise decrement counter 12060 bne SideCheckLoop ;run code until both sides of player are checked 12061 ExSCH: rts ;leave 12062 12063 CheckSideMTiles: 12064 jsr ChkInvisibleMTiles ;check for hidden or coin 1-up blocks 12065 beq ExCSM ;branch to leave if either found 12066 jsr CheckForClimbMTiles ;check for climbable metatiles 12067 bcc ContSChk ;if not found, skip and continue with code 12068 jmp HandleClimbing ;otherwise jump to handle climbing 12069 ContSChk: jsr CheckForCoinMTiles ;check to see if player touched coin 12070 bcs HandleCoinMetatile ;if so, execute code to erase coin and award to player 1 coin 12071 jsr ChkJumpspringMetatiles ;check for jumpspring metatiles 12072 bcc ChkPBtm ;if not found, branch ahead to continue cude 12073 lda JumpspringAnimCtrl ;otherwise check jumpspring animation control 12074 bne ExCSM ;branch to leave if set 12075 jmp StopPlayerMove ;otherwise jump to impede player's movement 12076 ChkPBtm: ldy Player_State ;get player's state 12077 cpy #$00 ;check for player's state set to normal 12078 bne StopPlayerMove ;if not, branch to impede player's movement 12079 ldy PlayerFacingDir ;get player's facing direction 12080 dey 12081 bne StopPlayerMove ;if facing left, branch to impede movement 12082 cmp #$6c ;otherwise check for pipe metatiles 12083 beq PipeDwnS ;if collided with sideways pipe (bottom), branch 12084 cmp #$1f ;if collided with water pipe (bottom), continue 12085 bne StopPlayerMove ;otherwise branch to impede player's movement 12086 PipeDwnS: lda Player_SprAttrib ;check player's attributes 12087 bne PlyrPipe ;if already set, branch, do not play sound again 12088 ldy #Sfx_PipeDown_Injury 12089 sty Square1SoundQueue ;otherwise load pipedown/injury sound 12090 PlyrPipe: ora #%00100000 12091 sta Player_SprAttrib ;set background priority bit in player attributes 12092 lda Player_X_Position 12093 and #%00001111 ;get lower nybble of player's horizontal coordinate 12094 beq ChkGERtn ;if at zero, branch ahead to skip this part 12095 ldy #$00 ;set default offset for timer setting data 12096 lda ScreenLeft_PageLoc ;load page location for left side of screen 12097 beq SetCATmr ;if at page zero, use default offset 12098 iny ;otherwise increment offset 12099 SetCATmr: lda AreaChangeTimerData,y ;set timer for change of area as appropriate 12100 sta ChangeAreaTimer 12101 ChkGERtn: lda GameEngineSubroutine ;get number of game engine routine running 12102 cmp #$07 12103 beq ExCSM ;if running player entrance routine or 12104 cmp #$08 ;player control routine, go ahead and branch to leave 12105 bne ExCSM 12106 lda #$02 12107 sta GameEngineSubroutine ;otherwise set sideways pipe entry routine to run 12108 rts ;and leave 12109 12110 ;-------------------------------- 12111 ;$02 - high nybble of vertical coordinate from block buffer 12112 ;$04 - low nybble of horizontal coordinate from block buffer 12113 ;$06-$07 - block buffer address 12114 12115 StopPlayerMove: 12116 jsr ImpedePlayerMove ;stop player's movement 12117 ExCSM: rts ;leave 12118 12119 AreaChangeTimerData: 12120 .db $a0, $34 12121 12122 HandleCoinMetatile: 12123 jsr ErACM ;do sub to erase coin metatile from block buffer 12124 inc CoinTallyFor1Ups ;increment coin tally used for 1-up blocks 12125 jmp GiveOneCoin ;update coin amount and tally on the screen 12126 12127 HandleAxeMetatile: 12128 lda #$00 12129 sta OperMode_Task ;reset secondary mode 12130 lda #$02 12131 sta OperMode ;set primary mode to autoctrl mode 12132 lda #$18 12133 sta Player_X_Speed ;set horizontal speed and continue to erase axe metatile 12134 ErACM: ldy $02 ;load vertical high nybble offset for block buffer 12135 lda #$00 ;load blank metatile 12136 sta ($06),y ;store to remove old contents from block buffer 12137 jmp RemoveCoin_Axe ;update the screen accordingly 12138 12139 ;-------------------------------- 12140 ;$02 - high nybble of vertical coordinate from block buffer 12141 ;$04 - low nybble of horizontal coordinate from block buffer 12142 ;$06-$07 - block buffer address 12143 12144 ClimbXPosAdder: 12145 .db $f9, $07 12146 12147 ClimbPLocAdder: 12148 .db $ff, $00 12149 12150 FlagpoleYPosData: 12151 .db $18, $22, $50, $68, $90 12152 12153 HandleClimbing: 12154 ldy $04 ;check low nybble of horizontal coordinate returned from 12155 cpy #$06 ;collision detection routine against certain values, this 12156 bcc ExHC ;makes actual physical part of vine or flagpole thinner 12157 cpy #$0a ;than 16 pixels 12158 bcc ChkForFlagpole 12159 ExHC: rts ;leave if too far left or too far right 12160 12161 ChkForFlagpole: 12162 cmp #$24 ;check climbing metatiles 12163 beq FlagpoleCollision ;branch if flagpole ball found 12164 cmp #$25 12165 bne VineCollision ;branch to alternate code if flagpole shaft not found 12166 12167 FlagpoleCollision: 12168 lda GameEngineSubroutine 12169 cmp #$05 ;check for end-of-level routine running 12170 beq PutPlayerOnVine ;if running, branch to end of climbing code 12171 lda #$01 12172 sta PlayerFacingDir ;set player's facing direction to right 12173 inc ScrollLock ;set scroll lock flag 12174 lda GameEngineSubroutine 12175 cmp #$04 ;check for flagpole slide routine running 12176 beq RunFR ;if running, branch to end of flagpole code here 12177 lda #BulletBill_CannonVar ;load identifier for bullet bills (cannon variant) 12178 jsr KillEnemies ;get rid of them 12179 lda #Silence 12180 sta EventMusicQueue ;silence music 12181 lsr 12182 sta FlagpoleSoundQueue ;load flagpole sound into flagpole sound queue 12183 ldx #$04 ;start at end of vertical coordinate data 12184 lda Player_Y_Position 12185 sta FlagpoleCollisionYPos ;store player's vertical coordinate here to be used later 12186 12187 ChkFlagpoleYPosLoop: 12188 cmp FlagpoleYPosData,x ;compare with current vertical coordinate data 12189 bcs MtchF ;if player's => current, branch to use current offset 12190 dex ;otherwise decrement offset to use 12191 bne ChkFlagpoleYPosLoop ;do this until all data is checked (use last one if all checked) 12192 MtchF: stx FlagpoleScore ;store offset here to be used later 12193 RunFR: lda #$04 12194 sta GameEngineSubroutine ;set value to run flagpole slide routine 12195 jmp PutPlayerOnVine ;jump to end of climbing code 12196 12197 VineCollision: 12198 cmp #$26 ;check for climbing metatile used on vines 12199 bne PutPlayerOnVine 12200 lda Player_Y_Position ;check player's vertical coordinate 12201 cmp #$20 ;for being in status bar area 12202 bcs PutPlayerOnVine ;branch if not that far up 12203 lda #$01 12204 sta GameEngineSubroutine ;otherwise set to run autoclimb routine next frame 12205 12206 PutPlayerOnVine: 12207 lda #$03 ;set player state to climbing 12208 sta Player_State 12209 lda #$00 ;nullify player's horizontal speed 12210 sta Player_X_Speed ;and fractional horizontal movement force 12211 sta Player_X_MoveForce 12212 lda Player_X_Position ;get player's horizontal coordinate 12213 sec 12214 sbc ScreenLeft_X_Pos ;subtract from left side horizontal coordinate 12215 cmp #$10 12216 bcs SetVXPl ;if 16 or more pixels difference, do not alter facing direction 12217 lda #$02 12218 sta PlayerFacingDir ;otherwise force player to face left 12219 SetVXPl: ldy PlayerFacingDir ;get current facing direction, use as offset 12220 lda $06 ;get low byte of block buffer address 12221 asl 12222 asl ;move low nybble to high 12223 asl 12224 asl 12225 clc 12226 adc ClimbXPosAdder-1,y ;add pixels depending on facing direction 12227 sta Player_X_Position ;store as player's horizontal coordinate 12228 lda $06 ;get low byte of block buffer address again 12229 bne ExPVne ;if not zero, branch 12230 lda ScreenRight_PageLoc ;load page location of right side of screen 12231 clc 12232 adc ClimbPLocAdder-1,y ;add depending on facing location 12233 sta Player_PageLoc ;store as player's page location 12234 ExPVne: rts ;finally, we're done! 12235 12236 ;-------------------------------- 12237 12238 ChkInvisibleMTiles: 12239 cmp #$5f ;check for hidden coin block 12240 beq ExCInvT ;branch to leave if found 12241 cmp #$60 ;check for hidden 1-up block 12242 ExCInvT: rts ;leave with zero flag set if either found 12243 12244 ;-------------------------------- 12245 ;$00-$01 - used to hold bottom right and bottom left metatiles (in that order) 12246 ;$00 - used as flag by ImpedePlayerMove to restrict specific movement 12247 12248 ChkForLandJumpSpring: 12249 jsr ChkJumpspringMetatiles ;do sub to check if player landed on jumpspring 12250 bcc ExCJSp ;if carry not set, jumpspring not found, therefore leave 12251 lda #$70 12252 sta VerticalForce ;otherwise set vertical movement force for player 12253 lda #$f9 12254 sta JumpspringForce ;set default jumpspring force 12255 lda #$03 12256 sta JumpspringTimer ;set jumpspring timer to be used later 12257 lsr 12258 sta JumpspringAnimCtrl ;set jumpspring animation control to start animating 12259 ExCJSp: rts ;and leave 12260 12261 ChkJumpspringMetatiles: 12262 cmp #$67 ;check for top jumpspring metatile 12263 beq JSFnd ;branch to set carry if found 12264 cmp #$68 ;check for bottom jumpspring metatile 12265 clc ;clear carry flag 12266 bne NoJSFnd ;branch to use cleared carry if not found 12267 JSFnd: sec ;set carry if found 12268 NoJSFnd: rts ;leave 12269 12270 HandlePipeEntry: 12271 lda Up_Down_Buttons ;check saved controller bits from earlier 12272 and #%00000100 ;for pressing down 12273 beq ExPipeE ;if not pressing down, branch to leave 12274 lda $00 12275 cmp #$11 ;check right foot metatile for warp pipe right metatile 12276 bne ExPipeE ;branch to leave if not found 12277 lda $01 12278 cmp #$10 ;check left foot metatile for warp pipe left metatile 12279 bne ExPipeE ;branch to leave if not found 12280 lda #$30 12281 sta ChangeAreaTimer ;set timer for change of area 12282 lda #$03 12283 sta GameEngineSubroutine ;set to run vertical pipe entry routine on next frame 12284 lda #Sfx_PipeDown_Injury 12285 sta Square1SoundQueue ;load pipedown/injury sound 12286 lda #%00100000 12287 sta Player_SprAttrib ;set background priority bit in player's attributes 12288 lda WarpZoneControl ;check warp zone control 12289 beq ExPipeE ;branch to leave if none found 12290 and #%00000011 ;mask out all but 2 LSB 12291 asl 12292 asl ;multiply by four 12293 tax ;save as offset to warp zone numbers (starts at left pipe) 12294 lda Player_X_Position ;get player's horizontal position 12295 cmp #$60 12296 bcc GetWNum ;if player at left, not near middle, use offset and skip ahead 12297 inx ;otherwise increment for middle pipe 12298 cmp #$a0 12299 bcc GetWNum ;if player at middle, but not too far right, use offset and skip 12300 inx ;otherwise increment for last pipe 12301 GetWNum: ldy WarpZoneNumbers,x ;get warp zone numbers 12302 dey ;decrement for use as world number 12303 sty WorldNumber ;store as world number and offset 12304 ldx WorldAddrOffsets,y ;get offset to where this world's area offsets are 12305 lda AreaAddrOffsets,x ;get area offset based on world offset 12306 sta AreaPointer ;store area offset here to be used to change areas 12307 lda #Silence 12308 sta EventMusicQueue ;silence music 12309 lda #$00 12310 sta EntrancePage ;initialize starting page number 12311 sta AreaNumber ;initialize area number used for area address offset 12312 sta LevelNumber ;initialize level number used for world display 12313 sta AltEntranceControl ;initialize mode of entry 12314 inc Hidden1UpFlag ;set flag for hidden 1-up blocks 12315 inc FetchNewGameTimerFlag ;set flag to load new game timer 12316 ExPipeE: rts ;leave!!! 12317 12318 ImpedePlayerMove: 12319 lda #$00 ;initialize value here 12320 ldy Player_X_Speed ;get player's horizontal speed 12321 ldx $00 ;check value set earlier for 12322 dex ;left side collision 12323 bne RImpd ;if right side collision, skip this part 12324 inx ;return value to X 12325 cpy #$00 ;if player moving to the left, 12326 bmi ExIPM ;branch to invert bit and leave 12327 lda #$ff ;otherwise load A with value to be used later 12328 jmp NXSpd ;and jump to affect movement 12329 RImpd: ldx #$02 ;return $02 to X 12330 cpy #$01 ;if player moving to the right, 12331 bpl ExIPM ;branch to invert bit and leave 12332 lda #$01 ;otherwise load A with value to be used here 12333 NXSpd: ldy #$10 12334 sty SideCollisionTimer ;set timer of some sort 12335 ldy #$00 12336 sty Player_X_Speed ;nullify player's horizontal speed 12337 cmp #$00 ;if value set in A not set to $ff, 12338 bpl PlatF ;branch ahead, do not decrement Y 12339 dey ;otherwise decrement Y now 12340 PlatF: sty $00 ;store Y as high bits of horizontal adder 12341 clc 12342 adc Player_X_Position ;add contents of A to player's horizontal 12343 sta Player_X_Position ;position to move player left or right 12344 lda Player_PageLoc 12345 adc $00 ;add high bits and carry to 12346 sta Player_PageLoc ;page location if necessary 12347 ExIPM: txa ;invert contents of X 12348 eor #$ff 12349 and Player_CollisionBits ;mask out bit that was set here 12350 sta Player_CollisionBits ;store to clear bit 12351 rts 12352 12353 ;-------------------------------- 12354 12355 SolidMTileUpperExt: 12356 .db $10, $61, $88, $c4 12357 12358 CheckForSolidMTiles: 12359 jsr GetMTileAttrib ;find appropriate offset based on metatile's 2 MSB 12360 cmp SolidMTileUpperExt,x ;compare current metatile with solid metatiles 12361 rts 12362 12363 ClimbMTileUpperExt: 12364 .db $24, $6d, $8a, $c6 12365 12366 CheckForClimbMTiles: 12367 jsr GetMTileAttrib ;find appropriate offset based on metatile's 2 MSB 12368 cmp ClimbMTileUpperExt,x ;compare current metatile with climbable metatiles 12369 rts 12370 12371 CheckForCoinMTiles: 12372 cmp #$c2 ;check for regular coin 12373 beq CoinSd ;branch if found 12374 cmp #$c3 ;check for underwater coin 12375 beq CoinSd ;branch if found 12376 clc ;otherwise clear carry and leave 12377 rts 12378 CoinSd: lda #Sfx_CoinGrab 12379 sta Square2SoundQueue ;load coin grab sound and leave 12380 rts 12381 12382 GetMTileAttrib: 12383 tay ;save metatile value into Y 12384 and #%11000000 ;mask out all but 2 MSB 12385 asl 12386 rol ;shift and rotate d7-d6 to d1-d0 12387 rol 12388 tax ;use as offset for metatile data 12389 tya ;get original metatile value back 12390 ExEBG: rts ;leave 12391 12392 ;------------------------------------------------------------------------------------- 12393 ;$06-$07 - address from block buffer routine 12394 12395 EnemyBGCStateData: 12396 .db $01, $01, $02, $02, $02, $05 12397 12398 EnemyBGCXSpdData: 12399 .db $10, $f0 12400 12401 EnemyToBGCollisionDet: 12402 lda Enemy_State,x ;check enemy state for d6 set 12403 and #%00100000 12404 bne ExEBG ;if set, branch to leave 12405 jsr SubtEnemyYPos ;otherwise, do a subroutine here 12406 bcc ExEBG ;if enemy vertical coord + 62 < 68, branch to leave 12407 ldy Enemy_ID,x 12408 cpy #Spiny ;if enemy object is not spiny, branch elsewhere 12409 bne DoIDCheckBGColl 12410 lda Enemy_Y_Position,x 12411 cmp #$25 ;if enemy vertical coordinate < 36 branch to leave 12412 bcc ExEBG 12413 12414 DoIDCheckBGColl: 12415 cpy #GreenParatroopaJump ;check for some other enemy object 12416 bne HBChk ;branch if not found 12417 jmp EnemyJump ;otherwise jump elsewhere 12418 HBChk: cpy #HammerBro ;check for hammer bro 12419 bne CInvu ;branch if not found 12420 jmp HammerBroBGColl ;otherwise jump elsewhere 12421 CInvu: cpy #Spiny ;if enemy object is spiny, branch 12422 beq YesIn 12423 cpy #PowerUpObject ;if special power-up object, branch 12424 beq YesIn 12425 cpy #$07 ;if enemy object =>$07, branch to leave 12426 bcs ExEBGChk 12427 YesIn: jsr ChkUnderEnemy ;if enemy object < $07, or = $12 or $2e, do this sub 12428 bne HandleEToBGCollision ;if block underneath enemy, branch 12429 12430 NoEToBGCollision: 12431 jmp ChkForRedKoopa ;otherwise skip and do something else 12432 12433 ;-------------------------------- 12434 ;$02 - vertical coordinate from block buffer routine 12435 12436 HandleEToBGCollision: 12437 jsr ChkForNonSolids ;if something is underneath enemy, find out what 12438 beq NoEToBGCollision ;if blank $26, coins, or hidden blocks, jump, enemy falls through 12439 cmp #$23 12440 bne LandEnemyProperly ;check for blank metatile $23 and branch if not found 12441 ldy $02 ;get vertical coordinate used to find block 12442 lda #$00 ;store default blank metatile in that spot so we won't 12443 sta ($06),y ;trigger this routine accidentally again 12444 lda Enemy_ID,x 12445 cmp #$15 ;if enemy object => $15, branch ahead 12446 bcs ChkToStunEnemies 12447 cmp #Goomba ;if enemy object not goomba, branch ahead of this routine 12448 bne GiveOEPoints 12449 jsr KillEnemyAboveBlock ;if enemy object IS goomba, do this sub 12450 12451 GiveOEPoints: 12452 lda #$01 ;award 100 points for hitting block beneath enemy 12453 jsr SetupFloateyNumber 12454 12455 ChkToStunEnemies: 12456 cmp #$09 ;perform many comparisons on enemy object identifier 12457 bcc SetStun 12458 cmp #$11 ;if the enemy object identifier is equal to the values 12459 bcs SetStun ;$09, $0e, $0f or $10, it will be modified, and not 12460 cmp #$0a ;modified if not any of those values, note that piranha plant will 12461 bcc Demote ;always fail this test because A will still have vertical 12462 cmp #PiranhaPlant ;coordinate from previous addition, also these comparisons 12463 bcc SetStun ;are only necessary if branching from $d7a1 12464 Demote: and #%00000001 ;erase all but LSB, essentially turning enemy object 12465 sta Enemy_ID,x ;into green or red koopa troopa to demote them 12466 SetStun: lda Enemy_State,x ;load enemy state 12467 and #%11110000 ;save high nybble 12468 ora #%00000010 12469 sta Enemy_State,x ;set d1 of enemy state 12470 dec Enemy_Y_Position,x 12471 dec Enemy_Y_Position,x ;subtract two pixels from enemy's vertical position 12472 lda Enemy_ID,x 12473 cmp #Bloober ;check for bloober object 12474 beq SetWYSpd 12475 lda #$fd ;set default vertical speed 12476 ldy AreaType 12477 bne SetNotW ;if area type not water, set as speed, otherwise 12478 SetWYSpd: lda #$ff ;change the vertical speed 12479 SetNotW: sta Enemy_Y_Speed,x ;set vertical speed now 12480 ldy #$01 12481 jsr PlayerEnemyDiff ;get horizontal difference between player and enemy object 12482 bpl ChkBBill ;branch if enemy is to the right of player 12483 iny ;increment Y if not 12484 ChkBBill: lda Enemy_ID,x 12485 cmp #BulletBill_CannonVar ;check for bullet bill (cannon variant) 12486 beq NoCDirF 12487 cmp #BulletBill_FrenzyVar ;check for bullet bill (frenzy variant) 12488 beq NoCDirF ;branch if either found, direction does not change 12489 sty Enemy_MovingDir,x ;store as moving direction 12490 NoCDirF: dey ;decrement and use as offset 12491 lda EnemyBGCXSpdData,y ;get proper horizontal speed 12492 sta Enemy_X_Speed,x ;and store, then leave 12493 ExEBGChk: rts 12494 12495 ;-------------------------------- 12496 ;$04 - low nybble of vertical coordinate from block buffer routine 12497 12498 LandEnemyProperly: 12499 lda $04 ;check lower nybble of vertical coordinate saved earlier 12500 sec 12501 sbc #$08 ;subtract eight pixels 12502 cmp #$05 ;used to determine whether enemy landed from falling 12503 bcs ChkForRedKoopa ;branch if lower nybble in range of $0d-$0f before subtract 12504 lda Enemy_State,x 12505 and #%01000000 ;branch if d6 in enemy state is set 12506 bne LandEnemyInitState 12507 lda Enemy_State,x 12508 asl ;branch if d7 in enemy state is not set 12509 bcc ChkLandedEnemyState 12510 SChkA: jmp DoEnemySideCheck ;if lower nybble < $0d, d7 set but d6 not set, jump here 12511 12512 ChkLandedEnemyState: 12513 lda Enemy_State,x ;if enemy in normal state, branch back to jump here 12514 beq SChkA 12515 cmp #$05 ;if in state used by spiny's egg 12516 beq ProcEnemyDirection ;then branch elsewhere 12517 cmp #$03 ;if already in state used by koopas and buzzy beetles 12518 bcs ExSteChk ;or in higher numbered state, branch to leave 12519 lda Enemy_State,x ;load enemy state again (why?) 12520 cmp #$02 ;if not in $02 state (used by koopas and buzzy beetles) 12521 bne ProcEnemyDirection ;then branch elsewhere 12522 lda #$10 ;load default timer here 12523 ldy Enemy_ID,x ;check enemy identifier for spiny 12524 cpy #Spiny 12525 bne SetForStn ;branch if not found 12526 lda #$00 ;set timer for $00 if spiny 12527 SetForStn: sta EnemyIntervalTimer,x ;set timer here 12528 lda #$03 ;set state here, apparently used to render 12529 sta Enemy_State,x ;upside-down koopas and buzzy beetles 12530 jsr EnemyLanding ;then land it properly 12531 ExSteChk: rts ;then leave 12532 12533 ProcEnemyDirection: 12534 lda Enemy_ID,x ;check enemy identifier for goomba 12535 cmp #Goomba ;branch if found 12536 beq LandEnemyInitState 12537 cmp #Spiny ;check for spiny 12538 bne InvtD ;branch if not found 12539 lda #$01 12540 sta Enemy_MovingDir,x ;send enemy moving to the right by default 12541 lda #$08 12542 sta Enemy_X_Speed,x ;set horizontal speed accordingly 12543 lda FrameCounter 12544 and #%00000111 ;if timed appropriately, spiny will skip over 12545 beq LandEnemyInitState ;trying to face the player 12546 InvtD: ldy #$01 ;load 1 for enemy to face the left (inverted here) 12547 jsr PlayerEnemyDiff ;get horizontal difference between player and enemy 12548 bpl CNwCDir ;if enemy to the right of player, branch 12549 iny ;if to the left, increment by one for enemy to face right (inverted) 12550 CNwCDir: tya 12551 cmp Enemy_MovingDir,x ;compare direction in A with current direction in memory 12552 bne LandEnemyInitState 12553 jsr ChkForBump_HammerBroJ ;if equal, not facing in correct dir, do sub to turn around 12554 12555 LandEnemyInitState: 12556 jsr EnemyLanding ;land enemy properly 12557 lda Enemy_State,x 12558 and #%10000000 ;if d7 of enemy state is set, branch 12559 bne NMovShellFallBit 12560 lda #$00 ;otherwise initialize enemy state and leave 12561 sta Enemy_State,x ;note this will also turn spiny's egg into spiny 12562 rts 12563 12564 NMovShellFallBit: 12565 lda Enemy_State,x ;nullify d6 of enemy state, save other bits 12566 and #%10111111 ;and store, then leave 12567 sta Enemy_State,x 12568 rts 12569 12570 ;-------------------------------- 12571 12572 ChkForRedKoopa: 12573 lda Enemy_ID,x ;check for red koopa troopa $03 12574 cmp #RedKoopa 12575 bne Chk2MSBSt ;branch if not found 12576 lda Enemy_State,x 12577 beq ChkForBump_HammerBroJ ;if enemy found and in normal state, branch 12578 Chk2MSBSt: lda Enemy_State,x ;save enemy state into Y 12579 tay 12580 asl ;check for d7 set 12581 bcc GetSteFromD ;branch if not set 12582 lda Enemy_State,x 12583 ora #%01000000 ;set d6 12584 jmp SetD6Ste ;jump ahead of this part 12585 GetSteFromD: lda EnemyBGCStateData,y ;load new enemy state with old as offset 12586 SetD6Ste: sta Enemy_State,x ;set as new state 12587 12588 ;-------------------------------- 12589 ;$00 - used to store bitmask (not used but initialized here) 12590 ;$eb - used in DoEnemySideCheck as counter and to compare moving directions 12591 12592 DoEnemySideCheck: 12593 lda Enemy_Y_Position,x ;if enemy within status bar, branch to leave 12594 cmp #$20 ;because there's nothing there that impedes movement 12595 bcc ExESdeC 12596 ldy #$16 ;start by finding block to the left of enemy ($00,$14) 12597 lda #$02 ;set value here in what is also used as 12598 sta $eb ;OAM data offset 12599 SdeCLoop: lda $eb ;check value 12600 cmp Enemy_MovingDir,x ;compare value against moving direction 12601 bne NextSdeC ;branch if different and do not seek block there 12602 lda #$01 ;set flag in A for save horizontal coordinate 12603 jsr BlockBufferChk_Enemy ;find block to left or right of enemy object 12604 beq NextSdeC ;if nothing found, branch 12605 jsr ChkForNonSolids ;check for non-solid blocks 12606 bne ChkForBump_HammerBroJ ;branch if not found 12607 NextSdeC: dec $eb ;move to the next direction 12608 iny 12609 cpy #$18 ;increment Y, loop only if Y < $18, thus we check 12610 bcc SdeCLoop ;enemy ($00, $14) and ($10, $14) pixel coordinates 12611 ExESdeC: rts 12612 12613 ChkForBump_HammerBroJ: 12614 cpx #$05 ;check if we're on the special use slot 12615 beq NoBump ;and if so, branch ahead and do not play sound 12616 lda Enemy_State,x ;if enemy state d7 not set, branch 12617 asl ;ahead and do not play sound 12618 bcc NoBump 12619 lda #Sfx_Bump ;otherwise, play bump sound 12620 sta Square1SoundQueue ;sound will never be played if branching from ChkForRedKoopa 12621 NoBump: lda Enemy_ID,x ;check for hammer bro 12622 cmp #$05 12623 bne InvEnemyDir ;branch if not found 12624 lda #$00 12625 sta $00 ;initialize value here for bitmask 12626 ldy #$fa ;load default vertical speed for jumping 12627 jmp SetHJ ;jump to code that makes hammer bro jump 12628 12629 InvEnemyDir: 12630 jmp RXSpd ;jump to turn the enemy around 12631 12632 ;-------------------------------- 12633 ;$00 - used to hold horizontal difference between player and enemy 12634 12635 PlayerEnemyDiff: 12636 lda Enemy_X_Position,x ;get distance between enemy object's 12637 sec ;horizontal coordinate and the player's 12638 sbc Player_X_Position ;horizontal coordinate 12639 sta $00 ;and store here 12640 lda Enemy_PageLoc,x 12641 sbc Player_PageLoc ;subtract borrow, then leave 12642 rts 12643 12644 ;-------------------------------- 12645 12646 EnemyLanding: 12647 jsr InitVStf ;do something here to vertical speed and something else 12648 lda Enemy_Y_Position,x 12649 and #%11110000 ;save high nybble of vertical coordinate, and 12650 ora #%00001000 ;set d3, then store, probably used to set enemy object 12651 sta Enemy_Y_Position,x ;neatly on whatever it's landing on 12652 rts 12653 12654 SubtEnemyYPos: 12655 lda Enemy_Y_Position,x ;add 62 pixels to enemy object's 12656 clc ;vertical coordinate 12657 adc #$3e 12658 cmp #$44 ;compare against a certain range 12659 rts ;and leave with flags set for conditional branch 12660 12661 EnemyJump: 12662 jsr SubtEnemyYPos ;do a sub here 12663 bcc DoSide ;if enemy vertical coord + 62 < 68, branch to leave 12664 lda Enemy_Y_Speed,x 12665 clc ;add two to vertical speed 12666 adc #$02 12667 cmp #$03 ;if green paratroopa not falling, branch ahead 12668 bcc DoSide 12669 jsr ChkUnderEnemy ;otherwise, check to see if green paratroopa is 12670 beq DoSide ;standing on anything, then branch to same place if not 12671 jsr ChkForNonSolids ;check for non-solid blocks 12672 beq DoSide ;branch if found 12673 jsr EnemyLanding ;change vertical coordinate and speed 12674 lda #$fd 12675 sta Enemy_Y_Speed,x ;make the paratroopa jump again 12676 DoSide: jmp DoEnemySideCheck ;check for horizontal blockage, then leave 12677 12678 ;-------------------------------- 12679 12680 HammerBroBGColl: 12681 jsr ChkUnderEnemy ;check to see if hammer bro is standing on anything 12682 beq NoUnderHammerBro 12683 cmp #$23 ;check for blank metatile $23 and branch if not found 12684 bne UnderHammerBro 12685 12686 KillEnemyAboveBlock: 12687 jsr ShellOrBlockDefeat ;do this sub to kill enemy 12688 lda #$fc ;alter vertical speed of enemy and leave 12689 sta Enemy_Y_Speed,x 12690 rts 12691 12692 UnderHammerBro: 12693 lda EnemyFrameTimer,x ;check timer used by hammer bro 12694 bne NoUnderHammerBro ;branch if not expired 12695 lda Enemy_State,x 12696 and #%10001000 ;save d7 and d3 from enemy state, nullify other bits 12697 sta Enemy_State,x ;and store 12698 jsr EnemyLanding ;modify vertical coordinate, speed and something else 12699 jmp DoEnemySideCheck ;then check for horizontal blockage and leave 12700 12701 NoUnderHammerBro: 12702 lda Enemy_State,x ;if hammer bro is not standing on anything, set d0 12703 ora #$01 ;in the enemy state to indicate jumping or falling, then leave 12704 sta Enemy_State,x 12705 rts 12706 12707 ChkUnderEnemy: 12708 lda #$00 ;set flag in A for save vertical coordinate 12709 ldy #$15 ;set Y to check the bottom middle (8,18) of enemy object 12710 jmp BlockBufferChk_Enemy ;hop to it! 12711 12712 ChkForNonSolids: 12713 cmp #$26 ;blank metatile used for vines? 12714 beq NSFnd 12715 cmp #$c2 ;regular coin? 12716 beq NSFnd 12717 cmp #$c3 ;underwater coin? 12718 beq NSFnd 12719 cmp #$5f ;hidden coin block? 12720 beq NSFnd 12721 cmp #$60 ;hidden 1-up block? 12722 NSFnd: rts 12723 12724 ;------------------------------------------------------------------------------------- 12725 12726 FireballBGCollision: 12727 lda Fireball_Y_Position,x ;check fireball's vertical coordinate 12728 cmp #$18 12729 bcc ClearBounceFlag ;if within the status bar area of the screen, branch ahead 12730 jsr BlockBufferChk_FBall ;do fireball to background collision detection on bottom of it 12731 beq ClearBounceFlag ;if nothing underneath fireball, branch 12732 jsr ChkForNonSolids ;check for non-solid metatiles 12733 beq ClearBounceFlag ;branch if any found 12734 lda Fireball_Y_Speed,x ;if fireball's vertical speed set to move upwards, 12735 bmi InitFireballExplode ;branch to set exploding bit in fireball's state 12736 lda FireballBouncingFlag,x ;if bouncing flag already set, 12737 bne InitFireballExplode ;branch to set exploding bit in fireball's state 12738 lda #$fd 12739 sta Fireball_Y_Speed,x ;otherwise set vertical speed to move upwards (give it bounce) 12740 lda #$01 12741 sta FireballBouncingFlag,x ;set bouncing flag 12742 lda Fireball_Y_Position,x 12743 and #$f8 ;modify vertical coordinate to land it properly 12744 sta Fireball_Y_Position,x ;store as new vertical coordinate 12745 rts ;leave 12746 12747 ClearBounceFlag: 12748 lda #$00 12749 sta FireballBouncingFlag,x ;clear bouncing flag by default 12750 rts ;leave 12751 12752 InitFireballExplode: 12753 lda #$80 12754 sta Fireball_State,x ;set exploding flag in fireball's state 12755 lda #Sfx_Bump 12756 sta Square1SoundQueue ;load bump sound 12757 rts ;leave 12758 12759 ;------------------------------------------------------------------------------------- 12760 ;$00 - used to hold one of bitmasks, or offset 12761 ;$01 - used for relative X coordinate, also used to store middle screen page location 12762 ;$02 - used for relative Y coordinate, also used to store middle screen coordinate 12763 12764 ;this data added to relative coordinates of sprite objects 12765 ;stored in order: left edge, top edge, right edge, bottom edge 12766 BoundBoxCtrlData: 12767 .db $02, $08, $0e, $20 12768 .db $03, $14, $0d, $20 12769 .db $02, $14, $0e, $20 12770 .db $02, $09, $0e, $15 12771 .db $00, $00, $18, $06 12772 .db $00, $00, $20, $0d 12773 .db $00, $00, $30, $0d 12774 .db $00, $00, $08, $08 12775 .db $06, $04, $0a, $08 12776 .db $03, $0e, $0d, $14 12777 .db $00, $02, $10, $15 12778 .db $04, $04, $0c, $1c 12779 12780 GetFireballBoundBox: 12781 txa ;add seven bytes to offset 12782 clc ;to use in routines as offset for fireball 12783 adc #$07 12784 tax 12785 ldy #$02 ;set offset for relative coordinates 12786 bne FBallB ;unconditional branch 12787 12788 GetMiscBoundBox: 12789 txa ;add nine bytes to offset 12790 clc ;to use in routines as offset for misc object 12791 adc #$09 12792 tax 12793 ldy #$06 ;set offset for relative coordinates 12794 FBallB: jsr BoundingBoxCore ;get bounding box coordinates 12795 jmp CheckRightScreenBBox ;jump to handle any offscreen coordinates 12796 12797 GetEnemyBoundBox: 12798 ldy #$48 ;store bitmask here for now 12799 sty $00 12800 ldy #$44 ;store another bitmask here for now and jump 12801 jmp GetMaskedOffScrBits 12802 12803 SmallPlatformBoundBox: 12804 ldy #$08 ;store bitmask here for now 12805 sty $00 12806 ldy #$04 ;store another bitmask here for now 12807 12808 GetMaskedOffScrBits: 12809 lda Enemy_X_Position,x ;get enemy object position relative 12810 sec ;to the left side of the screen 12811 sbc ScreenLeft_X_Pos 12812 sta $01 ;store here 12813 lda Enemy_PageLoc,x ;subtract borrow from current page location 12814 sbc ScreenLeft_PageLoc ;of left side 12815 bmi CMBits ;if enemy object is beyond left edge, branch 12816 ora $01 12817 beq CMBits ;if precisely at the left edge, branch 12818 ldy $00 ;if to the right of left edge, use value in $00 for A 12819 CMBits: tya ;otherwise use contents of Y 12820 and Enemy_OffscreenBits ;preserve bitwise whatever's in here 12821 sta EnemyOffscrBitsMasked,x ;save masked offscreen bits here 12822 bne MoveBoundBoxOffscreen ;if anything set here, branch 12823 jmp SetupEOffsetFBBox ;otherwise, do something else 12824 12825 LargePlatformBoundBox: 12826 inx ;increment X to get the proper offset 12827 jsr GetXOffscreenBits ;then jump directly to the sub for horizontal offscreen bits 12828 dex ;decrement to return to original offset 12829 cmp #$fe ;if completely offscreen, branch to put entire bounding 12830 bcs MoveBoundBoxOffscreen ;box offscreen, otherwise start getting coordinates 12831 12832 SetupEOffsetFBBox: 12833 txa ;add 1 to offset to properly address 12834 clc ;the enemy object memory locations 12835 adc #$01 12836 tax 12837 ldy #$01 ;load 1 as offset here, same reason 12838 jsr BoundingBoxCore ;do a sub to get the coordinates of the bounding box 12839 jmp CheckRightScreenBBox ;jump to handle offscreen coordinates of bounding box 12840 12841 MoveBoundBoxOffscreen: 12842 txa ;multiply offset by 4 12843 asl 12844 asl 12845 tay ;use as offset here 12846 lda #$ff 12847 sta EnemyBoundingBoxCoord,y ;load value into four locations here and leave 12848 sta EnemyBoundingBoxCoord+1,y 12849 sta EnemyBoundingBoxCoord+2,y 12850 sta EnemyBoundingBoxCoord+3,y 12851 rts 12852 12853 BoundingBoxCore: 12854 stx $00 ;save offset here 12855 lda SprObject_Rel_YPos,y ;store object coordinates relative to screen 12856 sta $02 ;vertically and horizontally, respectively 12857 lda SprObject_Rel_XPos,y 12858 sta $01 12859 txa ;multiply offset by four and save to stack 12860 asl 12861 asl 12862 pha 12863 tay ;use as offset for Y, X is left alone 12864 lda SprObj_BoundBoxCtrl,x ;load value here to be used as offset for X 12865 asl ;multiply that by four and use as X 12866 asl 12867 tax 12868 lda $01 ;add the first number in the bounding box data to the 12869 clc ;relative horizontal coordinate using enemy object offset 12870 adc BoundBoxCtrlData,x ;and store somewhere using same offset * 4 12871 sta BoundingBox_UL_Corner,y ;store here 12872 lda $01 12873 clc 12874 adc BoundBoxCtrlData+2,x ;add the third number in the bounding box data to the 12875 sta BoundingBox_LR_Corner,y ;relative horizontal coordinate and store 12876 inx ;increment both offsets 12877 iny 12878 lda $02 ;add the second number to the relative vertical coordinate 12879 clc ;using incremented offset and store using the other 12880 adc BoundBoxCtrlData,x ;incremented offset 12881 sta BoundingBox_UL_Corner,y 12882 lda $02 12883 clc 12884 adc BoundBoxCtrlData+2,x ;add the fourth number to the relative vertical coordinate 12885 sta BoundingBox_LR_Corner,y ;and store 12886 pla ;get original offset loaded into $00 * y from stack 12887 tay ;use as Y 12888 ldx $00 ;get original offset and use as X again 12889 rts 12890 12891 CheckRightScreenBBox: 12892 lda ScreenLeft_X_Pos ;add 128 pixels to left side of screen 12893 clc ;and store as horizontal coordinate of middle 12894 adc #$80 12895 sta $02 12896 lda ScreenLeft_PageLoc ;add carry to page location of left side of screen 12897 adc #$00 ;and store as page location of middle 12898 sta $01 12899 lda SprObject_X_Position,x ;get horizontal coordinate 12900 cmp $02 ;compare against middle horizontal coordinate 12901 lda SprObject_PageLoc,x ;get page location 12902 sbc $01 ;subtract from middle page location 12903 bcc CheckLeftScreenBBox ;if object is on the left side of the screen, branch 12904 lda BoundingBox_DR_XPos,y ;check right-side edge of bounding box for offscreen 12905 bmi NoOfs ;coordinates, branch if still on the screen 12906 lda #$ff ;load offscreen value here to use on one or both horizontal sides 12907 ldx BoundingBox_UL_XPos,y ;check left-side edge of bounding box for offscreen 12908 bmi SORte ;coordinates, and branch if still on the screen 12909 sta BoundingBox_UL_XPos,y ;store offscreen value for left side 12910 SORte: sta BoundingBox_DR_XPos,y ;store offscreen value for right side 12911 NoOfs: ldx ObjectOffset ;get object offset and leave 12912 rts 12913 12914 CheckLeftScreenBBox: 12915 lda BoundingBox_UL_XPos,y ;check left-side edge of bounding box for offscreen 12916 bpl NoOfs2 ;coordinates, and branch if still on the screen 12917 cmp #$a0 ;check to see if left-side edge is in the middle of the 12918 bcc NoOfs2 ;screen or really offscreen, and branch if still on 12919 lda #$00 12920 ldx BoundingBox_DR_XPos,y ;check right-side edge of bounding box for offscreen 12921 bpl SOLft ;coordinates, branch if still onscreen 12922 sta BoundingBox_DR_XPos,y ;store offscreen value for right side 12923 SOLft: sta BoundingBox_UL_XPos,y ;store offscreen value for left side 12924 NoOfs2: ldx ObjectOffset ;get object offset and leave 12925 rts 12926 12927 ;------------------------------------------------------------------------------------- 12928 ;$06 - second object's offset 12929 ;$07 - counter 12930 12931 PlayerCollisionCore: 12932 ldx #$00 ;initialize X to use player's bounding box for comparison 12933 12934 SprObjectCollisionCore: 12935 sty $06 ;save contents of Y here 12936 lda #$01 12937 sta $07 ;save value 1 here as counter, compare horizontal coordinates first 12938 12939 CollisionCoreLoop: 12940 lda BoundingBox_UL_Corner,y ;compare left/top coordinates 12941 cmp BoundingBox_UL_Corner,x ;of first and second objects' bounding boxes 12942 bcs FirstBoxGreater ;if first left/top => second, branch 12943 cmp BoundingBox_LR_Corner,x ;otherwise compare to right/bottom of second 12944 bcc SecondBoxVerticalChk ;if first left/top < second right/bottom, branch elsewhere 12945 beq CollisionFound ;if somehow equal, collision, thus branch 12946 lda BoundingBox_LR_Corner,y ;if somehow greater, check to see if bottom of 12947 cmp BoundingBox_UL_Corner,y ;first object's bounding box is greater than its top 12948 bcc CollisionFound ;if somehow less, vertical wrap collision, thus branch 12949 cmp BoundingBox_UL_Corner,x ;otherwise compare bottom of first bounding box to the top 12950 bcs CollisionFound ;of second box, and if equal or greater, collision, thus branch 12951 ldy $06 ;otherwise return with carry clear and Y = $0006 12952 rts ;note horizontal wrapping never occurs 12953 12954 SecondBoxVerticalChk: 12955 lda BoundingBox_LR_Corner,x ;check to see if the vertical bottom of the box 12956 cmp BoundingBox_UL_Corner,x ;is greater than the vertical top 12957 bcc CollisionFound ;if somehow less, vertical wrap collision, thus branch 12958 lda BoundingBox_LR_Corner,y ;otherwise compare horizontal right or vertical bottom 12959 cmp BoundingBox_UL_Corner,x ;of first box with horizontal left or vertical top of second box 12960 bcs CollisionFound ;if equal or greater, collision, thus branch 12961 ldy $06 ;otherwise return with carry clear and Y = $0006 12962 rts 12963 12964 FirstBoxGreater: 12965 cmp BoundingBox_UL_Corner,x ;compare first and second box horizontal left/vertical top again 12966 beq CollisionFound ;if first coordinate = second, collision, thus branch 12967 cmp BoundingBox_LR_Corner,x ;if not, compare with second object right or bottom edge 12968 bcc CollisionFound ;if left/top of first less than or equal to right/bottom of second 12969 beq CollisionFound ;then collision, thus branch 12970 cmp BoundingBox_LR_Corner,y ;otherwise check to see if top of first box is greater than bottom 12971 bcc NoCollisionFound ;if less than or equal, no collision, branch to end 12972 beq NoCollisionFound 12973 lda BoundingBox_LR_Corner,y ;otherwise compare bottom of first to top of second 12974 cmp BoundingBox_UL_Corner,x ;if bottom of first is greater than top of second, vertical wrap 12975 bcs CollisionFound ;collision, and branch, otherwise, proceed onwards here 12976 12977 NoCollisionFound: 12978 clc ;clear carry, then load value set earlier, then leave 12979 ldy $06 ;like previous ones, if horizontal coordinates do not collide, we do 12980 rts ;not bother checking vertical ones, because what's the point? 12981 12982 CollisionFound: 12983 inx ;increment offsets on both objects to check 12984 iny ;the vertical coordinates 12985 dec $07 ;decrement counter to reflect this 12986 bpl CollisionCoreLoop ;if counter not expired, branch to loop 12987 sec ;otherwise we already did both sets, therefore collision, so set carry 12988 ldy $06 ;load original value set here earlier, then leave 12989 rts 12990 12991 ;------------------------------------------------------------------------------------- 12992 ;$02 - modified y coordinate 12993 ;$03 - stores metatile involved in block buffer collisions 12994 ;$04 - comes in with offset to block buffer adder data, goes out with low nybble x/y coordinate 12995 ;$05 - modified x coordinate 12996 ;$06-$07 - block buffer address 12997 12998 BlockBufferChk_Enemy: 12999 pha ;save contents of A to stack 13000 txa 13001 clc ;add 1 to X to run sub with enemy offset in mind 13002 adc #$01 13003 tax 13004 pla ;pull A from stack and jump elsewhere 13005 jmp BBChk_E 13006 13007 ResidualMiscObjectCode: 13008 txa 13009 clc ;supposedly used once to set offset for 13010 adc #$0d ;miscellaneous objects 13011 tax 13012 ldy #$1b ;supposedly used once to set offset for block buffer data 13013 jmp ResJmpM ;probably used in early stages to do misc to bg collision detection 13014 13015 BlockBufferChk_FBall: 13016 ldy #$1a ;set offset for block buffer adder data 13017 txa 13018 clc 13019 adc #$07 ;add seven bytes to use 13020 tax 13021 ResJmpM: lda #$00 ;set A to return vertical coordinate 13022 BBChk_E: jsr BlockBufferCollision ;do collision detection subroutine for sprite object 13023 ldx ObjectOffset ;get object offset 13024 cmp #$00 ;check to see if object bumped into anything 13025 rts 13026 13027 BlockBufferAdderData: 13028 .db $00, $07, $0e 13029 13030 BlockBuffer_X_Adder: 13031 .db $08, $03, $0c, $02, $02, $0d, $0d, $08 13032 .db $03, $0c, $02, $02, $0d, $0d, $08, $03 13033 .db $0c, $02, $02, $0d, $0d, $08, $00, $10 13034 .db $04, $14, $04, $04 13035 13036 BlockBuffer_Y_Adder: 13037 .db $04, $20, $20, $08, $18, $08, $18, $02 13038 .db $20, $20, $08, $18, $08, $18, $12, $20 13039 .db $20, $18, $18, $18, $18, $18, $14, $14 13040 .db $06, $06, $08, $10 13041 13042 BlockBufferColli_Feet: 13043 iny ;if branched here, increment to next set of adders 13044 13045 BlockBufferColli_Head: 13046 lda #$00 ;set flag to return vertical coordinate 13047 .db $2c ;BIT instruction opcode 13048 13049 BlockBufferColli_Side: 13050 lda #$01 ;set flag to return horizontal coordinate 13051 ldx #$00 ;set offset for player object 13052 13053 BlockBufferCollision: 13054 pha ;save contents of A to stack 13055 sty $04 ;save contents of Y here 13056 lda BlockBuffer_X_Adder,y ;add horizontal coordinate 13057 clc ;of object to value obtained using Y as offset 13058 adc SprObject_X_Position,x 13059 sta $05 ;store here 13060 lda SprObject_PageLoc,x 13061 adc #$00 ;add carry to page location 13062 and #$01 ;get LSB, mask out all other bits 13063 lsr ;move to carry 13064 ora $05 ;get stored value 13065 ror ;rotate carry to MSB of A 13066 lsr ;and effectively move high nybble to 13067 lsr ;lower, LSB which became MSB will be 13068 lsr ;d4 at this point 13069 jsr GetBlockBufferAddr ;get address of block buffer into $06, $07 13070 ldy $04 ;get old contents of Y 13071 lda SprObject_Y_Position,x ;get vertical coordinate of object 13072 clc 13073 adc BlockBuffer_Y_Adder,y ;add it to value obtained using Y as offset 13074 and #%11110000 ;mask out low nybble 13075 sec 13076 sbc #$20 ;subtract 32 pixels for the status bar 13077 sta $02 ;store result here 13078 tay ;use as offset for block buffer 13079 lda ($06),y ;check current content of block buffer 13080 sta $03 ;and store here 13081 ldy $04 ;get old contents of Y again 13082 pla ;pull A from stack 13083 bne RetXC ;if A = 1, branch 13084 lda SprObject_Y_Position,x ;if A = 0, load vertical coordinate 13085 jmp RetYC ;and jump 13086 RetXC: lda SprObject_X_Position,x ;otherwise load horizontal coordinate 13087 RetYC: and #%00001111 ;and mask out high nybble 13088 sta $04 ;store masked out result here 13089 lda $03 ;get saved content of block buffer 13090 rts ;and leave 13091 13092 ;------------------------------------------------------------------------------------- 13093 13094 ;unused byte 13095 .db $ff 13096 13097 ;------------------------------------------------------------------------------------- 13098 ;$00 - offset to vine Y coordinate adder 13099 ;$02 - offset to sprite data 13100 13101 VineYPosAdder: 13102 .db $00, $30 13103 13104 DrawVine: 13105 sty $00 ;save offset here 13106 lda Enemy_Rel_YPos ;get relative vertical coordinate 13107 clc 13108 adc VineYPosAdder,y ;add value using offset in Y to get value 13109 ldx VineObjOffset,y ;get offset to vine 13110 ldy Enemy_SprDataOffset,x ;get sprite data offset 13111 sty $02 ;store sprite data offset here 13112 jsr SixSpriteStacker ;stack six sprites on top of each other vertically 13113 lda Enemy_Rel_XPos ;get relative horizontal coordinate 13114 sta Sprite_X_Position,y ;store in first, third and fifth sprites 13115 sta Sprite_X_Position+8,y 13116 sta Sprite_X_Position+16,y 13117 clc 13118 adc #$06 ;add six pixels to second, fourth and sixth sprites 13119 sta Sprite_X_Position+4,y ;to give characteristic staggered vine shape to 13120 sta Sprite_X_Position+12,y ;our vertical stack of sprites 13121 sta Sprite_X_Position+20,y 13122 lda #%00100001 ;set bg priority and palette attribute bits 13123 sta Sprite_Attributes,y ;set in first, third and fifth sprites 13124 sta Sprite_Attributes+8,y 13125 sta Sprite_Attributes+16,y 13126 ora #%01000000 ;additionally, set horizontal flip bit 13127 sta Sprite_Attributes+4,y ;for second, fourth and sixth sprites 13128 sta Sprite_Attributes+12,y 13129 sta Sprite_Attributes+20,y 13130 ldx #$05 ;set tiles for six sprites 13131 VineTL: lda #$e1 ;set tile number for sprite 13132 sta Sprite_Tilenumber,y 13133 iny ;move offset to next sprite data 13134 iny 13135 iny 13136 iny 13137 dex ;move onto next sprite 13138 bpl VineTL ;loop until all sprites are done 13139 ldy $02 ;get original offset 13140 lda $00 ;get offset to vine adding data 13141 bne SkpVTop ;if offset not zero, skip this part 13142 lda #$e0 13143 sta Sprite_Tilenumber,y ;set other tile number for top of vine 13144 SkpVTop: ldx #$00 ;start with the first sprite again 13145 ChkFTop: lda VineStart_Y_Position ;get original starting vertical coordinate 13146 sec 13147 sbc Sprite_Y_Position,y ;subtract top-most sprite's Y coordinate 13148 cmp #$64 ;if two coordinates are less than 100/$64 pixels 13149 bcc NextVSp ;apart, skip this to leave sprite alone 13150 lda #$f8 13151 sta Sprite_Y_Position,y ;otherwise move sprite offscreen 13152 NextVSp: iny ;move offset to next OAM data 13153 iny 13154 iny 13155 iny 13156 inx ;move onto next sprite 13157 cpx #$06 ;do this until all sprites are checked 13158 bne ChkFTop 13159 ldy $00 ;return offset set earlier 13160 rts 13161 13162 SixSpriteStacker: 13163 ldx #$06 ;do six sprites 13164 StkLp: sta Sprite_Data,y ;store X or Y coordinate into OAM data 13165 clc 13166 adc #$08 ;add eight pixels 13167 iny 13168 iny ;move offset four bytes forward 13169 iny 13170 iny 13171 dex ;do another sprite 13172 bne StkLp ;do this until all sprites are done 13173 ldy $02 ;get saved OAM data offset and leave 13174 rts 13175 13176 ;------------------------------------------------------------------------------------- 13177 13178 FirstSprXPos: 13179 .db $04, $00, $04, $00 13180 13181 FirstSprYPos: 13182 .db $00, $04, $00, $04 13183 13184 SecondSprXPos: 13185 .db $00, $08, $00, $08 13186 13187 SecondSprYPos: 13188 .db $08, $00, $08, $00 13189 13190 FirstSprTilenum: 13191 .db $80, $82, $81, $83 13192 13193 SecondSprTilenum: 13194 .db $81, $83, $80, $82 13195 13196 HammerSprAttrib: 13197 .db $03, $03, $c3, $c3 13198 13199 DrawHammer: 13200 ldy Misc_SprDataOffset,x ;get misc object OAM data offset 13201 lda TimerControl 13202 bne ForceHPose ;if master timer control set, skip this part 13203 lda Misc_State,x ;otherwise get hammer's state 13204 and #%01111111 ;mask out d7 13205 cmp #$01 ;check to see if set to 1 yet 13206 beq GetHPose ;if so, branch 13207 ForceHPose: ldx #$00 ;reset offset here 13208 beq RenderH ;do unconditional branch to rendering part 13209 GetHPose: lda FrameCounter ;get frame counter 13210 lsr ;move d3-d2 to d1-d0 13211 lsr 13212 and #%00000011 ;mask out all but d1-d0 (changes every four frames) 13213 tax ;use as timing offset 13214 RenderH: lda Misc_Rel_YPos ;get relative vertical coordinate 13215 clc 13216 adc FirstSprYPos,x ;add first sprite vertical adder based on offset 13217 sta Sprite_Y_Position,y ;store as sprite Y coordinate for first sprite 13218 clc 13219 adc SecondSprYPos,x ;add second sprite vertical adder based on offset 13220 sta Sprite_Y_Position+4,y ;store as sprite Y coordinate for second sprite 13221 lda Misc_Rel_XPos ;get relative horizontal coordinate 13222 clc 13223 adc FirstSprXPos,x ;add first sprite horizontal adder based on offset 13224 sta Sprite_X_Position,y ;store as sprite X coordinate for first sprite 13225 clc 13226 adc SecondSprXPos,x ;add second sprite horizontal adder based on offset 13227 sta Sprite_X_Position+4,y ;store as sprite X coordinate for second sprite 13228 lda FirstSprTilenum,x 13229 sta Sprite_Tilenumber,y ;get and store tile number of first sprite 13230 lda SecondSprTilenum,x 13231 sta Sprite_Tilenumber+4,y ;get and store tile number of second sprite 13232 lda HammerSprAttrib,x 13233 sta Sprite_Attributes,y ;get and store attribute bytes for both 13234 sta Sprite_Attributes+4,y ;note in this case they use the same data 13235 ldx ObjectOffset ;get misc object offset 13236 lda Misc_OffscreenBits 13237 and #%11111100 ;check offscreen bits 13238 beq NoHOffscr ;if all bits clear, leave object alone 13239 lda #$00 13240 sta Misc_State,x ;otherwise nullify misc object state 13241 lda #$f8 13242 jsr DumpTwoSpr ;do sub to move hammer sprites offscreen 13243 NoHOffscr: rts ;leave 13244 13245 ;------------------------------------------------------------------------------------- 13246 ;$00-$01 - used to hold tile numbers ($01 addressed in draw floatey number part) 13247 ;$02 - used to hold Y coordinate for floatey number 13248 ;$03 - residual byte used for flip (but value set here affects nothing) 13249 ;$04 - attribute byte for floatey number 13250 ;$05 - used as X coordinate for floatey number 13251 13252 FlagpoleScoreNumTiles: 13253 .db $f9, $50 13254 .db $f7, $50 13255 .db $fa, $fb 13256 .db $f8, $fb 13257 .db $f6, $fb 13258 13259 FlagpoleGfxHandler: 13260 ldy Enemy_SprDataOffset,x ;get sprite data offset for flagpole flag 13261 lda Enemy_Rel_XPos ;get relative horizontal coordinate 13262 sta Sprite_X_Position,y ;store as X coordinate for first sprite 13263 clc 13264 adc #$08 ;add eight pixels and store 13265 sta Sprite_X_Position+4,y ;as X coordinate for second and third sprites 13266 sta Sprite_X_Position+8,y 13267 clc 13268 adc #$0c ;add twelve more pixels and 13269 sta $05 ;store here to be used later by floatey number 13270 lda Enemy_Y_Position,x ;get vertical coordinate 13271 jsr DumpTwoSpr ;and do sub to dump into first and second sprites 13272 adc #$08 ;add eight pixels 13273 sta Sprite_Y_Position+8,y ;and store into third sprite 13274 lda FlagpoleFNum_Y_Pos ;get vertical coordinate for floatey number 13275 sta $02 ;store it here 13276 lda #$01 13277 sta $03 ;set value for flip which will not be used, and 13278 sta $04 ;attribute byte for floatey number 13279 sta Sprite_Attributes,y ;set attribute bytes for all three sprites 13280 sta Sprite_Attributes+4,y 13281 sta Sprite_Attributes+8,y 13282 lda #$7e 13283 sta Sprite_Tilenumber,y ;put triangle shaped tile 13284 sta Sprite_Tilenumber+8,y ;into first and third sprites 13285 lda #$7f 13286 sta Sprite_Tilenumber+4,y ;put skull tile into second sprite 13287 lda FlagpoleCollisionYPos ;get vertical coordinate at time of collision 13288 beq ChkFlagOffscreen ;if zero, branch ahead 13289 tya 13290 clc ;add 12 bytes to sprite data offset 13291 adc #$0c 13292 tay ;put back in Y 13293 lda FlagpoleScore ;get offset used to award points for touching flagpole 13294 asl ;multiply by 2 to get proper offset here 13295 tax 13296 lda FlagpoleScoreNumTiles,x ;get appropriate tile data 13297 sta $00 13298 lda FlagpoleScoreNumTiles+1,x 13299 jsr DrawOneSpriteRow ;use it to render floatey number 13300 13301 ChkFlagOffscreen: 13302 ldx ObjectOffset ;get object offset for flag 13303 ldy Enemy_SprDataOffset,x ;get OAM data offset 13304 lda Enemy_OffscreenBits ;get offscreen bits 13305 and #%00001110 ;mask out all but d3-d1 13306 beq ExitDumpSpr ;if none of these bits set, branch to leave 13307 13308 ;------------------------------------------------------------------------------------- 13309 13310 MoveSixSpritesOffscreen: 13311 lda #$f8 ;set offscreen coordinate if jumping here 13312 13313 DumpSixSpr: 13314 sta Sprite_Data+20,y ;dump A contents 13315 sta Sprite_Data+16,y ;into third row sprites 13316 13317 DumpFourSpr: 13318 sta Sprite_Data+12,y ;into second row sprites 13319 13320 DumpThreeSpr: 13321 sta Sprite_Data+8,y 13322 13323 DumpTwoSpr: 13324 sta Sprite_Data+4,y ;and into first row sprites 13325 sta Sprite_Data,y 13326 13327 ExitDumpSpr: 13328 rts 13329 13330 ;------------------------------------------------------------------------------------- 13331 13332 DrawLargePlatform: 13333 ldy Enemy_SprDataOffset,x ;get OAM data offset 13334 sty $02 ;store here 13335 iny ;add 3 to it for offset 13336 iny ;to X coordinate 13337 iny 13338 lda Enemy_Rel_XPos ;get horizontal relative coordinate 13339 jsr SixSpriteStacker ;store X coordinates using A as base, stack horizontally 13340 ldx ObjectOffset 13341 lda Enemy_Y_Position,x ;get vertical coordinate 13342 jsr DumpFourSpr ;dump into first four sprites as Y coordinate 13343 ldy AreaType 13344 cpy #$03 ;check for castle-type level 13345 beq ShrinkPlatform 13346 ldy SecondaryHardMode ;check for secondary hard mode flag set 13347 beq SetLast2Platform ;branch if not set elsewhere 13348 13349 ShrinkPlatform: 13350 lda #$f8 ;load offscreen coordinate if flag set or castle-type level 13351 13352 SetLast2Platform: 13353 ldy Enemy_SprDataOffset,x ;get OAM data offset 13354 sta Sprite_Y_Position+16,y ;store vertical coordinate or offscreen 13355 sta Sprite_Y_Position+20,y ;coordinate into last two sprites as Y coordinate 13356 lda #$5b ;load default tile for platform (girder) 13357 ldx CloudTypeOverride 13358 beq SetPlatformTilenum ;if cloud level override flag not set, use 13359 lda #$75 ;otherwise load other tile for platform (puff) 13360 13361 SetPlatformTilenum: 13362 ldx ObjectOffset ;get enemy object buffer offset 13363 iny ;increment Y for tile offset 13364 jsr DumpSixSpr ;dump tile number into all six sprites 13365 lda #$02 ;set palette controls 13366 iny ;increment Y for sprite attributes 13367 jsr DumpSixSpr ;dump attributes into all six sprites 13368 inx ;increment X for enemy objects 13369 jsr GetXOffscreenBits ;get offscreen bits again 13370 dex 13371 ldy Enemy_SprDataOffset,x ;get OAM data offset 13372 asl ;rotate d7 into carry, save remaining 13373 pha ;bits to the stack 13374 bcc SChk2 13375 lda #$f8 ;if d7 was set, move first sprite offscreen 13376 sta Sprite_Y_Position,y 13377 SChk2: pla ;get bits from stack 13378 asl ;rotate d6 into carry 13379 pha ;save to stack 13380 bcc SChk3 13381 lda #$f8 ;if d6 was set, move second sprite offscreen 13382 sta Sprite_Y_Position+4,y 13383 SChk3: pla ;get bits from stack 13384 asl ;rotate d5 into carry 13385 pha ;save to stack 13386 bcc SChk4 13387 lda #$f8 ;if d5 was set, move third sprite offscreen 13388 sta Sprite_Y_Position+8,y 13389 SChk4: pla ;get bits from stack 13390 asl ;rotate d4 into carry 13391 pha ;save to stack 13392 bcc SChk5 13393 lda #$f8 ;if d4 was set, move fourth sprite offscreen 13394 sta Sprite_Y_Position+12,y 13395 SChk5: pla ;get bits from stack 13396 asl ;rotate d3 into carry 13397 pha ;save to stack 13398 bcc SChk6 13399 lda #$f8 ;if d3 was set, move fifth sprite offscreen 13400 sta Sprite_Y_Position+16,y 13401 SChk6: pla ;get bits from stack 13402 asl ;rotate d2 into carry 13403 bcc SLChk ;save to stack 13404 lda #$f8 13405 sta Sprite_Y_Position+20,y ;if d2 was set, move sixth sprite offscreen 13406 SLChk: lda Enemy_OffscreenBits ;check d7 of offscreen bits 13407 asl ;and if d7 is not set, skip sub 13408 bcc ExDLPl 13409 jsr MoveSixSpritesOffscreen ;otherwise branch to move all sprites offscreen 13410 ExDLPl: rts 13411 13412 ;------------------------------------------------------------------------------------- 13413 13414 DrawFloateyNumber_Coin: 13415 lda FrameCounter ;get frame counter 13416 lsr ;divide by 2 13417 bcs NotRsNum ;branch if d0 not set to raise number every other frame 13418 dec Misc_Y_Position,x ;otherwise, decrement vertical coordinate 13419 NotRsNum: lda Misc_Y_Position,x ;get vertical coordinate 13420 jsr DumpTwoSpr ;dump into both sprites 13421 lda Misc_Rel_XPos ;get relative horizontal coordinate 13422 sta Sprite_X_Position,y ;store as X coordinate for first sprite 13423 clc 13424 adc #$08 ;add eight pixels 13425 sta Sprite_X_Position+4,y ;store as X coordinate for second sprite 13426 lda #$02 13427 sta Sprite_Attributes,y ;store attribute byte in both sprites 13428 sta Sprite_Attributes+4,y 13429 lda #$f7 13430 sta Sprite_Tilenumber,y ;put tile numbers into both sprites 13431 lda #$fb ;that resemble "200" 13432 sta Sprite_Tilenumber+4,y 13433 jmp ExJCGfx ;then jump to leave (why not an rts here instead?) 13434 13435 JumpingCoinTiles: 13436 .db $60, $61, $62, $63 13437 13438 JCoinGfxHandler: 13439 ldy Misc_SprDataOffset,x ;get coin/floatey number's OAM data offset 13440 lda Misc_State,x ;get state of misc object 13441 cmp #$02 ;if 2 or greater, 13442 bcs DrawFloateyNumber_Coin ;branch to draw floatey number 13443 lda Misc_Y_Position,x ;store vertical coordinate as 13444 sta Sprite_Y_Position,y ;Y coordinate for first sprite 13445 clc 13446 adc #$08 ;add eight pixels 13447 sta Sprite_Y_Position+4,y ;store as Y coordinate for second sprite 13448 lda Misc_Rel_XPos ;get relative horizontal coordinate 13449 sta Sprite_X_Position,y 13450 sta Sprite_X_Position+4,y ;store as X coordinate for first and second sprites 13451 lda FrameCounter ;get frame counter 13452 lsr ;divide by 2 to alter every other frame 13453 and #%00000011 ;mask out d2-d1 13454 tax ;use as graphical offset 13455 lda JumpingCoinTiles,x ;load tile number 13456 iny ;increment OAM data offset to write tile numbers 13457 jsr DumpTwoSpr ;do sub to dump tile number into both sprites 13458 dey ;decrement to get old offset 13459 lda #$02 13460 sta Sprite_Attributes,y ;set attribute byte in first sprite 13461 lda #$82 13462 sta Sprite_Attributes+4,y ;set attribute byte with vertical flip in second sprite 13463 ldx ObjectOffset ;get misc object offset 13464 ExJCGfx: rts ;leave 13465 13466 ;------------------------------------------------------------------------------------- 13467 ;$00-$01 - used to hold tiles for drawing the power-up, $00 also used to hold power-up type 13468 ;$02 - used to hold bottom row Y position 13469 ;$03 - used to hold flip control (not used here) 13470 ;$04 - used to hold sprite attributes 13471 ;$05 - used to hold X position 13472 ;$07 - counter 13473 13474 ;tiles arranged in top left, right, bottom left, right order 13475 PowerUpGfxTable: 13476 .db $76, $77, $78, $79 ;regular mushroom 13477 .db $d6, $d6, $d9, $d9 ;fire flower 13478 .db $8d, $8d, $e4, $e4 ;star 13479 .db $76, $77, $78, $79 ;1-up mushroom 13480 13481 PowerUpAttributes: 13482 .db $02, $01, $02, $01 13483 13484 DrawPowerUp: 13485 ldy Enemy_SprDataOffset+5 ;get power-up's sprite data offset 13486 lda Enemy_Rel_YPos ;get relative vertical coordinate 13487 clc 13488 adc #$08 ;add eight pixels 13489 sta $02 ;store result here 13490 lda Enemy_Rel_XPos ;get relative horizontal coordinate 13491 sta $05 ;store here 13492 ldx PowerUpType ;get power-up type 13493 lda PowerUpAttributes,x ;get attribute data for power-up type 13494 ora Enemy_SprAttrib+5 ;add background priority bit if set 13495 sta $04 ;store attributes here 13496 txa 13497 pha ;save power-up type to the stack 13498 asl 13499 asl ;multiply by four to get proper offset 13500 tax ;use as X 13501 lda #$01 13502 sta $07 ;set counter here to draw two rows of sprite object 13503 sta $03 ;init d1 of flip control 13504 13505 PUpDrawLoop: 13506 lda PowerUpGfxTable,x ;load left tile of power-up object 13507 sta $00 13508 lda PowerUpGfxTable+1,x ;load right tile 13509 jsr DrawOneSpriteRow ;branch to draw one row of our power-up object 13510 dec $07 ;decrement counter 13511 bpl PUpDrawLoop ;branch until two rows are drawn 13512 ldy Enemy_SprDataOffset+5 ;get sprite data offset again 13513 pla ;pull saved power-up type from the stack 13514 beq PUpOfs ;if regular mushroom, branch, do not change colors or flip 13515 cmp #$03 13516 beq PUpOfs ;if 1-up mushroom, branch, do not change colors or flip 13517 sta $00 ;store power-up type here now 13518 lda FrameCounter ;get frame counter 13519 lsr ;divide by 2 to change colors every two frames 13520 and #%00000011 ;mask out all but d1 and d0 (previously d2 and d1) 13521 ora Enemy_SprAttrib+5 ;add background priority bit if any set 13522 sta Sprite_Attributes,y ;set as new palette bits for top left and 13523 sta Sprite_Attributes+4,y ;top right sprites for fire flower and star 13524 ldx $00 13525 dex ;check power-up type for fire flower 13526 beq FlipPUpRightSide ;if found, skip this part 13527 sta Sprite_Attributes+8,y ;otherwise set new palette bits for bottom left 13528 sta Sprite_Attributes+12,y ;and bottom right sprites as well for star only 13529 13530 FlipPUpRightSide: 13531 lda Sprite_Attributes+4,y 13532 ora #%01000000 ;set horizontal flip bit for top right sprite 13533 sta Sprite_Attributes+4,y 13534 lda Sprite_Attributes+12,y 13535 ora #%01000000 ;set horizontal flip bit for bottom right sprite 13536 sta Sprite_Attributes+12,y ;note these are only done for fire flower and star power-ups 13537 PUpOfs: jmp SprObjectOffscrChk ;jump to check to see if power-up is offscreen at all, then leave 13538 13539 ;------------------------------------------------------------------------------------- 13540 ;$00-$01 - used in DrawEnemyObjRow to hold sprite tile numbers 13541 ;$02 - used to store Y position 13542 ;$03 - used to store moving direction, used to flip enemies horizontally 13543 ;$04 - used to store enemy's sprite attributes 13544 ;$05 - used to store X position 13545 ;$eb - used to hold sprite data offset 13546 ;$ec - used to hold either altered enemy state or special value used in gfx handler as condition 13547 ;$ed - used to hold enemy state from buffer 13548 ;$ef - used to hold enemy code used in gfx handler (may or may not resemble Enemy_ID values) 13549 13550 ;tiles arranged in top left, right, middle left, right, bottom left, right order 13551 EnemyGraphicsTable: 13552 .db $fc, $fc, $aa, $ab, $ac, $ad ;buzzy beetle frame 1 13553 .db $fc, $fc, $ae, $af, $b0, $b1 ; frame 2 13554 .db $fc, $a5, $a6, $a7, $a8, $a9 ;koopa troopa frame 1 13555 .db $fc, $a0, $a1, $a2, $a3, $a4 ; frame 2 13556 .db $69, $a5, $6a, $a7, $a8, $a9 ;koopa paratroopa frame 1 13557 .db $6b, $a0, $6c, $a2, $a3, $a4 ; frame 2 13558 .db $fc, $fc, $96, $97, $98, $99 ;spiny frame 1 13559 .db $fc, $fc, $9a, $9b, $9c, $9d ; frame 2 13560 .db $fc, $fc, $8f, $8e, $8e, $8f ;spiny's egg frame 1 13561 .db $fc, $fc, $95, $94, $94, $95 ; frame 2 13562 .db $fc, $fc, $dc, $dc, $df, $df ;bloober frame 1 13563 .db $dc, $dc, $dd, $dd, $de, $de ; frame 2 13564 .db $fc, $fc, $b2, $b3, $b4, $b5 ;cheep-cheep frame 1 13565 .db $fc, $fc, $b6, $b3, $b7, $b5 ; frame 2 13566 .db $fc, $fc, $70, $71, $72, $73 ;goomba 13567 .db $fc, $fc, $6e, $6e, $6f, $6f ;koopa shell frame 1 (upside-down) 13568 .db $fc, $fc, $6d, $6d, $6f, $6f ; frame 2 13569 .db $fc, $fc, $6f, $6f, $6e, $6e ;koopa shell frame 1 (rightsideup) 13570 .db $fc, $fc, $6f, $6f, $6d, $6d ; frame 2 13571 .db $fc, $fc, $f4, $f4, $f5, $f5 ;buzzy beetle shell frame 1 (rightsideup) 13572 .db $fc, $fc, $f4, $f4, $f5, $f5 ; frame 2 13573 .db $fc, $fc, $f5, $f5, $f4, $f4 ;buzzy beetle shell frame 1 (upside-down) 13574 .db $fc, $fc, $f5, $f5, $f4, $f4 ; frame 2 13575 .db $fc, $fc, $fc, $fc, $ef, $ef ;defeated goomba 13576 .db $b9, $b8, $bb, $ba, $bc, $bc ;lakitu frame 1 13577 .db $fc, $fc, $bd, $bd, $bc, $bc ; frame 2 13578 .db $7a, $7b, $da, $db, $d8, $d8 ;princess 13579 .db $cd, $cd, $ce, $ce, $cf, $cf ;mushroom retainer 13580 .db $7d, $7c, $d1, $8c, $d3, $d2 ;hammer bro frame 1 13581 .db $7d, $7c, $89, $88, $8b, $8a ; frame 2 13582 .db $d5, $d4, $e3, $e2, $d3, $d2 ; frame 3 13583 .db $d5, $d4, $e3, $e2, $8b, $8a ; frame 4 13584 .db $e5, $e5, $e6, $e6, $eb, $eb ;piranha plant frame 1 13585 .db $ec, $ec, $ed, $ed, $ee, $ee ; frame 2 13586 .db $fc, $fc, $d0, $d0, $d7, $d7 ;podoboo 13587 .db $bf, $be, $c1, $c0, $c2, $fc ;bowser front frame 1 13588 .db $c4, $c3, $c6, $c5, $c8, $c7 ;bowser rear frame 1 13589 .db $bf, $be, $ca, $c9, $c2, $fc ; front frame 2 13590 .db $c4, $c3, $c6, $c5, $cc, $cb ; rear frame 2 13591 .db $fc, $fc, $e8, $e7, $ea, $e9 ;bullet bill 13592 .db $f2, $f2, $f3, $f3, $f2, $f2 ;jumpspring frame 1 13593 .db $f1, $f1, $f1, $f1, $fc, $fc ; frame 2 13594 .db $f0, $f0, $fc, $fc, $fc, $fc ; frame 3 13595 13596 EnemyGfxTableOffsets: 13597 .db $0c, $0c, $00, $0c, $0c, $a8, $54, $3c 13598 .db $ea, $18, $48, $48, $cc, $c0, $18, $18 13599 .db $18, $90, $24, $ff, $48, $9c, $d2, $d8 13600 .db $f0, $f6, $fc 13601 13602 EnemyAttributeData: 13603 .db $01, $02, $03, $02, $01, $01, $03, $03 13604 .db $03, $01, $01, $02, $02, $21, $01, $02 13605 .db $01, $01, $02, $ff, $02, $02, $01, $01 13606 .db $02, $02, $02 13607 13608 EnemyAnimTimingBMask: 13609 .db $08, $18 13610 13611 JumpspringFrameOffsets: 13612 .db $18, $19, $1a, $19, $18 13613 13614 EnemyGfxHandler: 13615 lda Enemy_Y_Position,x ;get enemy object vertical position 13616 sta $02 13617 lda Enemy_Rel_XPos ;get enemy object horizontal position 13618 sta $05 ;relative to screen 13619 ldy Enemy_SprDataOffset,x 13620 sty $eb ;get sprite data offset 13621 lda #$00 13622 sta VerticalFlipFlag ;initialize vertical flip flag by default 13623 lda Enemy_MovingDir,x 13624 sta $03 ;get enemy object moving direction 13625 lda Enemy_SprAttrib,x 13626 sta $04 ;get enemy object sprite attributes 13627 lda Enemy_ID,x 13628 cmp #PiranhaPlant ;is enemy object piranha plant? 13629 bne CheckForRetainerObj ;if not, branch 13630 ldy PiranhaPlant_Y_Speed,x 13631 bmi CheckForRetainerObj ;if piranha plant moving upwards, branch 13632 ldy EnemyFrameTimer,x 13633 beq CheckForRetainerObj ;if timer for movement expired, branch 13634 rts ;if all conditions fail, leave 13635 13636 CheckForRetainerObj: 13637 lda Enemy_State,x ;store enemy state 13638 sta $ed 13639 and #%00011111 ;nullify all but 5 LSB and use as Y 13640 tay 13641 lda Enemy_ID,x ;check for mushroom retainer/princess object 13642 cmp #RetainerObject 13643 bne CheckForBulletBillCV ;if not found, branch 13644 ldy #$00 ;if found, nullify saved state in Y 13645 lda #$01 ;set value that will not be used 13646 sta $03 13647 lda #$15 ;set value $15 as code for mushroom retainer/princess object 13648 13649 CheckForBulletBillCV: 13650 cmp #BulletBill_CannonVar ;otherwise check for bullet bill object 13651 bne CheckForJumpspring ;if not found, branch again 13652 dec $02 ;decrement saved vertical position 13653 lda #$03 13654 ldy EnemyFrameTimer,x ;get timer for enemy object 13655 beq SBBAt ;if expired, do not set priority bit 13656 ora #%00100000 ;otherwise do so 13657 SBBAt: sta $04 ;set new sprite attributes 13658 ldy #$00 ;nullify saved enemy state both in Y and in 13659 sty $ed ;memory location here 13660 lda #$08 ;set specific value to unconditionally branch once 13661 13662 CheckForJumpspring: 13663 cmp #JumpspringObject ;check for jumpspring object 13664 bne CheckForPodoboo 13665 ldy #$03 ;set enemy state -2 MSB here for jumpspring object 13666 ldx JumpspringAnimCtrl ;get current frame number for jumpspring object 13667 lda JumpspringFrameOffsets,x ;load data using frame number as offset 13668 13669 CheckForPodoboo: 13670 sta $ef ;store saved enemy object value here 13671 sty $ec ;and Y here (enemy state -2 MSB if not changed) 13672 ldx ObjectOffset ;get enemy object offset 13673 cmp #$0c ;check for podoboo object 13674 bne CheckBowserGfxFlag ;branch if not found 13675 lda Enemy_Y_Speed,x ;if moving upwards, branch 13676 bmi CheckBowserGfxFlag 13677 inc VerticalFlipFlag ;otherwise, set flag for vertical flip 13678 13679 CheckBowserGfxFlag: 13680 lda BowserGfxFlag ;if not drawing bowser at all, skip to something else 13681 beq CheckForGoomba 13682 ldy #$16 ;if set to 1, draw bowser's front 13683 cmp #$01 13684 beq SBwsrGfxOfs 13685 iny ;otherwise draw bowser's rear 13686 SBwsrGfxOfs: sty $ef 13687 13688 CheckForGoomba: 13689 ldy $ef ;check value for goomba object 13690 cpy #Goomba 13691 bne CheckBowserFront ;branch if not found 13692 lda Enemy_State,x 13693 cmp #$02 ;check for defeated state 13694 bcc GmbaAnim ;if not defeated, go ahead and animate 13695 ldx #$04 ;if defeated, write new value here 13696 stx $ec 13697 GmbaAnim: and #%00100000 ;check for d5 set in enemy object state 13698 ora TimerControl ;or timer disable flag set 13699 bne CheckBowserFront ;if either condition true, do not animate goomba 13700 lda FrameCounter 13701 and #%00001000 ;check for every eighth frame 13702 bne CheckBowserFront 13703 lda $03 13704 eor #%00000011 ;invert bits to flip horizontally every eight frames 13705 sta $03 ;leave alone otherwise 13706 13707 CheckBowserFront: 13708 lda EnemyAttributeData,y ;load sprite attribute using enemy object 13709 ora $04 ;as offset, and add to bits already loaded 13710 sta $04 13711 lda EnemyGfxTableOffsets,y ;load value based on enemy object as offset 13712 tax ;save as X 13713 ldy $ec ;get previously saved value 13714 lda BowserGfxFlag 13715 beq CheckForSpiny ;if not drawing bowser object at all, skip all of this 13716 cmp #$01 13717 bne CheckBowserRear ;if not drawing front part, branch to draw the rear part 13718 lda BowserBodyControls ;check bowser's body control bits 13719 bpl ChkFrontSte ;branch if d7 not set (control's bowser's mouth) 13720 ldx #$de ;otherwise load offset for second frame 13721 ChkFrontSte: lda $ed ;check saved enemy state 13722 and #%00100000 ;if bowser not defeated, do not set flag 13723 beq DrawBowser 13724 13725 FlipBowserOver: 13726 stx VerticalFlipFlag ;set vertical flip flag to nonzero 13727 13728 DrawBowser: 13729 jmp DrawEnemyObject ;draw bowser's graphics now 13730 13731 CheckBowserRear: 13732 lda BowserBodyControls ;check bowser's body control bits 13733 and #$01 13734 beq ChkRearSte ;branch if d0 not set (control's bowser's feet) 13735 ldx #$e4 ;otherwise load offset for second frame 13736 ChkRearSte: lda $ed ;check saved enemy state 13737 and #%00100000 ;if bowser not defeated, do not set flag 13738 beq DrawBowser 13739 lda $02 ;subtract 16 pixels from 13740 sec ;saved vertical coordinate 13741 sbc #$10 13742 sta $02 13743 jmp FlipBowserOver ;jump to set vertical flip flag 13744 13745 CheckForSpiny: 13746 cpx #$24 ;check if value loaded is for spiny 13747 bne CheckForLakitu ;if not found, branch 13748 cpy #$05 ;if enemy state set to $05, do this, 13749 bne NotEgg ;otherwise branch 13750 ldx #$30 ;set to spiny egg offset 13751 lda #$02 13752 sta $03 ;set enemy direction to reverse sprites horizontally 13753 lda #$05 13754 sta $ec ;set enemy state 13755 NotEgg: jmp CheckForHammerBro ;skip a big chunk of this if we found spiny but not in egg 13756 13757 CheckForLakitu: 13758 cpx #$90 ;check value for lakitu's offset loaded 13759 bne CheckUpsideDownShell ;branch if not loaded 13760 lda $ed 13761 and #%00100000 ;check for d5 set in enemy state 13762 bne NoLAFr ;branch if set 13763 lda FrenzyEnemyTimer 13764 cmp #$10 ;check timer to see if we've reached a certain range 13765 bcs NoLAFr ;branch if not 13766 ldx #$96 ;if d6 not set and timer in range, load alt frame for lakitu 13767 NoLAFr: jmp CheckDefeatedState ;skip this next part if we found lakitu but alt frame not needed 13768 13769 CheckUpsideDownShell: 13770 lda $ef ;check for enemy object => $04 13771 cmp #$04 13772 bcs CheckRightSideUpShell ;branch if true 13773 cpy #$02 13774 bcc CheckRightSideUpShell ;branch if enemy state < $02 13775 ldx #$5a ;set for upside-down koopa shell by default 13776 ldy $ef 13777 cpy #BuzzyBeetle ;check for buzzy beetle object 13778 bne CheckRightSideUpShell 13779 ldx #$7e ;set for upside-down buzzy beetle shell if found 13780 inc $02 ;increment vertical position by one pixel 13781 13782 CheckRightSideUpShell: 13783 lda $ec ;check for value set here 13784 cmp #$04 ;if enemy state < $02, do not change to shell, if 13785 bne CheckForHammerBro ;enemy state => $02 but not = $04, leave shell upside-down 13786 ldx #$72 ;set right-side up buzzy beetle shell by default 13787 inc $02 ;increment saved vertical position by one pixel 13788 ldy $ef 13789 cpy #BuzzyBeetle ;check for buzzy beetle object 13790 beq CheckForDefdGoomba ;branch if found 13791 ldx #$66 ;change to right-side up koopa shell if not found 13792 inc $02 ;and increment saved vertical position again 13793 13794 CheckForDefdGoomba: 13795 cpy #Goomba ;check for goomba object (necessary if previously 13796 bne CheckForHammerBro ;failed buzzy beetle object test) 13797 ldx #$54 ;load for regular goomba 13798 lda $ed ;note that this only gets performed if enemy state => $02 13799 and #%00100000 ;check saved enemy state for d5 set 13800 bne CheckForHammerBro ;branch if set 13801 ldx #$8a ;load offset for defeated goomba 13802 dec $02 ;set different value and decrement saved vertical position 13803 13804 CheckForHammerBro: 13805 ldy ObjectOffset 13806 lda $ef ;check for hammer bro object 13807 cmp #HammerBro 13808 bne CheckForBloober ;branch if not found 13809 lda $ed 13810 beq CheckToAnimateEnemy ;branch if not in normal enemy state 13811 and #%00001000 13812 beq CheckDefeatedState ;if d3 not set, branch further away 13813 ldx #$b4 ;otherwise load offset for different frame 13814 bne CheckToAnimateEnemy ;unconditional branch 13815 13816 CheckForBloober: 13817 cpx #$48 ;check for cheep-cheep offset loaded 13818 beq CheckToAnimateEnemy ;branch if found 13819 lda EnemyIntervalTimer,y 13820 cmp #$05 13821 bcs CheckDefeatedState ;branch if some timer is above a certain point 13822 cpx #$3c ;check for bloober offset loaded 13823 bne CheckToAnimateEnemy ;branch if not found this time 13824 cmp #$01 13825 beq CheckDefeatedState ;branch if timer is set to certain point 13826 inc $02 ;increment saved vertical coordinate three pixels 13827 inc $02 13828 inc $02 13829 jmp CheckAnimationStop ;and do something else 13830 13831 CheckToAnimateEnemy: 13832 lda $ef ;check for specific enemy objects 13833 cmp #Goomba 13834 beq CheckDefeatedState ;branch if goomba 13835 cmp #$08 13836 beq CheckDefeatedState ;branch if bullet bill (note both variants use $08 here) 13837 cmp #Podoboo 13838 beq CheckDefeatedState ;branch if podoboo 13839 cmp #$18 ;branch if => $18 13840 bcs CheckDefeatedState 13841 ldy #$00 13842 cmp #$15 ;check for mushroom retainer/princess object 13843 bne CheckForSecondFrame ;which uses different code here, branch if not found 13844 iny ;residual instruction 13845 lda WorldNumber ;are we on world 8? 13846 cmp #World8 13847 bcs CheckDefeatedState ;if so, leave the offset alone (use princess) 13848 ldx #$a2 ;otherwise, set for mushroom retainer object instead 13849 lda #$03 ;set alternate state here 13850 sta $ec 13851 bne CheckDefeatedState ;unconditional branch 13852 13853 CheckForSecondFrame: 13854 lda FrameCounter ;load frame counter 13855 and EnemyAnimTimingBMask,y ;mask it (partly residual, one byte not ever used) 13856 bne CheckDefeatedState ;branch if timing is off 13857 13858 CheckAnimationStop: 13859 lda $ed ;check saved enemy state 13860 and #%10100000 ;for d7 or d5, or check for timers stopped 13861 ora TimerControl 13862 bne CheckDefeatedState ;if either condition true, branch 13863 txa 13864 clc 13865 adc #$06 ;add $06 to current enemy offset 13866 tax ;to animate various enemy objects 13867 13868 CheckDefeatedState: 13869 lda $ed ;check saved enemy state 13870 and #%00100000 ;for d5 set 13871 beq DrawEnemyObject ;branch if not set 13872 lda $ef 13873 cmp #$04 ;check for saved enemy object => $04 13874 bcc DrawEnemyObject ;branch if less 13875 ldy #$01 13876 sty VerticalFlipFlag ;set vertical flip flag 13877 dey 13878 sty $ec ;init saved value here 13879 13880 DrawEnemyObject: 13881 ldy $eb ;load sprite data offset 13882 jsr DrawEnemyObjRow ;draw six tiles of data 13883 jsr DrawEnemyObjRow ;into sprite data 13884 jsr DrawEnemyObjRow 13885 ldx ObjectOffset ;get enemy object offset 13886 ldy Enemy_SprDataOffset,x ;get sprite data offset 13887 lda $ef 13888 cmp #$08 ;get saved enemy object and check 13889 bne CheckForVerticalFlip ;for bullet bill, branch if not found 13890 13891 SkipToOffScrChk: 13892 jmp SprObjectOffscrChk ;jump if found 13893 13894 CheckForVerticalFlip: 13895 lda VerticalFlipFlag ;check if vertical flip flag is set here 13896 beq CheckForESymmetry ;branch if not 13897 lda Sprite_Attributes,y ;get attributes of first sprite we dealt with 13898 ora #%10000000 ;set bit for vertical flip 13899 iny 13900 iny ;increment two bytes so that we store the vertical flip 13901 jsr DumpSixSpr ;in attribute bytes of enemy obj sprite data 13902 dey 13903 dey ;now go back to the Y coordinate offset 13904 tya 13905 tax ;give offset to X 13906 lda $ef 13907 cmp #HammerBro ;check saved enemy object for hammer bro 13908 beq FlipEnemyVertically 13909 cmp #Lakitu ;check saved enemy object for lakitu 13910 beq FlipEnemyVertically ;branch for hammer bro or lakitu 13911 cmp #$15 13912 bcs FlipEnemyVertically ;also branch if enemy object => $15 13913 txa 13914 clc 13915 adc #$08 ;if not selected objects or => $15, set 13916 tax ;offset in X for next row 13917 13918 FlipEnemyVertically: 13919 lda Sprite_Tilenumber,x ;load first or second row tiles 13920 pha ;and save tiles to the stack 13921 lda Sprite_Tilenumber+4,x 13922 pha 13923 lda Sprite_Tilenumber+16,y ;exchange third row tiles 13924 sta Sprite_Tilenumber,x ;with first or second row tiles 13925 lda Sprite_Tilenumber+20,y 13926 sta Sprite_Tilenumber+4,x 13927 pla ;pull first or second row tiles from stack 13928 sta Sprite_Tilenumber+20,y ;and save in third row 13929 pla 13930 sta Sprite_Tilenumber+16,y 13931 13932 CheckForESymmetry: 13933 lda BowserGfxFlag ;are we drawing bowser at all? 13934 bne SkipToOffScrChk ;branch if so 13935 lda $ef 13936 ldx $ec ;get alternate enemy state 13937 cmp #$05 ;check for hammer bro object 13938 bne ContES 13939 jmp SprObjectOffscrChk ;jump if found 13940 ContES: cmp #Bloober ;check for bloober object 13941 beq MirrorEnemyGfx 13942 cmp #PiranhaPlant ;check for piranha plant object 13943 beq MirrorEnemyGfx 13944 cmp #Podoboo ;check for podoboo object 13945 beq MirrorEnemyGfx ;branch if either of three are found 13946 cmp #Spiny ;check for spiny object 13947 bne ESRtnr ;branch closer if not found 13948 cpx #$05 ;check spiny's state 13949 bne CheckToMirrorLakitu ;branch if not an egg, otherwise 13950 ESRtnr: cmp #$15 ;check for princess/mushroom retainer object 13951 bne SpnySC 13952 lda #$42 ;set horizontal flip on bottom right sprite 13953 sta Sprite_Attributes+20,y ;note that palette bits were already set earlier 13954 SpnySC: cpx #$02 ;if alternate enemy state set to 1 or 0, branch 13955 bcc CheckToMirrorLakitu 13956 13957 MirrorEnemyGfx: 13958 lda BowserGfxFlag ;if enemy object is bowser, skip all of this 13959 bne CheckToMirrorLakitu 13960 lda Sprite_Attributes,y ;load attribute bits of first sprite 13961 and #%10100011 13962 sta Sprite_Attributes,y ;save vertical flip, priority, and palette bits 13963 sta Sprite_Attributes+8,y ;in left sprite column of enemy object OAM data 13964 sta Sprite_Attributes+16,y 13965 ora #%01000000 ;set horizontal flip 13966 cpx #$05 ;check for state used by spiny's egg 13967 bne EggExc ;if alternate state not set to $05, branch 13968 ora #%10000000 ;otherwise set vertical flip 13969 EggExc: sta Sprite_Attributes+4,y ;set bits of right sprite column 13970 sta Sprite_Attributes+12,y ;of enemy object sprite data 13971 sta Sprite_Attributes+20,y 13972 cpx #$04 ;check alternate enemy state 13973 bne CheckToMirrorLakitu ;branch if not $04 13974 lda Sprite_Attributes+8,y ;get second row left sprite attributes 13975 ora #%10000000 13976 sta Sprite_Attributes+8,y ;store bits with vertical flip in 13977 sta Sprite_Attributes+16,y ;second and third row left sprites 13978 ora #%01000000 13979 sta Sprite_Attributes+12,y ;store with horizontal and vertical flip in 13980 sta Sprite_Attributes+20,y ;second and third row right sprites 13981 13982 CheckToMirrorLakitu: 13983 lda $ef ;check for lakitu enemy object 13984 cmp #Lakitu 13985 bne CheckToMirrorJSpring ;branch if not found 13986 lda VerticalFlipFlag 13987 bne NVFLak ;branch if vertical flip flag not set 13988 lda Sprite_Attributes+16,y ;save vertical flip and palette bits 13989 and #%10000001 ;in third row left sprite 13990 sta Sprite_Attributes+16,y 13991 lda Sprite_Attributes+20,y ;set horizontal flip and palette bits 13992 ora #%01000001 ;in third row right sprite 13993 sta Sprite_Attributes+20,y 13994 ldx FrenzyEnemyTimer ;check timer 13995 cpx #$10 13996 bcs SprObjectOffscrChk ;branch if timer has not reached a certain range 13997 sta Sprite_Attributes+12,y ;otherwise set same for second row right sprite 13998 and #%10000001 13999 sta Sprite_Attributes+8,y ;preserve vertical flip and palette bits for left sprite 14000 bcc SprObjectOffscrChk ;unconditional branch 14001 NVFLak: lda Sprite_Attributes,y ;get first row left sprite attributes 14002 and #%10000001 14003 sta Sprite_Attributes,y ;save vertical flip and palette bits 14004 lda Sprite_Attributes+4,y ;get first row right sprite attributes 14005 ora #%01000001 ;set horizontal flip and palette bits 14006 sta Sprite_Attributes+4,y ;note that vertical flip is left as-is 14007 14008 CheckToMirrorJSpring: 14009 lda $ef ;check for jumpspring object (any frame) 14010 cmp #$18 14011 bcc SprObjectOffscrChk ;branch if not jumpspring object at all 14012 lda #$82 14013 sta Sprite_Attributes+8,y ;set vertical flip and palette bits of 14014 sta Sprite_Attributes+16,y ;second and third row left sprites 14015 ora #%01000000 14016 sta Sprite_Attributes+12,y ;set, in addition to those, horizontal flip 14017 sta Sprite_Attributes+20,y ;for second and third row right sprites 14018 14019 SprObjectOffscrChk: 14020 ldx ObjectOffset ;get enemy buffer offset 14021 lda Enemy_OffscreenBits ;check offscreen information 14022 lsr 14023 lsr ;shift three times to the right 14024 lsr ;which puts d2 into carry 14025 pha ;save to stack 14026 bcc LcChk ;branch if not set 14027 lda #$04 ;set for right column sprites 14028 jsr MoveESprColOffscreen ;and move them offscreen 14029 LcChk: pla ;get from stack 14030 lsr ;move d3 to carry 14031 pha ;save to stack 14032 bcc Row3C ;branch if not set 14033 lda #$00 ;set for left column sprites, 14034 jsr MoveESprColOffscreen ;move them offscreen 14035 Row3C: pla ;get from stack again 14036 lsr ;move d5 to carry this time 14037 lsr 14038 pha ;save to stack again 14039 bcc Row23C ;branch if carry not set 14040 lda #$10 ;set for third row of sprites 14041 jsr MoveESprRowOffscreen ;and move them offscreen 14042 Row23C: pla ;get from stack 14043 lsr ;move d6 into carry 14044 pha ;save to stack 14045 bcc AllRowC 14046 lda #$08 ;set for second and third rows 14047 jsr MoveESprRowOffscreen ;move them offscreen 14048 AllRowC: pla ;get from stack once more 14049 lsr ;move d7 into carry 14050 bcc ExEGHandler 14051 jsr MoveESprRowOffscreen ;move all sprites offscreen (A should be 0 by now) 14052 lda Enemy_ID,x 14053 cmp #Podoboo ;check enemy identifier for podoboo 14054 beq ExEGHandler ;skip this part if found, we do not want to erase podoboo! 14055 lda Enemy_Y_HighPos,x ;check high byte of vertical position 14056 cmp #$02 ;if not yet past the bottom of the screen, branch 14057 bne ExEGHandler 14058 jsr EraseEnemyObject ;what it says 14059 14060 ExEGHandler: 14061 rts 14062 14063 DrawEnemyObjRow: 14064 lda EnemyGraphicsTable,x ;load two tiles of enemy graphics 14065 sta $00 14066 lda EnemyGraphicsTable+1,x 14067 14068 DrawOneSpriteRow: 14069 sta $01 14070 jmp DrawSpriteObject ;draw them 14071 14072 MoveESprRowOffscreen: 14073 clc ;add A to enemy object OAM data offset 14074 adc Enemy_SprDataOffset,x 14075 tay ;use as offset 14076 lda #$f8 14077 jmp DumpTwoSpr ;move first row of sprites offscreen 14078 14079 MoveESprColOffscreen: 14080 clc ;add A to enemy object OAM data offset 14081 adc Enemy_SprDataOffset,x 14082 tay ;use as offset 14083 jsr MoveColOffscreen ;move first and second row sprites in column offscreen 14084 sta Sprite_Data+16,y ;move third row sprite in column offscreen 14085 rts 14086 14087 ;------------------------------------------------------------------------------------- 14088 ;$00-$01 - tile numbers 14089 ;$02 - relative Y position 14090 ;$03 - horizontal flip flag (not used here) 14091 ;$04 - attributes 14092 ;$05 - relative X position 14093 14094 DefaultBlockObjTiles: 14095 .db $85, $85, $86, $86 ;brick w/ line (these are sprite tiles, not BG!) 14096 14097 DrawBlock: 14098 lda Block_Rel_YPos ;get relative vertical coordinate of block object 14099 sta $02 ;store here 14100 lda Block_Rel_XPos ;get relative horizontal coordinate of block object 14101 sta $05 ;store here 14102 lda #$03 14103 sta $04 ;set attribute byte here 14104 lsr 14105 sta $03 ;set horizontal flip bit here (will not be used) 14106 ldy Block_SprDataOffset,x ;get sprite data offset 14107 ldx #$00 ;reset X for use as offset to tile data 14108 DBlkLoop: lda DefaultBlockObjTiles,x ;get left tile number 14109 sta $00 ;set here 14110 lda DefaultBlockObjTiles+1,x ;get right tile number 14111 jsr DrawOneSpriteRow ;do sub to write tile numbers to first row of sprites 14112 cpx #$04 ;check incremented offset 14113 bne DBlkLoop ;and loop back until all four sprites are done 14114 ldx ObjectOffset ;get block object offset 14115 ldy Block_SprDataOffset,x ;get sprite data offset 14116 lda AreaType 14117 cmp #$01 ;check for ground level type area 14118 beq ChkRep ;if found, branch to next part 14119 lda #$86 14120 sta Sprite_Tilenumber,y ;otherwise remove brick tiles with lines 14121 sta Sprite_Tilenumber+4,y ;and replace then with lineless brick tiles 14122 ChkRep: lda Block_Metatile,x ;check replacement metatile 14123 cmp #$c4 ;if not used block metatile, then 14124 bne BlkOffscr ;branch ahead to use current graphics 14125 lda #$87 ;set A for used block tile 14126 iny ;increment Y to write to tile bytes 14127 jsr DumpFourSpr ;do sub to dump into all four sprites 14128 dey ;return Y to original offset 14129 lda #$03 ;set palette bits 14130 ldx AreaType 14131 dex ;check for ground level type area again 14132 beq SetBFlip ;if found, use current palette bits 14133 lsr ;otherwise set to $01 14134 SetBFlip: ldx ObjectOffset ;put block object offset back in X 14135 sta Sprite_Attributes,y ;store attribute byte as-is in first sprite 14136 ora #%01000000 14137 sta Sprite_Attributes+4,y ;set horizontal flip bit for second sprite 14138 ora #%10000000 14139 sta Sprite_Attributes+12,y ;set both flip bits for fourth sprite 14140 and #%10000011 14141 sta Sprite_Attributes+8,y ;set vertical flip bit for third sprite 14142 BlkOffscr: lda Block_OffscreenBits ;get offscreen bits for block object 14143 pha ;save to stack 14144 and #%00000100 ;check to see if d2 in offscreen bits are set 14145 beq PullOfsB ;if not set, branch, otherwise move sprites offscreen 14146 lda #$f8 ;move offscreen two OAMs 14147 sta Sprite_Y_Position+4,y ;on the right side 14148 sta Sprite_Y_Position+12,y 14149 PullOfsB: pla ;pull offscreen bits from stack 14150 ChkLeftCo: and #%00001000 ;check to see if d3 in offscreen bits are set 14151 beq ExDBlk ;if not set, branch, otherwise move sprites offscreen 14152 14153 MoveColOffscreen: 14154 lda #$f8 ;move offscreen two OAMs 14155 sta Sprite_Y_Position,y ;on the left side (or two rows of enemy on either side 14156 sta Sprite_Y_Position+8,y ;if branched here from enemy graphics handler) 14157 ExDBlk: rts 14158 14159 ;------------------------------------------------------------------------------------- 14160 ;$00 - used to hold palette bits for attribute byte or relative X position 14161 14162 DrawBrickChunks: 14163 lda #$02 ;set palette bits here 14164 sta $00 14165 lda #$75 ;set tile number for ball (something residual, likely) 14166 ldy GameEngineSubroutine 14167 cpy #$05 ;if end-of-level routine running, 14168 beq DChunks ;use palette and tile number assigned 14169 lda #$03 ;otherwise set different palette bits 14170 sta $00 14171 lda #$84 ;and set tile number for brick chunks 14172 DChunks: ldy Block_SprDataOffset,x ;get OAM data offset 14173 iny ;increment to start with tile bytes in OAM 14174 jsr DumpFourSpr ;do sub to dump tile number into all four sprites 14175 lda FrameCounter ;get frame counter 14176 asl 14177 asl 14178 asl ;move low nybble to high 14179 asl 14180 and #$c0 ;get what was originally d3-d2 of low nybble 14181 ora $00 ;add palette bits 14182 iny ;increment offset for attribute bytes 14183 jsr DumpFourSpr ;do sub to dump attribute data into all four sprites 14184 dey 14185 dey ;decrement offset to Y coordinate 14186 lda Block_Rel_YPos ;get first block object's relative vertical coordinate 14187 jsr DumpTwoSpr ;do sub to dump current Y coordinate into two sprites 14188 lda Block_Rel_XPos ;get first block object's relative horizontal coordinate 14189 sta Sprite_X_Position,y ;save into X coordinate of first sprite 14190 lda Block_Orig_XPos,x ;get original horizontal coordinate 14191 sec 14192 sbc ScreenLeft_X_Pos ;subtract coordinate of left side from original coordinate 14193 sta $00 ;store result as relative horizontal coordinate of original 14194 sec 14195 sbc Block_Rel_XPos ;get difference of relative positions of original - current 14196 adc $00 ;add original relative position to result 14197 adc #$06 ;plus 6 pixels to position second brick chunk correctly 14198 sta Sprite_X_Position+4,y ;save into X coordinate of second sprite 14199 lda Block_Rel_YPos+1 ;get second block object's relative vertical coordinate 14200 sta Sprite_Y_Position+8,y 14201 sta Sprite_Y_Position+12,y ;dump into Y coordinates of third and fourth sprites 14202 lda Block_Rel_XPos+1 ;get second block object's relative horizontal coordinate 14203 sta Sprite_X_Position+8,y ;save into X coordinate of third sprite 14204 lda $00 ;use original relative horizontal position 14205 sec 14206 sbc Block_Rel_XPos+1 ;get difference of relative positions of original - current 14207 adc $00 ;add original relative position to result 14208 adc #$06 ;plus 6 pixels to position fourth brick chunk correctly 14209 sta Sprite_X_Position+12,y ;save into X coordinate of fourth sprite 14210 lda Block_OffscreenBits ;get offscreen bits for block object 14211 jsr ChkLeftCo ;do sub to move left half of sprites offscreen if necessary 14212 lda Block_OffscreenBits ;get offscreen bits again 14213 asl ;shift d7 into carry 14214 bcc ChnkOfs ;if d7 not set, branch to last part 14215 lda #$f8 14216 jsr DumpTwoSpr ;otherwise move top sprites offscreen 14217 ChnkOfs: lda $00 ;if relative position on left side of screen, 14218 bpl ExBCDr ;go ahead and leave 14219 lda Sprite_X_Position,y ;otherwise compare left-side X coordinate 14220 cmp Sprite_X_Position+4,y ;to right-side X coordinate 14221 bcc ExBCDr ;branch to leave if less 14222 lda #$f8 ;otherwise move right half of sprites offscreen 14223 sta Sprite_Y_Position+4,y 14224 sta Sprite_Y_Position+12,y 14225 ExBCDr: rts ;leave 14226 14227 ;------------------------------------------------------------------------------------- 14228 14229 DrawFireball: 14230 ldy FBall_SprDataOffset,x ;get fireball's sprite data offset 14231 lda Fireball_Rel_YPos ;get relative vertical coordinate 14232 sta Sprite_Y_Position,y ;store as sprite Y coordinate 14233 lda Fireball_Rel_XPos ;get relative horizontal coordinate 14234 sta Sprite_X_Position,y ;store as sprite X coordinate, then do shared code 14235 14236 DrawFirebar: 14237 lda FrameCounter ;get frame counter 14238 lsr ;divide by four 14239 lsr 14240 pha ;save result to stack 14241 and #$01 ;mask out all but last bit 14242 eor #$64 ;set either tile $64 or $65 as fireball tile 14243 sta Sprite_Tilenumber,y ;thus tile changes every four frames 14244 pla ;get from stack 14245 lsr ;divide by four again 14246 lsr 14247 lda #$02 ;load value $02 to set palette in attrib byte 14248 bcc FireA ;if last bit shifted out was not set, skip this 14249 ora #%11000000 ;otherwise flip both ways every eight frames 14250 FireA: sta Sprite_Attributes,y ;store attribute byte and leave 14251 rts 14252 14253 ;------------------------------------------------------------------------------------- 14254 14255 ExplosionTiles: 14256 .db $68, $67, $66 14257 14258 DrawExplosion_Fireball: 14259 ldy Alt_SprDataOffset,x ;get OAM data offset of alternate sort for fireball's explosion 14260 lda Fireball_State,x ;load fireball state 14261 inc Fireball_State,x ;increment state for next frame 14262 lsr ;divide by 2 14263 and #%00000111 ;mask out all but d3-d1 14264 cmp #$03 ;check to see if time to kill fireball 14265 bcs KillFireBall ;branch if so, otherwise continue to draw explosion 14266 14267 DrawExplosion_Fireworks: 14268 tax ;use whatever's in A for offset 14269 lda ExplosionTiles,x ;get tile number using offset 14270 iny ;increment Y (contains sprite data offset) 14271 jsr DumpFourSpr ;and dump into tile number part of sprite data 14272 dey ;decrement Y so we have the proper offset again 14273 ldx ObjectOffset ;return enemy object buffer offset to X 14274 lda Fireball_Rel_YPos ;get relative vertical coordinate 14275 sec ;subtract four pixels vertically 14276 sbc #$04 ;for first and third sprites 14277 sta Sprite_Y_Position,y 14278 sta Sprite_Y_Position+8,y 14279 clc ;add eight pixels vertically 14280 adc #$08 ;for second and fourth sprites 14281 sta Sprite_Y_Position+4,y 14282 sta Sprite_Y_Position+12,y 14283 lda Fireball_Rel_XPos ;get relative horizontal coordinate 14284 sec ;subtract four pixels horizontally 14285 sbc #$04 ;for first and second sprites 14286 sta Sprite_X_Position,y 14287 sta Sprite_X_Position+4,y 14288 clc ;add eight pixels horizontally 14289 adc #$08 ;for third and fourth sprites 14290 sta Sprite_X_Position+8,y 14291 sta Sprite_X_Position+12,y 14292 lda #$02 ;set palette attributes for all sprites, but 14293 sta Sprite_Attributes,y ;set no flip at all for first sprite 14294 lda #$82 14295 sta Sprite_Attributes+4,y ;set vertical flip for second sprite 14296 lda #$42 14297 sta Sprite_Attributes+8,y ;set horizontal flip for third sprite 14298 lda #$c2 14299 sta Sprite_Attributes+12,y ;set both flips for fourth sprite 14300 rts ;we are done 14301 14302 KillFireBall: 14303 lda #$00 ;clear fireball state to kill it 14304 sta Fireball_State,x 14305 rts 14306 14307 ;------------------------------------------------------------------------------------- 14308 14309 DrawSmallPlatform: 14310 ldy Enemy_SprDataOffset,x ;get OAM data offset 14311 lda #$5b ;load tile number for small platforms 14312 iny ;increment offset for tile numbers 14313 jsr DumpSixSpr ;dump tile number into all six sprites 14314 iny ;increment offset for attributes 14315 lda #$02 ;load palette controls 14316 jsr DumpSixSpr ;dump attributes into all six sprites 14317 dey ;decrement for original offset 14318 dey 14319 lda Enemy_Rel_XPos ;get relative horizontal coordinate 14320 sta Sprite_X_Position,y 14321 sta Sprite_X_Position+12,y ;dump as X coordinate into first and fourth sprites 14322 clc 14323 adc #$08 ;add eight pixels 14324 sta Sprite_X_Position+4,y ;dump into second and fifth sprites 14325 sta Sprite_X_Position+16,y 14326 clc 14327 adc #$08 ;add eight more pixels 14328 sta Sprite_X_Position+8,y ;dump into third and sixth sprites 14329 sta Sprite_X_Position+20,y 14330 lda Enemy_Y_Position,x ;get vertical coordinate 14331 tax 14332 pha ;save to stack 14333 cpx #$20 ;if vertical coordinate below status bar, 14334 bcs TopSP ;do not mess with it 14335 lda #$f8 ;otherwise move first three sprites offscreen 14336 TopSP: jsr DumpThreeSpr ;dump vertical coordinate into Y coordinates 14337 pla ;pull from stack 14338 clc 14339 adc #$80 ;add 128 pixels 14340 tax 14341 cpx #$20 ;if below status bar (taking wrap into account) 14342 bcs BotSP ;then do not change altered coordinate 14343 lda #$f8 ;otherwise move last three sprites offscreen 14344 BotSP: sta Sprite_Y_Position+12,y ;dump vertical coordinate + 128 pixels 14345 sta Sprite_Y_Position+16,y ;into Y coordinates 14346 sta Sprite_Y_Position+20,y 14347 lda Enemy_OffscreenBits ;get offscreen bits 14348 pha ;save to stack 14349 and #%00001000 ;check d3 14350 beq SOfs 14351 lda #$f8 ;if d3 was set, move first and 14352 sta Sprite_Y_Position,y ;fourth sprites offscreen 14353 sta Sprite_Y_Position+12,y 14354 SOfs: pla ;move out and back into stack 14355 pha 14356 and #%00000100 ;check d2 14357 beq SOfs2 14358 lda #$f8 ;if d2 was set, move second and 14359 sta Sprite_Y_Position+4,y ;fifth sprites offscreen 14360 sta Sprite_Y_Position+16,y 14361 SOfs2: pla ;get from stack 14362 and #%00000010 ;check d1 14363 beq ExSPl 14364 lda #$f8 ;if d1 was set, move third and 14365 sta Sprite_Y_Position+8,y ;sixth sprites offscreen 14366 sta Sprite_Y_Position+20,y 14367 ExSPl: ldx ObjectOffset ;get enemy object offset and leave 14368 rts 14369 14370 ;------------------------------------------------------------------------------------- 14371 14372 DrawBubble: 14373 ldy Player_Y_HighPos ;if player's vertical high position 14374 dey ;not within screen, skip all of this 14375 bne ExDBub 14376 lda Bubble_OffscreenBits ;check air bubble's offscreen bits 14377 and #%00001000 14378 bne ExDBub ;if bit set, branch to leave 14379 ldy Bubble_SprDataOffset,x ;get air bubble's OAM data offset 14380 lda Bubble_Rel_XPos ;get relative horizontal coordinate 14381 sta Sprite_X_Position,y ;store as X coordinate here 14382 lda Bubble_Rel_YPos ;get relative vertical coordinate 14383 sta Sprite_Y_Position,y ;store as Y coordinate here 14384 lda #$74 14385 sta Sprite_Tilenumber,y ;put air bubble tile into OAM data 14386 lda #$02 14387 sta Sprite_Attributes,y ;set attribute byte 14388 ExDBub: rts ;leave 14389 14390 ;------------------------------------------------------------------------------------- 14391 ;$00 - used to store player's vertical offscreen bits 14392 14393 PlayerGfxTblOffsets: 14394 .db $20, $28, $c8, $18, $00, $40, $50, $58 14395 .db $80, $88, $b8, $78, $60, $a0, $b0, $b8 14396 14397 ;tiles arranged in order, 2 tiles per row, top to bottom 14398 14399 PlayerGraphicsTable: 14400 ;big player table 14401 .db $00, $01, $02, $03, $04, $05, $06, $07 ;walking frame 1 14402 .db $08, $09, $0a, $0b, $0c, $0d, $0e, $0f ; frame 2 14403 .db $10, $11, $12, $13, $14, $15, $16, $17 ; frame 3 14404 .db $18, $19, $1a, $1b, $1c, $1d, $1e, $1f ;skidding 14405 .db $20, $21, $22, $23, $24, $25, $26, $27 ;jumping 14406 .db $08, $09, $28, $29, $2a, $2b, $2c, $2d ;swimming frame 1 14407 .db $08, $09, $0a, $0b, $0c, $30, $2c, $2d ; frame 2 14408 .db $08, $09, $0a, $0b, $2e, $2f, $2c, $2d ; frame 3 14409 .db $08, $09, $28, $29, $2a, $2b, $5c, $5d ;climbing frame 1 14410 .db $08, $09, $0a, $0b, $0c, $0d, $5e, $5f ; frame 2 14411 .db $fc, $fc, $08, $09, $58, $59, $5a, $5a ;crouching 14412 .db $08, $09, $28, $29, $2a, $2b, $0e, $0f ;fireball throwing 14413 14414 ;small player table 14415 .db $fc, $fc, $fc, $fc, $32, $33, $34, $35 ;walking frame 1 14416 .db $fc, $fc, $fc, $fc, $36, $37, $38, $39 ; frame 2 14417 .db $fc, $fc, $fc, $fc, $3a, $37, $3b, $3c ; frame 3 14418 .db $fc, $fc, $fc, $fc, $3d, $3e, $3f, $40 ;skidding 14419 .db $fc, $fc, $fc, $fc, $32, $41, $42, $43 ;jumping 14420 .db $fc, $fc, $fc, $fc, $32, $33, $44, $45 ;swimming frame 1 14421 .db $fc, $fc, $fc, $fc, $32, $33, $44, $47 ; frame 2 14422 .db $fc, $fc, $fc, $fc, $32, $33, $48, $49 ; frame 3 14423 .db $fc, $fc, $fc, $fc, $32, $33, $90, $91 ;climbing frame 1 14424 .db $fc, $fc, $fc, $fc, $3a, $37, $92, $93 ; frame 2 14425 .db $fc, $fc, $fc, $fc, $9e, $9e, $9f, $9f ;killed 14426 14427 ;used by both player sizes 14428 .db $fc, $fc, $fc, $fc, $3a, $37, $4f, $4f ;small player standing 14429 .db $fc, $fc, $00, $01, $4c, $4d, $4e, $4e ;intermediate grow frame 14430 .db $00, $01, $4c, $4d, $4a, $4a, $4b, $4b ;big player standing 14431 14432 SwimKickTileNum: 14433 .db $31, $46 14434 14435 PlayerGfxHandler: 14436 lda InjuryTimer ;if player's injured invincibility timer 14437 beq CntPl ;not set, skip checkpoint and continue code 14438 lda FrameCounter 14439 lsr ;otherwise check frame counter and branch 14440 bcs ExPGH ;to leave on every other frame (when d0 is set) 14441 CntPl: lda GameEngineSubroutine ;if executing specific game engine routine, 14442 cmp #$0b ;branch ahead to some other part 14443 beq PlayerKilled 14444 lda PlayerChangeSizeFlag ;if grow/shrink flag set 14445 bne DoChangeSize ;then branch to some other code 14446 ldy SwimmingFlag ;if swimming flag set, branch to 14447 beq FindPlayerAction ;different part, do not return 14448 lda Player_State 14449 cmp #$00 ;if player status normal, 14450 beq FindPlayerAction ;branch and do not return 14451 jsr FindPlayerAction ;otherwise jump and return 14452 lda FrameCounter 14453 and #%00000100 ;check frame counter for d2 set (8 frames every 14454 bne ExPGH ;eighth frame), and branch if set to leave 14455 tax ;initialize X to zero 14456 ldy Player_SprDataOffset ;get player sprite data offset 14457 lda PlayerFacingDir ;get player's facing direction 14458 lsr 14459 bcs SwimKT ;if player facing to the right, use current offset 14460 iny 14461 iny ;otherwise move to next OAM data 14462 iny 14463 iny 14464 SwimKT: lda PlayerSize ;check player's size 14465 beq BigKTS ;if big, use first tile 14466 lda Sprite_Tilenumber+24,y ;check tile number of seventh/eighth sprite 14467 cmp SwimTileRepOffset ;against tile number in player graphics table 14468 beq ExPGH ;if spr7/spr8 tile number = value, branch to leave 14469 inx ;otherwise increment X for second tile 14470 BigKTS: lda SwimKickTileNum,x ;overwrite tile number in sprite 7/8 14471 sta Sprite_Tilenumber+24,y ;to animate player's feet when swimming 14472 ExPGH: rts ;then leave 14473 14474 FindPlayerAction: 14475 jsr ProcessPlayerAction ;find proper offset to graphics table by player's actions 14476 jmp PlayerGfxProcessing ;draw player, then process for fireball throwing 14477 14478 DoChangeSize: 14479 jsr HandleChangeSize ;find proper offset to graphics table for grow/shrink 14480 jmp PlayerGfxProcessing ;draw player, then process for fireball throwing 14481 14482 PlayerKilled: 14483 ldy #$0e ;load offset for player killed 14484 lda PlayerGfxTblOffsets,y ;get offset to graphics table 14485 14486 PlayerGfxProcessing: 14487 sta PlayerGfxOffset ;store offset to graphics table here 14488 lda #$04 14489 jsr RenderPlayerSub ;draw player based on offset loaded 14490 jsr ChkForPlayerAttrib ;set horizontal flip bits as necessary 14491 lda FireballThrowingTimer 14492 beq PlayerOffscreenChk ;if fireball throw timer not set, skip to the end 14493 ldy #$00 ;set value to initialize by default 14494 lda PlayerAnimTimer ;get animation frame timer 14495 cmp FireballThrowingTimer ;compare to fireball throw timer 14496 sty FireballThrowingTimer ;initialize fireball throw timer 14497 bcs PlayerOffscreenChk ;if animation frame timer => fireball throw timer skip to end 14498 sta FireballThrowingTimer ;otherwise store animation timer into fireball throw timer 14499 ldy #$07 ;load offset for throwing 14500 lda PlayerGfxTblOffsets,y ;get offset to graphics table 14501 sta PlayerGfxOffset ;store it for use later 14502 ldy #$04 ;set to update four sprite rows by default 14503 lda Player_X_Speed 14504 ora Left_Right_Buttons ;check for horizontal speed or left/right button press 14505 beq SUpdR ;if no speed or button press, branch using set value in Y 14506 dey ;otherwise set to update only three sprite rows 14507 SUpdR: tya ;save in A for use 14508 jsr RenderPlayerSub ;in sub, draw player object again 14509 14510 PlayerOffscreenChk: 14511 lda Player_OffscreenBits ;get player's offscreen bits 14512 lsr 14513 lsr ;move vertical bits to low nybble 14514 lsr 14515 lsr 14516 sta $00 ;store here 14517 ldx #$03 ;check all four rows of player sprites 14518 lda Player_SprDataOffset ;get player's sprite data offset 14519 clc 14520 adc #$18 ;add 24 bytes to start at bottom row 14521 tay ;set as offset here 14522 PROfsLoop: lda #$f8 ;load offscreen Y coordinate just in case 14523 lsr $00 ;shift bit into carry 14524 bcc NPROffscr ;if bit not set, skip, do not move sprites 14525 jsr DumpTwoSpr ;otherwise dump offscreen Y coordinate into sprite data 14526 NPROffscr: tya 14527 sec ;subtract eight bytes to do 14528 sbc #$08 ;next row up 14529 tay 14530 dex ;decrement row counter 14531 bpl PROfsLoop ;do this until all sprite rows are checked 14532 rts ;then we are done! 14533 14534 ;------------------------------------------------------------------------------------- 14535 14536 IntermediatePlayerData: 14537 .db $58, $01, $00, $60, $ff, $04 14538 14539 DrawPlayer_Intermediate: 14540 ldx #$05 ;store data into zero page memory 14541 PIntLoop: lda IntermediatePlayerData,x ;load data to display player as he always 14542 sta $02,x ;appears on world/lives display 14543 dex 14544 bpl PIntLoop ;do this until all data is loaded 14545 ldx #$b8 ;load offset for small standing 14546 ldy #$04 ;load sprite data offset 14547 jsr DrawPlayerLoop ;draw player accordingly 14548 lda Sprite_Attributes+36 ;get empty sprite attributes 14549 ora #%01000000 ;set horizontal flip bit for bottom-right sprite 14550 sta Sprite_Attributes+32 ;store and leave 14551 rts 14552 14553 ;------------------------------------------------------------------------------------- 14554 ;$00-$01 - used to hold tile numbers, $00 also used to hold upper extent of animation frames 14555 ;$02 - vertical position 14556 ;$03 - facing direction, used as horizontal flip control 14557 ;$04 - attributes 14558 ;$05 - horizontal position 14559 ;$07 - number of rows to draw 14560 ;these also used in IntermediatePlayerData 14561 14562 RenderPlayerSub: 14563 sta $07 ;store number of rows of sprites to draw 14564 lda Player_Rel_XPos 14565 sta Player_Pos_ForScroll ;store player's relative horizontal position 14566 sta $05 ;store it here also 14567 lda Player_Rel_YPos 14568 sta $02 ;store player's vertical position 14569 lda PlayerFacingDir 14570 sta $03 ;store player's facing direction 14571 lda Player_SprAttrib 14572 sta $04 ;store player's sprite attributes 14573 ldx PlayerGfxOffset ;load graphics table offset 14574 ldy Player_SprDataOffset ;get player's sprite data offset 14575 14576 DrawPlayerLoop: 14577 lda PlayerGraphicsTable,x ;load player's left side 14578 sta $00 14579 lda PlayerGraphicsTable+1,x ;now load right side 14580 jsr DrawOneSpriteRow 14581 dec $07 ;decrement rows of sprites to draw 14582 bne DrawPlayerLoop ;do this until all rows are drawn 14583 rts 14584 14585 ProcessPlayerAction: 14586 lda Player_State ;get player's state 14587 cmp #$03 14588 beq ActionClimbing ;if climbing, branch here 14589 cmp #$02 14590 beq ActionFalling ;if falling, branch here 14591 cmp #$01 14592 bne ProcOnGroundActs ;if not jumping, branch here 14593 lda SwimmingFlag 14594 bne ActionSwimming ;if swimming flag set, branch elsewhere 14595 ldy #$06 ;load offset for crouching 14596 lda CrouchingFlag ;get crouching flag 14597 bne NonAnimatedActs ;if set, branch to get offset for graphics table 14598 ldy #$00 ;otherwise load offset for jumping 14599 jmp NonAnimatedActs ;go to get offset to graphics table 14600 14601 ProcOnGroundActs: 14602 ldy #$06 ;load offset for crouching 14603 lda CrouchingFlag ;get crouching flag 14604 bne NonAnimatedActs ;if set, branch to get offset for graphics table 14605 ldy #$02 ;load offset for standing 14606 lda Player_X_Speed ;check player's horizontal speed 14607 ora Left_Right_Buttons ;and left/right controller bits 14608 beq NonAnimatedActs ;if no speed or buttons pressed, use standing offset 14609 lda Player_XSpeedAbsolute ;load walking/running speed 14610 cmp #$09 14611 bcc ActionWalkRun ;if less than a certain amount, branch, too slow to skid 14612 lda Player_MovingDir ;otherwise check to see if moving direction 14613 and PlayerFacingDir ;and facing direction are the same 14614 bne ActionWalkRun ;if moving direction = facing direction, branch, don't skid 14615 iny ;otherwise increment to skid offset ($03) 14616 14617 NonAnimatedActs: 14618 jsr GetGfxOffsetAdder ;do a sub here to get offset adder for graphics table 14619 lda #$00 14620 sta PlayerAnimCtrl ;initialize animation frame control 14621 lda PlayerGfxTblOffsets,y ;load offset to graphics table using size as offset 14622 rts 14623 14624 ActionFalling: 14625 ldy #$04 ;load offset for walking/running 14626 jsr GetGfxOffsetAdder ;get offset to graphics table 14627 jmp GetCurrentAnimOffset ;execute instructions for falling state 14628 14629 ActionWalkRun: 14630 ldy #$04 ;load offset for walking/running 14631 jsr GetGfxOffsetAdder ;get offset to graphics table 14632 jmp FourFrameExtent ;execute instructions for normal state 14633 14634 ActionClimbing: 14635 ldy #$05 ;load offset for climbing 14636 lda Player_Y_Speed ;check player's vertical speed 14637 beq NonAnimatedActs ;if no speed, branch, use offset as-is 14638 jsr GetGfxOffsetAdder ;otherwise get offset for graphics table 14639 jmp ThreeFrameExtent ;then skip ahead to more code 14640 14641 ActionSwimming: 14642 ldy #$01 ;load offset for swimming 14643 jsr GetGfxOffsetAdder 14644 lda JumpSwimTimer ;check jump/swim timer 14645 ora PlayerAnimCtrl ;and animation frame control 14646 bne FourFrameExtent ;if any one of these set, branch ahead 14647 lda A_B_Buttons 14648 asl ;check for A button pressed 14649 bcs FourFrameExtent ;branch to same place if A button pressed 14650 14651 GetCurrentAnimOffset: 14652 lda PlayerAnimCtrl ;get animation frame control 14653 jmp GetOffsetFromAnimCtrl ;jump to get proper offset to graphics table 14654 14655 FourFrameExtent: 14656 lda #$03 ;load upper extent for frame control 14657 jmp AnimationControl ;jump to get offset and animate player object 14658 14659 ThreeFrameExtent: 14660 lda #$02 ;load upper extent for frame control for climbing 14661 14662 AnimationControl: 14663 sta $00 ;store upper extent here 14664 jsr GetCurrentAnimOffset ;get proper offset to graphics table 14665 pha ;save offset to stack 14666 lda PlayerAnimTimer ;load animation frame timer 14667 bne ExAnimC ;branch if not expired 14668 lda PlayerAnimTimerSet ;get animation frame timer amount 14669 sta PlayerAnimTimer ;and set timer accordingly 14670 lda PlayerAnimCtrl 14671 clc ;add one to animation frame control 14672 adc #$01 14673 cmp $00 ;compare to upper extent 14674 bcc SetAnimC ;if frame control + 1 < upper extent, use as next 14675 lda #$00 ;otherwise initialize frame control 14676 SetAnimC: sta PlayerAnimCtrl ;store as new animation frame control 14677 ExAnimC: pla ;get offset to graphics table from stack and leave 14678 rts 14679 14680 GetGfxOffsetAdder: 14681 lda PlayerSize ;get player's size 14682 beq SzOfs ;if player big, use current offset as-is 14683 tya ;for big player 14684 clc ;otherwise add eight bytes to offset 14685 adc #$08 ;for small player 14686 tay 14687 SzOfs: rts ;go back 14688 14689 ChangeSizeOffsetAdder: 14690 .db $00, $01, $00, $01, $00, $01, $02, $00, $01, $02 14691 .db $02, $00, $02, $00, $02, $00, $02, $00, $02, $00 14692 14693 HandleChangeSize: 14694 ldy PlayerAnimCtrl ;get animation frame control 14695 lda FrameCounter 14696 and #%00000011 ;get frame counter and execute this code every 14697 bne GorSLog ;fourth frame, otherwise branch ahead 14698 iny ;increment frame control 14699 cpy #$0a ;check for preset upper extent 14700 bcc CSzNext ;if not there yet, skip ahead to use 14701 ldy #$00 ;otherwise initialize both grow/shrink flag 14702 sty PlayerChangeSizeFlag ;and animation frame control 14703 CSzNext: sty PlayerAnimCtrl ;store proper frame control 14704 GorSLog: lda PlayerSize ;get player's size 14705 bne ShrinkPlayer ;if player small, skip ahead to next part 14706 lda ChangeSizeOffsetAdder,y ;get offset adder based on frame control as offset 14707 ldy #$0f ;load offset for player growing 14708 14709 GetOffsetFromAnimCtrl: 14710 asl ;multiply animation frame control 14711 asl ;by eight to get proper amount 14712 asl ;to add to our offset 14713 adc PlayerGfxTblOffsets,y ;add to offset to graphics table 14714 rts ;and return with result in A 14715 14716 ShrinkPlayer: 14717 tya ;add ten bytes to frame control as offset 14718 clc 14719 adc #$0a ;this thing apparently uses two of the swimming frames 14720 tax ;to draw the player shrinking 14721 ldy #$09 ;load offset for small player swimming 14722 lda ChangeSizeOffsetAdder,x ;get what would normally be offset adder 14723 bne ShrPlF ;and branch to use offset if nonzero 14724 ldy #$01 ;otherwise load offset for big player swimming 14725 ShrPlF: lda PlayerGfxTblOffsets,y ;get offset to graphics table based on offset loaded 14726 rts ;and leave 14727 14728 ChkForPlayerAttrib: 14729 ldy Player_SprDataOffset ;get sprite data offset 14730 lda GameEngineSubroutine 14731 cmp #$0b ;if executing specific game engine routine, 14732 beq KilledAtt ;branch to change third and fourth row OAM attributes 14733 lda PlayerGfxOffset ;get graphics table offset 14734 cmp #$50 14735 beq C_S_IGAtt ;if crouch offset, either standing offset, 14736 cmp #$b8 ;or intermediate growing offset, 14737 beq C_S_IGAtt ;go ahead and execute code to change 14738 cmp #$c0 ;fourth row OAM attributes only 14739 beq C_S_IGAtt 14740 cmp #$c8 14741 bne ExPlyrAt ;if none of these, branch to leave 14742 KilledAtt: lda Sprite_Attributes+16,y 14743 and #%00111111 ;mask out horizontal and vertical flip bits 14744 sta Sprite_Attributes+16,y ;for third row sprites and save 14745 lda Sprite_Attributes+20,y 14746 and #%00111111 14747 ora #%01000000 ;set horizontal flip bit for second 14748 sta Sprite_Attributes+20,y ;sprite in the third row 14749 C_S_IGAtt: lda Sprite_Attributes+24,y 14750 and #%00111111 ;mask out horizontal and vertical flip bits 14751 sta Sprite_Attributes+24,y ;for fourth row sprites and save 14752 lda Sprite_Attributes+28,y 14753 and #%00111111 14754 ora #%01000000 ;set horizontal flip bit for second 14755 sta Sprite_Attributes+28,y ;sprite in the fourth row 14756 ExPlyrAt: rts ;leave 14757 14758 ;------------------------------------------------------------------------------------- 14759 ;$00 - used in adding to get proper offset 14760 14761 RelativePlayerPosition: 14762 ldx #$00 ;set offsets for relative cooordinates 14763 ldy #$00 ;routine to correspond to player object 14764 jmp RelWOfs ;get the coordinates 14765 14766 RelativeBubblePosition: 14767 ldy #$01 ;set for air bubble offsets 14768 jsr GetProperObjOffset ;modify X to get proper air bubble offset 14769 ldy #$03 14770 jmp RelWOfs ;get the coordinates 14771 14772 RelativeFireballPosition: 14773 ldy #$00 ;set for fireball offsets 14774 jsr GetProperObjOffset ;modify X to get proper fireball offset 14775 ldy #$02 14776 RelWOfs: jsr GetObjRelativePosition ;get the coordinates 14777 ldx ObjectOffset ;return original offset 14778 rts ;leave 14779 14780 RelativeMiscPosition: 14781 ldy #$02 ;set for misc object offsets 14782 jsr GetProperObjOffset ;modify X to get proper misc object offset 14783 ldy #$06 14784 jmp RelWOfs ;get the coordinates 14785 14786 RelativeEnemyPosition: 14787 lda #$01 ;get coordinates of enemy object 14788 ldy #$01 ;relative to the screen 14789 jmp VariableObjOfsRelPos 14790 14791 RelativeBlockPosition: 14792 lda #$09 ;get coordinates of one block object 14793 ldy #$04 ;relative to the screen 14794 jsr VariableObjOfsRelPos 14795 inx ;adjust offset for other block object if any 14796 inx 14797 lda #$09 14798 iny ;adjust other and get coordinates for other one 14799 14800 VariableObjOfsRelPos: 14801 stx $00 ;store value to add to A here 14802 clc 14803 adc $00 ;add A to value stored 14804 tax ;use as enemy offset 14805 jsr GetObjRelativePosition 14806 ldx ObjectOffset ;reload old object offset and leave 14807 rts 14808 14809 GetObjRelativePosition: 14810 lda SprObject_Y_Position,x ;load vertical coordinate low 14811 sta SprObject_Rel_YPos,y ;store here 14812 lda SprObject_X_Position,x ;load horizontal coordinate 14813 sec ;subtract left edge coordinate 14814 sbc ScreenLeft_X_Pos 14815 sta SprObject_Rel_XPos,y ;store result here 14816 rts 14817 14818 ;------------------------------------------------------------------------------------- 14819 ;$00 - used as temp variable to hold offscreen bits 14820 14821 GetPlayerOffscreenBits: 14822 ldx #$00 ;set offsets for player-specific variables 14823 ldy #$00 ;and get offscreen information about player 14824 jmp GetOffScreenBitsSet 14825 14826 GetFireballOffscreenBits: 14827 ldy #$00 ;set for fireball offsets 14828 jsr GetProperObjOffset ;modify X to get proper fireball offset 14829 ldy #$02 ;set other offset for fireball's offscreen bits 14830 jmp GetOffScreenBitsSet ;and get offscreen information about fireball 14831 14832 GetBubbleOffscreenBits: 14833 ldy #$01 ;set for air bubble offsets 14834 jsr GetProperObjOffset ;modify X to get proper air bubble offset 14835 ldy #$03 ;set other offset for airbubble's offscreen bits 14836 jmp GetOffScreenBitsSet ;and get offscreen information about air bubble 14837 14838 GetMiscOffscreenBits: 14839 ldy #$02 ;set for misc object offsets 14840 jsr GetProperObjOffset ;modify X to get proper misc object offset 14841 ldy #$06 ;set other offset for misc object's offscreen bits 14842 jmp GetOffScreenBitsSet ;and get offscreen information about misc object 14843 14844 ObjOffsetData: 14845 .db $07, $16, $0d 14846 14847 GetProperObjOffset: 14848 txa ;move offset to A 14849 clc 14850 adc ObjOffsetData,y ;add amount of bytes to offset depending on setting in Y 14851 tax ;put back in X and leave 14852 rts 14853 14854 GetEnemyOffscreenBits: 14855 lda #$01 ;set A to add 1 byte in order to get enemy offset 14856 ldy #$01 ;set Y to put offscreen bits in Enemy_OffscreenBits 14857 jmp SetOffscrBitsOffset 14858 14859 GetBlockOffscreenBits: 14860 lda #$09 ;set A to add 9 bytes in order to get block obj offset 14861 ldy #$04 ;set Y to put offscreen bits in Block_OffscreenBits 14862 14863 SetOffscrBitsOffset: 14864 stx $00 14865 clc ;add contents of X to A to get 14866 adc $00 ;appropriate offset, then give back to X 14867 tax 14868 14869 GetOffScreenBitsSet: 14870 tya ;save offscreen bits offset to stack for now 14871 pha 14872 jsr RunOffscrBitsSubs 14873 asl ;move low nybble to high nybble 14874 asl 14875 asl 14876 asl 14877 ora $00 ;mask together with previously saved low nybble 14878 sta $00 ;store both here 14879 pla ;get offscreen bits offset from stack 14880 tay 14881 lda $00 ;get value here and store elsewhere 14882 sta SprObject_OffscrBits,y 14883 ldx ObjectOffset 14884 rts 14885 14886 RunOffscrBitsSubs: 14887 jsr GetXOffscreenBits ;do subroutine here 14888 lsr ;move high nybble to low 14889 lsr 14890 lsr 14891 lsr 14892 sta $00 ;store here 14893 jmp GetYOffscreenBits 14894 14895 ;-------------------------------- 14896 ;(these apply to these three subsections) 14897 ;$04 - used to store proper offset 14898 ;$05 - used as adder in DividePDiff 14899 ;$06 - used to store preset value used to compare to pixel difference in $07 14900 ;$07 - used to store difference between coordinates of object and screen edges 14901 14902 XOffscreenBitsData: 14903 .db $7f, $3f, $1f, $0f, $07, $03, $01, $00 14904 .db $80, $c0, $e0, $f0, $f8, $fc, $fe, $ff 14905 14906 DefaultXOnscreenOfs: 14907 .db $07, $0f, $07 14908 14909 GetXOffscreenBits: 14910 stx $04 ;save position in buffer to here 14911 ldy #$01 ;start with right side of screen 14912 XOfsLoop: lda ScreenEdge_X_Pos,y ;get pixel coordinate of edge 14913 sec ;get difference between pixel coordinate of edge 14914 sbc SprObject_X_Position,x ;and pixel coordinate of object position 14915 sta $07 ;store here 14916 lda ScreenEdge_PageLoc,y ;get page location of edge 14917 sbc SprObject_PageLoc,x ;subtract from page location of object position 14918 ldx DefaultXOnscreenOfs,y ;load offset value here 14919 cmp #$00 14920 bmi XLdBData ;if beyond right edge or in front of left edge, branch 14921 ldx DefaultXOnscreenOfs+1,y ;if not, load alternate offset value here 14922 cmp #$01 14923 bpl XLdBData ;if one page or more to the left of either edge, branch 14924 lda #$38 ;if no branching, load value here and store 14925 sta $06 14926 lda #$08 ;load some other value and execute subroutine 14927 jsr DividePDiff 14928 XLdBData: lda XOffscreenBitsData,x ;get bits here 14929 ldx $04 ;reobtain position in buffer 14930 cmp #$00 ;if bits not zero, branch to leave 14931 bne ExXOfsBS 14932 dey ;otherwise, do left side of screen now 14933 bpl XOfsLoop ;branch if not already done with left side 14934 ExXOfsBS: rts 14935 14936 ;-------------------------------- 14937 14938 YOffscreenBitsData: 14939 .db $00, $08, $0c, $0e 14940 .db $0f, $07, $03, $01 14941 .db $00 14942 14943 DefaultYOnscreenOfs: 14944 .db $04, $00, $04 14945 14946 HighPosUnitData: 14947 .db $ff, $00 14948 14949 GetYOffscreenBits: 14950 stx $04 ;save position in buffer to here 14951 ldy #$01 ;start with top of screen 14952 YOfsLoop: lda HighPosUnitData,y ;load coordinate for edge of vertical unit 14953 sec 14954 sbc SprObject_Y_Position,x ;subtract from vertical coordinate of object 14955 sta $07 ;store here 14956 lda #$01 ;subtract one from vertical high byte of object 14957 sbc SprObject_Y_HighPos,x 14958 ldx DefaultYOnscreenOfs,y ;load offset value here 14959 cmp #$00 14960 bmi YLdBData ;if under top of the screen or beyond bottom, branch 14961 ldx DefaultYOnscreenOfs+1,y ;if not, load alternate offset value here 14962 cmp #$01 14963 bpl YLdBData ;if one vertical unit or more above the screen, branch 14964 lda #$20 ;if no branching, load value here and store 14965 sta $06 14966 lda #$04 ;load some other value and execute subroutine 14967 jsr DividePDiff 14968 YLdBData: lda YOffscreenBitsData,x ;get offscreen data bits using offset 14969 ldx $04 ;reobtain position in buffer 14970 cmp #$00 14971 bne ExYOfsBS ;if bits not zero, branch to leave 14972 dey ;otherwise, do bottom of the screen now 14973 bpl YOfsLoop 14974 ExYOfsBS: rts 14975 14976 ;-------------------------------- 14977 14978 DividePDiff: 14979 sta $05 ;store current value in A here 14980 lda $07 ;get pixel difference 14981 cmp $06 ;compare to preset value 14982 bcs ExDivPD ;if pixel difference >= preset value, branch 14983 lsr ;divide by eight 14984 lsr 14985 lsr 14986 and #$07 ;mask out all but 3 LSB 14987 cpy #$01 ;right side of the screen or top? 14988 bcs SetOscrO ;if so, branch, use difference / 8 as offset 14989 adc $05 ;if not, add value to difference / 8 14990 SetOscrO: tax ;use as offset 14991 ExDivPD: rts ;leave 14992 14993 ;------------------------------------------------------------------------------------- 14994 ;$00-$01 - tile numbers 14995 ;$02 - Y coordinate 14996 ;$03 - flip control 14997 ;$04 - sprite attributes 14998 ;$05 - X coordinate 14999 15000 DrawSpriteObject: 15001 lda $03 ;get saved flip control bits 15002 lsr 15003 lsr ;move d1 into carry 15004 lda $00 15005 bcc NoHFlip ;if d1 not set, branch 15006 sta Sprite_Tilenumber+4,y ;store first tile into second sprite 15007 lda $01 ;and second into first sprite 15008 sta Sprite_Tilenumber,y 15009 lda #$40 ;activate horizontal flip OAM attribute 15010 bne SetHFAt ;and unconditionally branch 15011 NoHFlip: sta Sprite_Tilenumber,y ;store first tile into first sprite 15012 lda $01 ;and second into second sprite 15013 sta Sprite_Tilenumber+4,y 15014 lda #$00 ;clear bit for horizontal flip 15015 SetHFAt: ora $04 ;add other OAM attributes if necessary 15016 sta Sprite_Attributes,y ;store sprite attributes 15017 sta Sprite_Attributes+4,y 15018 lda $02 ;now the y coordinates 15019 sta Sprite_Y_Position,y ;note because they are 15020 sta Sprite_Y_Position+4,y ;side by side, they are the same 15021 lda $05 15022 sta Sprite_X_Position,y ;store x coordinate, then 15023 clc ;add 8 pixels and store another to 15024 adc #$08 ;put them side by side 15025 sta Sprite_X_Position+4,y 15026 lda $02 ;add eight pixels to the next y 15027 clc ;coordinate 15028 adc #$08 15029 sta $02 15030 tya ;add eight to the offset in Y to 15031 clc ;move to the next two sprites 15032 adc #$08 15033 tay 15034 inx ;increment offset to return it to the 15035 inx ;routine that called this subroutine 15036 rts 15037 15038 ;------------------------------------------------------------------------------------- 15039 15040 ;unused space 15041 .db $ff, $ff, $ff, $ff, $ff, $ff 15042 15043 ;------------------------------------------------------------------------------------- 15044 15045 SoundEngine: 15046 lda OperMode ;are we in title screen mode? 15047 bne SndOn 15048 sta SND_MASTERCTRL_REG ;if so, disable sound and leave 15049 rts 15050 SndOn: lda #$ff 15051 sta JOYPAD_PORT2 ;disable irqs and set frame counter mode??? 15052 lda #$0f 15053 sta SND_MASTERCTRL_REG ;enable first four channels 15054 lda PauseModeFlag ;is sound already in pause mode? 15055 bne InPause 15056 lda PauseSoundQueue ;if not, check pause sfx queue 15057 cmp #$01 15058 bne RunSoundSubroutines ;if queue is empty, skip pause mode routine 15059 InPause: lda PauseSoundBuffer ;check pause sfx buffer 15060 bne ContPau 15061 lda PauseSoundQueue ;check pause queue 15062 beq SkipSoundSubroutines 15063 sta PauseSoundBuffer ;if queue full, store in buffer and activate 15064 sta PauseModeFlag ;pause mode to interrupt game sounds 15065 lda #$00 ;disable sound and clear sfx buffers 15066 sta SND_MASTERCTRL_REG 15067 sta Square1SoundBuffer 15068 sta Square2SoundBuffer 15069 sta NoiseSoundBuffer 15070 lda #$0f 15071 sta SND_MASTERCTRL_REG ;enable sound again 15072 lda #$2a ;store length of sound in pause counter 15073 sta Squ1_SfxLenCounter 15074 PTone1F: lda #$44 ;play first tone 15075 bne PTRegC ;unconditional branch 15076 ContPau: lda Squ1_SfxLenCounter ;check pause length left 15077 cmp #$24 ;time to play second? 15078 beq PTone2F 15079 cmp #$1e ;time to play first again? 15080 beq PTone1F 15081 cmp #$18 ;time to play second again? 15082 bne DecPauC ;only load regs during times, otherwise skip 15083 PTone2F: lda #$64 ;store reg contents and play the pause sfx 15084 PTRegC: ldx #$84 15085 ldy #$7f 15086 jsr PlaySqu1Sfx 15087 DecPauC: dec Squ1_SfxLenCounter ;decrement pause sfx counter 15088 bne SkipSoundSubroutines 15089 lda #$00 ;disable sound if in pause mode and 15090 sta SND_MASTERCTRL_REG ;not currently playing the pause sfx 15091 lda PauseSoundBuffer ;if no longer playing pause sfx, check to see 15092 cmp #$02 ;if we need to be playing sound again 15093 bne SkipPIn 15094 lda #$00 ;clear pause mode to allow game sounds again 15095 sta PauseModeFlag 15096 SkipPIn: lda #$00 ;clear pause sfx buffer 15097 sta PauseSoundBuffer 15098 beq SkipSoundSubroutines 15099 15100 RunSoundSubroutines: 15101 jsr Square1SfxHandler ;play sfx on square channel 1 15102 jsr Square2SfxHandler ; '' '' '' square channel 2 15103 jsr NoiseSfxHandler ; '' '' '' noise channel 15104 jsr MusicHandler ;play music on all channels 15105 lda #$00 ;clear the music queues 15106 sta AreaMusicQueue 15107 sta EventMusicQueue 15108 15109 SkipSoundSubroutines: 15110 lda #$00 ;clear the sound effects queues 15111 sta Square1SoundQueue 15112 sta Square2SoundQueue 15113 sta NoiseSoundQueue 15114 sta PauseSoundQueue 15115 ldy DAC_Counter ;load some sort of counter 15116 lda AreaMusicBuffer 15117 and #%00000011 ;check for specific music 15118 beq NoIncDAC 15119 inc DAC_Counter ;increment and check counter 15120 cpy #$30 15121 bcc StrWave ;if not there yet, just store it 15122 NoIncDAC: tya 15123 beq StrWave ;if we are at zero, do not decrement 15124 dec DAC_Counter ;decrement counter 15125 StrWave: sty SND_DELTA_REG+1 ;store into DMC load register (??) 15126 rts ;we are done here 15127 15128 ;-------------------------------- 15129 15130 Dump_Squ1_Regs: 15131 sty SND_SQUARE1_REG+1 ;dump the contents of X and Y into square 1's control regs 15132 stx SND_SQUARE1_REG 15133 rts 15134 15135 PlaySqu1Sfx: 15136 jsr Dump_Squ1_Regs ;do sub to set ctrl regs for square 1, then set frequency regs 15137 15138 SetFreq_Squ1: 15139 ldx #$00 ;set frequency reg offset for square 1 sound channel 15140 15141 Dump_Freq_Regs: 15142 tay 15143 lda FreqRegLookupTbl+1,y ;use previous contents of A for sound reg offset 15144 beq NoTone ;if zero, then do not load 15145 sta SND_REGISTER+2,x ;first byte goes into LSB of frequency divider 15146 lda FreqRegLookupTbl,y ;second byte goes into 3 MSB plus extra bit for 15147 ora #%00001000 ;length counter 15148 sta SND_REGISTER+3,x 15149 NoTone: rts 15150 15151 Dump_Sq2_Regs: 15152 stx SND_SQUARE2_REG ;dump the contents of X and Y into square 2's control regs 15153 sty SND_SQUARE2_REG+1 15154 rts 15155 15156 PlaySqu2Sfx: 15157 jsr Dump_Sq2_Regs ;do sub to set ctrl regs for square 2, then set frequency regs 15158 15159 SetFreq_Squ2: 15160 ldx #$04 ;set frequency reg offset for square 2 sound channel 15161 bne Dump_Freq_Regs ;unconditional branch 15162 15163 SetFreq_Tri: 15164 ldx #$08 ;set frequency reg offset for triangle sound channel 15165 bne Dump_Freq_Regs ;unconditional branch 15166 15167 ;-------------------------------- 15168 15169 SwimStompEnvelopeData: 15170 .db $9f, $9b, $98, $96, $95, $94, $92, $90 15171 .db $90, $9a, $97, $95, $93, $92 15172 15173 PlayFlagpoleSlide: 15174 lda #$40 ;store length of flagpole sound 15175 sta Squ1_SfxLenCounter 15176 lda #$62 ;load part of reg contents for flagpole sound 15177 jsr SetFreq_Squ1 15178 ldx #$99 ;now load the rest 15179 bne FPS2nd 15180 15181 PlaySmallJump: 15182 lda #$26 ;branch here for small mario jumping sound 15183 bne JumpRegContents 15184 15185 PlayBigJump: 15186 lda #$18 ;branch here for big mario jumping sound 15187 15188 JumpRegContents: 15189 ldx #$82 ;note that small and big jump borrow each others' reg contents 15190 ldy #$a7 ;anyway, this loads the first part of mario's jumping sound 15191 jsr PlaySqu1Sfx 15192 lda #$28 ;store length of sfx for both jumping sounds 15193 sta Squ1_SfxLenCounter ;then continue on here 15194 15195 ContinueSndJump: 15196 lda Squ1_SfxLenCounter ;jumping sounds seem to be composed of three parts 15197 cmp #$25 ;check for time to play second part yet 15198 bne N2Prt 15199 ldx #$5f ;load second part 15200 ldy #$f6 15201 bne DmpJpFPS ;unconditional branch 15202 N2Prt: cmp #$20 ;check for third part 15203 bne DecJpFPS 15204 ldx #$48 ;load third part 15205 FPS2nd: ldy #$bc ;the flagpole slide sound shares part of third part 15206 DmpJpFPS: jsr Dump_Squ1_Regs 15207 bne DecJpFPS ;unconditional branch outta here 15208 15209 PlayFireballThrow: 15210 lda #$05 15211 ldy #$99 ;load reg contents for fireball throw sound 15212 bne Fthrow ;unconditional branch 15213 15214 PlayBump: 15215 lda #$0a ;load length of sfx and reg contents for bump sound 15216 ldy #$93 15217 Fthrow: ldx #$9e ;the fireball sound shares reg contents with the bump sound 15218 sta Squ1_SfxLenCounter 15219 lda #$0c ;load offset for bump sound 15220 jsr PlaySqu1Sfx 15221 15222 ContinueBumpThrow: 15223 lda Squ1_SfxLenCounter ;check for second part of bump sound 15224 cmp #$06 15225 bne DecJpFPS 15226 lda #$bb ;load second part directly 15227 sta SND_SQUARE1_REG+1 15228 DecJpFPS: bne BranchToDecLength1 ;unconditional branch 15229 15230 15231 Square1SfxHandler: 15232 ldy Square1SoundQueue ;check for sfx in queue 15233 beq CheckSfx1Buffer 15234 sty Square1SoundBuffer ;if found, put in buffer 15235 bmi PlaySmallJump ;small jump 15236 lsr Square1SoundQueue 15237 bcs PlayBigJump ;big jump 15238 lsr Square1SoundQueue 15239 bcs PlayBump ;bump 15240 lsr Square1SoundQueue 15241 bcs PlaySwimStomp ;swim/stomp 15242 lsr Square1SoundQueue 15243 bcs PlaySmackEnemy ;smack enemy 15244 lsr Square1SoundQueue 15245 bcs PlayPipeDownInj ;pipedown/injury 15246 lsr Square1SoundQueue 15247 bcs PlayFireballThrow ;fireball throw 15248 lsr Square1SoundQueue 15249 bcs PlayFlagpoleSlide ;slide flagpole 15250 15251 CheckSfx1Buffer: 15252 lda Square1SoundBuffer ;check for sfx in buffer 15253 beq ExS1H ;if not found, exit sub 15254 bmi ContinueSndJump ;small mario jump 15255 lsr 15256 bcs ContinueSndJump ;big mario jump 15257 lsr 15258 bcs ContinueBumpThrow ;bump 15259 lsr 15260 bcs ContinueSwimStomp ;swim/stomp 15261 lsr 15262 bcs ContinueSmackEnemy ;smack enemy 15263 lsr 15264 bcs ContinuePipeDownInj ;pipedown/injury 15265 lsr 15266 bcs ContinueBumpThrow ;fireball throw 15267 lsr 15268 bcs DecrementSfx1Length ;slide flagpole 15269 ExS1H: rts 15270 15271 PlaySwimStomp: 15272 lda #$0e ;store length of swim/stomp sound 15273 sta Squ1_SfxLenCounter 15274 ldy #$9c ;store reg contents for swim/stomp sound 15275 ldx #$9e 15276 lda #$26 15277 jsr PlaySqu1Sfx 15278 15279 ContinueSwimStomp: 15280 ldy Squ1_SfxLenCounter ;look up reg contents in data section based on 15281 lda SwimStompEnvelopeData-1,y ;length of sound left, used to control sound's 15282 sta SND_SQUARE1_REG ;envelope 15283 cpy #$06 15284 bne BranchToDecLength1 15285 lda #$9e ;when the length counts down to a certain point, put this 15286 sta SND_SQUARE1_REG+2 ;directly into the LSB of square 1's frequency divider 15287 15288 BranchToDecLength1: 15289 bne DecrementSfx1Length ;unconditional branch (regardless of how we got here) 15290 15291 PlaySmackEnemy: 15292 lda #$0e ;store length of smack enemy sound 15293 ldy #$cb 15294 ldx #$9f 15295 sta Squ1_SfxLenCounter 15296 lda #$28 ;store reg contents for smack enemy sound 15297 jsr PlaySqu1Sfx 15298 bne DecrementSfx1Length ;unconditional branch 15299 15300 ContinueSmackEnemy: 15301 ldy Squ1_SfxLenCounter ;check about halfway through 15302 cpy #$08 15303 bne SmSpc 15304 lda #$a0 ;if we're at the about-halfway point, make the second tone 15305 sta SND_SQUARE1_REG+2 ;in the smack enemy sound 15306 lda #$9f 15307 bne SmTick 15308 SmSpc: lda #$90 ;this creates spaces in the sound, giving it its distinct noise 15309 SmTick: sta SND_SQUARE1_REG 15310 15311 DecrementSfx1Length: 15312 dec Squ1_SfxLenCounter ;decrement length of sfx 15313 bne ExSfx1 15314 15315 StopSquare1Sfx: 15316 ldx #$00 ;if end of sfx reached, clear buffer 15317 stx $f1 ;and stop making the sfx 15318 ldx #$0e 15319 stx SND_MASTERCTRL_REG 15320 ldx #$0f 15321 stx SND_MASTERCTRL_REG 15322 ExSfx1: rts 15323 15324 PlayPipeDownInj: 15325 lda #$2f ;load length of pipedown sound 15326 sta Squ1_SfxLenCounter 15327 15328 ContinuePipeDownInj: 15329 lda Squ1_SfxLenCounter ;some bitwise logic, forces the regs 15330 lsr ;to be written to only during six specific times 15331 bcs NoPDwnL ;during which d3 must be set and d1-0 must be clear 15332 lsr 15333 bcs NoPDwnL 15334 and #%00000010 15335 beq NoPDwnL 15336 ldy #$91 ;and this is where it actually gets written in 15337 ldx #$9a 15338 lda #$44 15339 jsr PlaySqu1Sfx 15340 NoPDwnL: jmp DecrementSfx1Length 15341 15342 ;-------------------------------- 15343 15344 ExtraLifeFreqData: 15345 .db $58, $02, $54, $56, $4e, $44 15346 15347 PowerUpGrabFreqData: 15348 .db $4c, $52, $4c, $48, $3e, $36, $3e, $36, $30 15349 .db $28, $4a, $50, $4a, $64, $3c, $32, $3c, $32 15350 .db $2c, $24, $3a, $64, $3a, $34, $2c, $22, $2c 15351 15352 ;residual frequency data 15353 .db $22, $1c, $14 15354 15355 PUp_VGrow_FreqData: 15356 .db $14, $04, $22, $24, $16, $04, $24, $26 ;used by both 15357 .db $18, $04, $26, $28, $1a, $04, $28, $2a 15358 .db $1c, $04, $2a, $2c, $1e, $04, $2c, $2e ;used by vinegrow 15359 .db $20, $04, $2e, $30, $22, $04, $30, $32 15360 15361 PlayCoinGrab: 15362 lda #$35 ;load length of coin grab sound 15363 ldx #$8d ;and part of reg contents 15364 bne CGrab_TTickRegL 15365 15366 PlayTimerTick: 15367 lda #$06 ;load length of timer tick sound 15368 ldx #$98 ;and part of reg contents 15369 15370 CGrab_TTickRegL: 15371 sta Squ2_SfxLenCounter 15372 ldy #$7f ;load the rest of reg contents 15373 lda #$42 ;of coin grab and timer tick sound 15374 jsr PlaySqu2Sfx 15375 15376 ContinueCGrabTTick: 15377 lda Squ2_SfxLenCounter ;check for time to play second tone yet 15378 cmp #$30 ;timer tick sound also executes this, not sure why 15379 bne N2Tone 15380 lda #$54 ;if so, load the tone directly into the reg 15381 sta SND_SQUARE2_REG+2 15382 N2Tone: bne DecrementSfx2Length 15383 15384 PlayBlast: 15385 lda #$20 ;load length of fireworks/gunfire sound 15386 sta Squ2_SfxLenCounter 15387 ldy #$94 ;load reg contents of fireworks/gunfire sound 15388 lda #$5e 15389 bne SBlasJ 15390 15391 ContinueBlast: 15392 lda Squ2_SfxLenCounter ;check for time to play second part 15393 cmp #$18 15394 bne DecrementSfx2Length 15395 ldy #$93 ;load second part reg contents then 15396 lda #$18 15397 SBlasJ: bne BlstSJp ;unconditional branch to load rest of reg contents 15398 15399 PlayPowerUpGrab: 15400 lda #$36 ;load length of power-up grab sound 15401 sta Squ2_SfxLenCounter 15402 15403 ContinuePowerUpGrab: 15404 lda Squ2_SfxLenCounter ;load frequency reg based on length left over 15405 lsr ;divide by 2 15406 bcs DecrementSfx2Length ;alter frequency every other frame 15407 tay 15408 lda PowerUpGrabFreqData-1,y ;use length left over / 2 for frequency offset 15409 ldx #$5d ;store reg contents of power-up grab sound 15410 ldy #$7f 15411 15412 LoadSqu2Regs: 15413 jsr PlaySqu2Sfx 15414 15415 DecrementSfx2Length: 15416 dec Squ2_SfxLenCounter ;decrement length of sfx 15417 bne ExSfx2 15418 15419 EmptySfx2Buffer: 15420 ldx #$00 ;initialize square 2's sound effects buffer 15421 stx Square2SoundBuffer 15422 15423 StopSquare2Sfx: 15424 ldx #$0d ;stop playing the sfx 15425 stx SND_MASTERCTRL_REG 15426 ldx #$0f 15427 stx SND_MASTERCTRL_REG 15428 ExSfx2: rts 15429 15430 Square2SfxHandler: 15431 lda Square2SoundBuffer ;special handling for the 1-up sound to keep it 15432 and #Sfx_ExtraLife ;from being interrupted by other sounds on square 2 15433 bne ContinueExtraLife 15434 ldy Square2SoundQueue ;check for sfx in queue 15435 beq CheckSfx2Buffer 15436 sty Square2SoundBuffer ;if found, put in buffer and check for the following 15437 bmi PlayBowserFall ;bowser fall 15438 lsr Square2SoundQueue 15439 bcs PlayCoinGrab ;coin grab 15440 lsr Square2SoundQueue 15441 bcs PlayGrowPowerUp ;power-up reveal 15442 lsr Square2SoundQueue 15443 bcs PlayGrowVine ;vine grow 15444 lsr Square2SoundQueue 15445 bcs PlayBlast ;fireworks/gunfire 15446 lsr Square2SoundQueue 15447 bcs PlayTimerTick ;timer tick 15448 lsr Square2SoundQueue 15449 bcs PlayPowerUpGrab ;power-up grab 15450 lsr Square2SoundQueue 15451 bcs PlayExtraLife ;1-up 15452 15453 CheckSfx2Buffer: 15454 lda Square2SoundBuffer ;check for sfx in buffer 15455 beq ExS2H ;if not found, exit sub 15456 bmi ContinueBowserFall ;bowser fall 15457 lsr 15458 bcs Cont_CGrab_TTick ;coin grab 15459 lsr 15460 bcs ContinueGrowItems ;power-up reveal 15461 lsr 15462 bcs ContinueGrowItems ;vine grow 15463 lsr 15464 bcs ContinueBlast ;fireworks/gunfire 15465 lsr 15466 bcs Cont_CGrab_TTick ;timer tick 15467 lsr 15468 bcs ContinuePowerUpGrab ;power-up grab 15469 lsr 15470 bcs ContinueExtraLife ;1-up 15471 ExS2H: rts 15472 15473 Cont_CGrab_TTick: 15474 jmp ContinueCGrabTTick 15475 15476 JumpToDecLength2: 15477 jmp DecrementSfx2Length 15478 15479 PlayBowserFall: 15480 lda #$38 ;load length of bowser defeat sound 15481 sta Squ2_SfxLenCounter 15482 ldy #$c4 ;load contents of reg for bowser defeat sound 15483 lda #$18 15484 BlstSJp: bne PBFRegs 15485 15486 ContinueBowserFall: 15487 lda Squ2_SfxLenCounter ;check for almost near the end 15488 cmp #$08 15489 bne DecrementSfx2Length 15490 ldy #$a4 ;if so, load the rest of reg contents for bowser defeat sound 15491 lda #$5a 15492 PBFRegs: ldx #$9f ;the fireworks/gunfire sound shares part of reg contents here 15493 EL_LRegs: bne LoadSqu2Regs ;this is an unconditional branch outta here 15494 15495 PlayExtraLife: 15496 lda #$30 ;load length of 1-up sound 15497 sta Squ2_SfxLenCounter 15498 15499 ContinueExtraLife: 15500 lda Squ2_SfxLenCounter 15501 ldx #$03 ;load new tones only every eight frames 15502 DivLLoop: lsr 15503 bcs JumpToDecLength2 ;if any bits set here, branch to dec the length 15504 dex 15505 bne DivLLoop ;do this until all bits checked, if none set, continue 15506 tay 15507 lda ExtraLifeFreqData-1,y ;load our reg contents 15508 ldx #$82 15509 ldy #$7f 15510 bne EL_LRegs ;unconditional branch 15511 15512 PlayGrowPowerUp: 15513 lda #$10 ;load length of power-up reveal sound 15514 bne GrowItemRegs 15515 15516 PlayGrowVine: 15517 lda #$20 ;load length of vine grow sound 15518 15519 GrowItemRegs: 15520 sta Squ2_SfxLenCounter 15521 lda #$7f ;load contents of reg for both sounds directly 15522 sta SND_SQUARE2_REG+1 15523 lda #$00 ;start secondary counter for both sounds 15524 sta Sfx_SecondaryCounter 15525 15526 ContinueGrowItems: 15527 inc Sfx_SecondaryCounter ;increment secondary counter for both sounds 15528 lda Sfx_SecondaryCounter ;this sound doesn't decrement the usual counter 15529 lsr ;divide by 2 to get the offset 15530 tay 15531 cpy Squ2_SfxLenCounter ;have we reached the end yet? 15532 beq StopGrowItems ;if so, branch to jump, and stop playing sounds 15533 lda #$9d ;load contents of other reg directly 15534 sta SND_SQUARE2_REG 15535 lda PUp_VGrow_FreqData,y ;use secondary counter / 2 as offset for frequency regs 15536 jsr SetFreq_Squ2 15537 rts 15538 15539 StopGrowItems: 15540 jmp EmptySfx2Buffer ;branch to stop playing sounds 15541 15542 ;-------------------------------- 15543 15544 BrickShatterFreqData: 15545 .db $01, $0e, $0e, $0d, $0b, $06, $0c, $0f 15546 .db $0a, $09, $03, $0d, $08, $0d, $06, $0c 15547 15548 PlayBrickShatter: 15549 lda #$20 ;load length of brick shatter sound 15550 sta Noise_SfxLenCounter 15551 15552 ContinueBrickShatter: 15553 lda Noise_SfxLenCounter 15554 lsr ;divide by 2 and check for bit set to use offset 15555 bcc DecrementSfx3Length 15556 tay 15557 ldx BrickShatterFreqData,y ;load reg contents of brick shatter sound 15558 lda BrickShatterEnvData,y 15559 15560 PlayNoiseSfx: 15561 sta SND_NOISE_REG ;play the sfx 15562 stx SND_NOISE_REG+2 15563 lda #$18 15564 sta SND_NOISE_REG+3 15565 15566 DecrementSfx3Length: 15567 dec Noise_SfxLenCounter ;decrement length of sfx 15568 bne ExSfx3 15569 lda #$f0 ;if done, stop playing the sfx 15570 sta SND_NOISE_REG 15571 lda #$00 15572 sta NoiseSoundBuffer 15573 ExSfx3: rts 15574 15575 NoiseSfxHandler: 15576 ldy NoiseSoundQueue ;check for sfx in queue 15577 beq CheckNoiseBuffer 15578 sty NoiseSoundBuffer ;if found, put in buffer 15579 lsr NoiseSoundQueue 15580 bcs PlayBrickShatter ;brick shatter 15581 lsr NoiseSoundQueue 15582 bcs PlayBowserFlame ;bowser flame 15583 15584 CheckNoiseBuffer: 15585 lda NoiseSoundBuffer ;check for sfx in buffer 15586 beq ExNH ;if not found, exit sub 15587 lsr 15588 bcs ContinueBrickShatter ;brick shatter 15589 lsr 15590 bcs ContinueBowserFlame ;bowser flame 15591 ExNH: rts 15592 15593 PlayBowserFlame: 15594 lda #$40 ;load length of bowser flame sound 15595 sta Noise_SfxLenCounter 15596 15597 ContinueBowserFlame: 15598 lda Noise_SfxLenCounter 15599 lsr 15600 tay 15601 ldx #$0f ;load reg contents of bowser flame sound 15602 lda BowserFlameEnvData-1,y 15603 bne PlayNoiseSfx ;unconditional branch here 15604 15605 ;-------------------------------- 15606 15607 ContinueMusic: 15608 jmp HandleSquare2Music ;if we have music, start with square 2 channel 15609 15610 MusicHandler: 15611 lda EventMusicQueue ;check event music queue 15612 bne LoadEventMusic 15613 lda AreaMusicQueue ;check area music queue 15614 bne LoadAreaMusic 15615 lda EventMusicBuffer ;check both buffers 15616 ora AreaMusicBuffer 15617 bne ContinueMusic 15618 rts ;no music, then leave 15619 15620 LoadEventMusic: 15621 sta EventMusicBuffer ;copy event music queue contents to buffer 15622 cmp #DeathMusic ;is it death music? 15623 bne NoStopSfx ;if not, jump elsewhere 15624 jsr StopSquare1Sfx ;stop sfx in square 1 and 2 15625 jsr StopSquare2Sfx ;but clear only square 1's sfx buffer 15626 NoStopSfx: ldx AreaMusicBuffer 15627 stx AreaMusicBuffer_Alt ;save current area music buffer to be re-obtained later 15628 ldy #$00 15629 sty NoteLengthTblAdder ;default value for additional length byte offset 15630 sty AreaMusicBuffer ;clear area music buffer 15631 cmp #TimeRunningOutMusic ;is it time running out music? 15632 bne FindEventMusicHeader 15633 ldx #$08 ;load offset to be added to length byte of header 15634 stx NoteLengthTblAdder 15635 bne FindEventMusicHeader ;unconditional branch 15636 15637 LoadAreaMusic: 15638 cmp #$04 ;is it underground music? 15639 bne NoStop1 ;no, do not stop square 1 sfx 15640 jsr StopSquare1Sfx 15641 NoStop1: ldy #$10 ;start counter used only by ground level music 15642 GMLoopB: sty GroundMusicHeaderOfs 15643 15644 HandleAreaMusicLoopB: 15645 ldy #$00 ;clear event music buffer 15646 sty EventMusicBuffer 15647 sta AreaMusicBuffer ;copy area music queue contents to buffer 15648 cmp #$01 ;is it ground level music? 15649 bne FindAreaMusicHeader 15650 inc GroundMusicHeaderOfs ;increment but only if playing ground level music 15651 ldy GroundMusicHeaderOfs ;is it time to loopback ground level music? 15652 cpy #$32 15653 bne LoadHeader ;branch ahead with alternate offset 15654 ldy #$11 15655 bne GMLoopB ;unconditional branch 15656 15657 FindAreaMusicHeader: 15658 ldy #$08 ;load Y for offset of area music 15659 sty MusicOffset_Square2 ;residual instruction here 15660 15661 FindEventMusicHeader: 15662 iny ;increment Y pointer based on previously loaded queue contents 15663 lsr ;bit shift and increment until we find a set bit for music 15664 bcc FindEventMusicHeader 15665 15666 LoadHeader: 15667 lda MusicHeaderOffsetData,y ;load offset for header 15668 tay 15669 lda MusicHeaderData,y ;now load the header 15670 sta NoteLenLookupTblOfs 15671 lda MusicHeaderData+1,y 15672 sta MusicDataLow 15673 lda MusicHeaderData+2,y 15674 sta MusicDataHigh 15675 lda MusicHeaderData+3,y 15676 sta MusicOffset_Triangle 15677 lda MusicHeaderData+4,y 15678 sta MusicOffset_Square1 15679 lda MusicHeaderData+5,y 15680 sta MusicOffset_Noise 15681 sta NoiseDataLoopbackOfs 15682 lda #$01 ;initialize music note counters 15683 sta Squ2_NoteLenCounter 15684 sta Squ1_NoteLenCounter 15685 sta Tri_NoteLenCounter 15686 sta Noise_BeatLenCounter 15687 lda #$00 ;initialize music data offset for square 2 15688 sta MusicOffset_Square2 15689 sta AltRegContentFlag ;initialize alternate control reg data used by square 1 15690 lda #$0b ;disable triangle channel and reenable it 15691 sta SND_MASTERCTRL_REG 15692 lda #$0f 15693 sta SND_MASTERCTRL_REG 15694 15695 HandleSquare2Music: 15696 dec Squ2_NoteLenCounter ;decrement square 2 note length 15697 bne MiscSqu2MusicTasks ;is it time for more data? if not, branch to end tasks 15698 ldy MusicOffset_Square2 ;increment square 2 music offset and fetch data 15699 inc MusicOffset_Square2 15700 lda (MusicData),y 15701 beq EndOfMusicData ;if zero, the data is a null terminator 15702 bpl Squ2NoteHandler ;if non-negative, data is a note 15703 bne Squ2LengthHandler ;otherwise it is length data 15704 15705 EndOfMusicData: 15706 lda EventMusicBuffer ;check secondary buffer for time running out music 15707 cmp #TimeRunningOutMusic 15708 bne NotTRO 15709 lda AreaMusicBuffer_Alt ;load previously saved contents of primary buffer 15710 bne MusicLoopBack ;and start playing the song again if there is one 15711 NotTRO: and #VictoryMusic ;check for victory music (the only secondary that loops) 15712 bne VictoryMLoopBack 15713 lda AreaMusicBuffer ;check primary buffer for any music except pipe intro 15714 and #%01011111 15715 bne MusicLoopBack ;if any area music except pipe intro, music loops 15716 lda #$00 ;clear primary and secondary buffers and initialize 15717 sta AreaMusicBuffer ;control regs of square and triangle channels 15718 sta EventMusicBuffer 15719 sta SND_TRIANGLE_REG 15720 lda #$90 15721 sta SND_SQUARE1_REG 15722 sta SND_SQUARE2_REG 15723 rts 15724 15725 MusicLoopBack: 15726 jmp HandleAreaMusicLoopB 15727 15728 VictoryMLoopBack: 15729 jmp LoadEventMusic 15730 15731 Squ2LengthHandler: 15732 jsr ProcessLengthData ;store length of note 15733 sta Squ2_NoteLenBuffer 15734 ldy MusicOffset_Square2 ;fetch another byte (MUST NOT BE LENGTH BYTE!) 15735 inc MusicOffset_Square2 15736 lda (MusicData),y 15737 15738 Squ2NoteHandler: 15739 ldx Square2SoundBuffer ;is there a sound playing on this channel? 15740 bne SkipFqL1 15741 jsr SetFreq_Squ2 ;no, then play the note 15742 beq Rest ;check to see if note is rest 15743 jsr LoadControlRegs ;if not, load control regs for square 2 15744 Rest: sta Squ2_EnvelopeDataCtrl ;save contents of A 15745 jsr Dump_Sq2_Regs ;dump X and Y into square 2 control regs 15746 SkipFqL1: lda Squ2_NoteLenBuffer ;save length in square 2 note counter 15747 sta Squ2_NoteLenCounter 15748 15749 MiscSqu2MusicTasks: 15750 lda Square2SoundBuffer ;is there a sound playing on square 2? 15751 bne HandleSquare1Music 15752 lda EventMusicBuffer ;check for death music or d4 set on secondary buffer 15753 and #%10010001 ;note that regs for death music or d4 are loaded by default 15754 bne HandleSquare1Music 15755 ldy Squ2_EnvelopeDataCtrl ;check for contents saved from LoadControlRegs 15756 beq NoDecEnv1 15757 dec Squ2_EnvelopeDataCtrl ;decrement unless already zero 15758 NoDecEnv1: jsr LoadEnvelopeData ;do a load of envelope data to replace default 15759 sta SND_SQUARE2_REG ;based on offset set by first load unless playing 15760 ldx #$7f ;death music or d4 set on secondary buffer 15761 stx SND_SQUARE2_REG+1 15762 15763 HandleSquare1Music: 15764 ldy MusicOffset_Square1 ;is there a nonzero offset here? 15765 beq HandleTriangleMusic ;if not, skip ahead to the triangle channel 15766 dec Squ1_NoteLenCounter ;decrement square 1 note length 15767 bne MiscSqu1MusicTasks ;is it time for more data? 15768 15769 FetchSqu1MusicData: 15770 ldy MusicOffset_Square1 ;increment square 1 music offset and fetch data 15771 inc MusicOffset_Square1 15772 lda (MusicData),y 15773 bne Squ1NoteHandler ;if nonzero, then skip this part 15774 lda #$83 15775 sta SND_SQUARE1_REG ;store some data into control regs for square 1 15776 lda #$94 ;and fetch another byte of data, used to give 15777 sta SND_SQUARE1_REG+1 ;death music its unique sound 15778 sta AltRegContentFlag 15779 bne FetchSqu1MusicData ;unconditional branch 15780 15781 Squ1NoteHandler: 15782 jsr AlternateLengthHandler 15783 sta Squ1_NoteLenCounter ;save contents of A in square 1 note counter 15784 ldy Square1SoundBuffer ;is there a sound playing on square 1? 15785 bne HandleTriangleMusic 15786 txa 15787 and #%00111110 ;change saved data to appropriate note format 15788 jsr SetFreq_Squ1 ;play the note 15789 beq SkipCtrlL 15790 jsr LoadControlRegs 15791 SkipCtrlL: sta Squ1_EnvelopeDataCtrl ;save envelope offset 15792 jsr Dump_Squ1_Regs 15793 15794 MiscSqu1MusicTasks: 15795 lda Square1SoundBuffer ;is there a sound playing on square 1? 15796 bne HandleTriangleMusic 15797 lda EventMusicBuffer ;check for death music or d4 set on secondary buffer 15798 and #%10010001 15799 bne DeathMAltReg 15800 ldy Squ1_EnvelopeDataCtrl ;check saved envelope offset 15801 beq NoDecEnv2 15802 dec Squ1_EnvelopeDataCtrl ;decrement unless already zero 15803 NoDecEnv2: jsr LoadEnvelopeData ;do a load of envelope data 15804 sta SND_SQUARE1_REG ;based on offset set by first load 15805 DeathMAltReg: lda AltRegContentFlag ;check for alternate control reg data 15806 bne DoAltLoad 15807 lda #$7f ;load this value if zero, the alternate value 15808 DoAltLoad: sta SND_SQUARE1_REG+1 ;if nonzero, and let's move on 15809 15810 HandleTriangleMusic: 15811 lda MusicOffset_Triangle 15812 dec Tri_NoteLenCounter ;decrement triangle note length 15813 bne HandleNoiseMusic ;is it time for more data? 15814 ldy MusicOffset_Triangle ;increment square 1 music offset and fetch data 15815 inc MusicOffset_Triangle 15816 lda (MusicData),y 15817 beq LoadTriCtrlReg ;if zero, skip all this and move on to noise 15818 bpl TriNoteHandler ;if non-negative, data is note 15819 jsr ProcessLengthData ;otherwise, it is length data 15820 sta Tri_NoteLenBuffer ;save contents of A 15821 lda #$1f 15822 sta SND_TRIANGLE_REG ;load some default data for triangle control reg 15823 ldy MusicOffset_Triangle ;fetch another byte 15824 inc MusicOffset_Triangle 15825 lda (MusicData),y 15826 beq LoadTriCtrlReg ;check once more for nonzero data 15827 15828 TriNoteHandler: 15829 jsr SetFreq_Tri 15830 ldx Tri_NoteLenBuffer ;save length in triangle note counter 15831 stx Tri_NoteLenCounter 15832 lda EventMusicBuffer 15833 and #%01101110 ;check for death music or d4 set on secondary buffer 15834 bne NotDOrD4 ;if playing any other secondary, skip primary buffer check 15835 lda AreaMusicBuffer ;check primary buffer for water or castle level music 15836 and #%00001010 15837 beq HandleNoiseMusic ;if playing any other primary, or death or d4, go on to noise routine 15838 NotDOrD4: txa ;if playing water or castle music or any secondary 15839 cmp #$12 ;besides death music or d4 set, check length of note 15840 bcs LongN 15841 lda EventMusicBuffer ;check for win castle music again if not playing a long note 15842 and #EndOfCastleMusic 15843 beq MediN 15844 lda #$0f ;load value $0f if playing the win castle music and playing a short 15845 bne LoadTriCtrlReg ;note, load value $1f if playing water or castle level music or any 15846 MediN: lda #$1f ;secondary besides death and d4 except win castle or win castle and playing 15847 bne LoadTriCtrlReg ;a short note, and load value $ff if playing a long note on water, castle 15848 LongN: lda #$ff ;or any secondary (including win castle) except death and d4 15849 15850 LoadTriCtrlReg: 15851 sta SND_TRIANGLE_REG ;save final contents of A into control reg for triangle 15852 15853 HandleNoiseMusic: 15854 lda AreaMusicBuffer ;check if playing underground or castle music 15855 and #%11110011 15856 beq ExitMusicHandler ;if so, skip the noise routine 15857 dec Noise_BeatLenCounter ;decrement noise beat length 15858 bne ExitMusicHandler ;is it time for more data? 15859 15860 FetchNoiseBeatData: 15861 ldy MusicOffset_Noise ;increment noise beat offset and fetch data 15862 inc MusicOffset_Noise 15863 lda (MusicData),y ;get noise beat data, if nonzero, branch to handle 15864 bne NoiseBeatHandler 15865 lda NoiseDataLoopbackOfs ;if data is zero, reload original noise beat offset 15866 sta MusicOffset_Noise ;and loopback next time around 15867 bne FetchNoiseBeatData ;unconditional branch 15868 15869 NoiseBeatHandler: 15870 jsr AlternateLengthHandler 15871 sta Noise_BeatLenCounter ;store length in noise beat counter 15872 txa 15873 and #%00111110 ;reload data and erase length bits 15874 beq SilentBeat ;if no beat data, silence 15875 cmp #$30 ;check the beat data and play the appropriate 15876 beq LongBeat ;noise accordingly 15877 cmp #$20 15878 beq StrongBeat 15879 and #%00010000 15880 beq SilentBeat 15881 lda #$1c ;short beat data 15882 ldx #$03 15883 ldy #$18 15884 bne PlayBeat 15885 15886 StrongBeat: 15887 lda #$1c ;strong beat data 15888 ldx #$0c 15889 ldy #$18 15890 bne PlayBeat 15891 15892 LongBeat: 15893 lda #$1c ;long beat data 15894 ldx #$03 15895 ldy #$58 15896 bne PlayBeat 15897 15898 SilentBeat: 15899 lda #$10 ;silence 15900 15901 PlayBeat: 15902 sta SND_NOISE_REG ;load beat data into noise regs 15903 stx SND_NOISE_REG+2 15904 sty SND_NOISE_REG+3 15905 15906 ExitMusicHandler: 15907 rts 15908 15909 AlternateLengthHandler: 15910 tax ;save a copy of original byte into X 15911 ror ;save LSB from original byte into carry 15912 txa ;reload original byte and rotate three times 15913 rol ;turning xx00000x into 00000xxx, with the 15914 rol ;bit in carry as the MSB here 15915 rol 15916 15917 ProcessLengthData: 15918 and #%00000111 ;clear all but the three LSBs 15919 clc 15920 adc $f0 ;add offset loaded from first header byte 15921 adc NoteLengthTblAdder ;add extra if time running out music 15922 tay 15923 lda MusicLengthLookupTbl,y ;load length 15924 rts 15925 15926 LoadControlRegs: 15927 lda EventMusicBuffer ;check secondary buffer for win castle music 15928 and #EndOfCastleMusic 15929 beq NotECstlM 15930 lda #$04 ;this value is only used for win castle music 15931 bne AllMus ;unconditional branch 15932 NotECstlM: lda AreaMusicBuffer 15933 and #%01111101 ;check primary buffer for water music 15934 beq WaterMus 15935 lda #$08 ;this is the default value for all other music 15936 bne AllMus 15937 WaterMus: lda #$28 ;this value is used for water music and all other event music 15938 AllMus: ldx #$82 ;load contents of other sound regs for square 2 15939 ldy #$7f 15940 rts 15941 15942 LoadEnvelopeData: 15943 lda EventMusicBuffer ;check secondary buffer for win castle music 15944 and #EndOfCastleMusic 15945 beq LoadUsualEnvData 15946 lda EndOfCastleMusicEnvData,y ;load data from offset for win castle music 15947 rts 15948 15949 LoadUsualEnvData: 15950 lda AreaMusicBuffer ;check primary buffer for water music 15951 and #%01111101 15952 beq LoadWaterEventMusEnvData 15953 lda AreaMusicEnvData,y ;load default data from offset for all other music 15954 rts 15955 15956 LoadWaterEventMusEnvData: 15957 lda WaterEventMusEnvData,y ;load data from offset for water music and all other event music 15958 rts 15959 15960 ;-------------------------------- 15961 15962 ;music header offsets 15963 15964 MusicHeaderData: 15965 .db DeathMusHdr-MHD ;event music 15966 .db GameOverMusHdr-MHD 15967 .db VictoryMusHdr-MHD 15968 .db WinCastleMusHdr-MHD 15969 .db GameOverMusHdr-MHD 15970 .db EndOfLevelMusHdr-MHD 15971 .db TimeRunningOutHdr-MHD 15972 .db SilenceHdr-MHD 15973 15974 .db GroundLevelPart1Hdr-MHD ;area music 15975 .db WaterMusHdr-MHD 15976 .db UndergroundMusHdr-MHD 15977 .db CastleMusHdr-MHD 15978 .db Star_CloudHdr-MHD 15979 .db GroundLevelLeadInHdr-MHD 15980 .db Star_CloudHdr-MHD 15981 .db SilenceHdr-MHD 15982 15983 .db GroundLevelLeadInHdr-MHD ;ground level music layout 15984 .db GroundLevelPart1Hdr-MHD, GroundLevelPart1Hdr-MHD 15985 .db GroundLevelPart2AHdr-MHD, GroundLevelPart2BHdr-MHD, GroundLevelPart2AHdr-MHD, GroundLevelPart2CHdr-MHD 15986 .db GroundLevelPart2AHdr-MHD, GroundLevelPart2BHdr-MHD, GroundLevelPart2AHdr-MHD, GroundLevelPart2CHdr-MHD 15987 .db GroundLevelPart3AHdr-MHD, GroundLevelPart3BHdr-MHD, GroundLevelPart3AHdr-MHD, GroundLevelLeadInHdr-MHD 15988 .db GroundLevelPart1Hdr-MHD, GroundLevelPart1Hdr-MHD 15989 .db GroundLevelPart4AHdr-MHD, GroundLevelPart4BHdr-MHD, GroundLevelPart4AHdr-MHD, GroundLevelPart4CHdr-MHD 15990 .db GroundLevelPart4AHdr-MHD, GroundLevelPart4BHdr-MHD, GroundLevelPart4AHdr-MHD, GroundLevelPart4CHdr-MHD 15991 .db GroundLevelPart3AHdr-MHD, GroundLevelPart3BHdr-MHD, GroundLevelPart3AHdr-MHD, GroundLevelLeadInHdr-MHD 15992 .db GroundLevelPart4AHdr-MHD, GroundLevelPart4BHdr-MHD, GroundLevelPart4AHdr-MHD, GroundLevelPart4CHdr-MHD 15993 15994 ;music headers 15995 ;header format is as follows: 15996 ;1 byte - length byte offset 15997 ;2 bytes - music data address 15998 ;1 byte - triangle data offset 15999 ;1 byte - square 1 data offset 16000 ;1 byte - noise data offset (not used by secondary music) 16001 16002 TimeRunningOutHdr: .db $08, <TimeRunOutMusData, >TimeRunOutMusData, $27, $18 16003 Star_CloudHdr: .db $20, <Star_CloudMData, >Star_CloudMData, $2e, $1a, $40 16004 EndOfLevelMusHdr: .db $20, <WinLevelMusData, >WinLevelMusData, $3d, $21 16005 ResidualHeaderData: .db $20, $c4, $fc, $3f, $1d 16006 UndergroundMusHdr: .db $18, <UndergroundMusData, >UndergroundMusData, $00, $00 16007 SilenceHdr: .db $08, <SilenceData, >SilenceData, $00 16008 CastleMusHdr: .db $00, <CastleMusData, >CastleMusData, $93, $62 16009 VictoryMusHdr: .db $10, <VictoryMusData, >VictoryMusData, $24, $14 16010 GameOverMusHdr: .db $18, <GameOverMusData, >GameOverMusData, $1e, $14 16011 WaterMusHdr: .db $08, <WaterMusData, >WaterMusData, $a0, $70, $68 16012 WinCastleMusHdr: .db $08, <EndOfCastleMusData, >EndOfCastleMusData, $4c, $24 16013 GroundLevelPart1Hdr: .db $18, <GroundM_P1Data, >GroundM_P1Data, $2d, $1c, $b8 16014 GroundLevelPart2AHdr: .db $18, <GroundM_P2AData, >GroundM_P2AData, $20, $12, $70 16015 GroundLevelPart2BHdr: .db $18, <GroundM_P2BData, >GroundM_P2BData, $1b, $10, $44 16016 GroundLevelPart2CHdr: .db $18, <GroundM_P2CData, >GroundM_P2CData, $11, $0a, $1c 16017 GroundLevelPart3AHdr: .db $18, <GroundM_P3AData, >GroundM_P3AData, $2d, $10, $58 16018 GroundLevelPart3BHdr: .db $18, <GroundM_P3BData, >GroundM_P3BData, $14, $0d, $3f 16019 GroundLevelLeadInHdr: .db $18, <GroundMLdInData, >GroundMLdInData, $15, $0d, $21 16020 GroundLevelPart4AHdr: .db $18, <GroundM_P4AData, >GroundM_P4AData, $18, $10, $7a 16021 GroundLevelPart4BHdr: .db $18, <GroundM_P4BData, >GroundM_P4BData, $19, $0f, $54 16022 GroundLevelPart4CHdr: .db $18, <GroundM_P4CData, >GroundM_P4CData, $1e, $12, $2b 16023 DeathMusHdr: .db $18, <DeathMusData, >DeathMusData, $1e, $0f, $2d 16024 16025 ;-------------------------------- 16026 16027 ;MUSIC DATA 16028 ;square 2/triangle format 16029 ;d7 - length byte flag (0-note, 1-length) 16030 ;if d7 is set to 0 and d6-d0 is nonzero: 16031 ;d6-d0 - note offset in frequency look-up table (must be even) 16032 ;if d7 is set to 1: 16033 ;d6-d3 - unused 16034 ;d2-d0 - length offset in length look-up table 16035 ;value of $00 in square 2 data is used as null terminator, affects all sound channels 16036 ;value of $00 in triangle data causes routine to skip note 16037 16038 ;square 1 format 16039 ;d7-d6, d0 - length offset in length look-up table (bit order is d0,d7,d6) 16040 ;d5-d1 - note offset in frequency look-up table 16041 ;value of $00 in square 1 data is flag alternate control reg data to be loaded 16042 16043 ;noise format 16044 ;d7-d6, d0 - length offset in length look-up table (bit order is d0,d7,d6) 16045 ;d5-d4 - beat type (0 - rest, 1 - short, 2 - strong, 3 - long) 16046 ;d3-d1 - unused 16047 ;value of $00 in noise data is used as null terminator, affects only noise 16048 16049 ;all music data is organized into sections (unless otherwise stated): 16050 ;square 2, square 1, triangle, noise 16051 16052 Star_CloudMData: 16053 .db $84, $2c, $2c, $2c, $82, $04, $2c, $04, $85, $2c, $84, $2c, $2c 16054 .db $2a, $2a, $2a, $82, $04, $2a, $04, $85, $2a, $84, $2a, $2a, $00 16055 16056 .db $1f, $1f, $1f, $98, $1f, $1f, $98, $9e, $98, $1f 16057 .db $1d, $1d, $1d, $94, $1d, $1d, $94, $9c, $94, $1d 16058 16059 .db $86, $18, $85, $26, $30, $84, $04, $26, $30 16060 .db $86, $14, $85, $22, $2c, $84, $04, $22, $2c 16061 16062 .db $21, $d0, $c4, $d0, $31, $d0, $c4, $d0, $00 16063 16064 GroundM_P1Data: 16065 .db $85, $2c, $22, $1c, $84, $26, $2a, $82, $28, $26, $04 16066 .db $87, $22, $34, $3a, $82, $40, $04, $36, $84, $3a, $34 16067 .db $82, $2c, $30, $85, $2a 16068 16069 SilenceData: 16070 .db $00 16071 16072 .db $5d, $55, $4d, $15, $19, $96, $15, $d5, $e3, $eb 16073 .db $2d, $a6, $2b, $27, $9c, $9e, $59 16074 16075 .db $85, $22, $1c, $14, $84, $1e, $22, $82, $20, $1e, $04, $87 16076 .db $1c, $2c, $34, $82, $36, $04, $30, $34, $04, $2c, $04, $26 16077 .db $2a, $85, $22 16078 16079 GroundM_P2AData: 16080 .db $84, $04, $82, $3a, $38, $36, $32, $04, $34 16081 .db $04, $24, $26, $2c, $04, $26, $2c, $30, $00 16082 16083 .db $05, $b4, $b2, $b0, $2b, $ac, $84 16084 .db $9c, $9e, $a2, $84, $94, $9c, $9e 16085 16086 .db $85, $14, $22, $84, $2c, $85, $1e 16087 .db $82, $2c, $84, $2c, $1e 16088 16089 GroundM_P2BData: 16090 .db $84, $04, $82, $3a, $38, $36, $32, $04, $34 16091 .db $04, $64, $04, $64, $86, $64, $00 16092 16093 .db $05, $b4, $b2, $b0, $2b, $ac, $84 16094 .db $37, $b6, $b6, $45 16095 16096 .db $85, $14, $1c, $82, $22, $84, $2c 16097 .db $4e, $82, $4e, $84, $4e, $22 16098 16099 GroundM_P2CData: 16100 .db $84, $04, $85, $32, $85, $30, $86, $2c, $04, $00 16101 16102 .db $05, $a4, $05, $9e, $05, $9d, $85 16103 16104 .db $84, $14, $85, $24, $28, $2c, $82 16105 .db $22, $84, $22, $14 16106 16107 .db $21, $d0, $c4, $d0, $31, $d0, $c4, $d0, $00 16108 16109 GroundM_P3AData: 16110 .db $82, $2c, $84, $2c, $2c, $82, $2c, $30 16111 .db $04, $34, $2c, $04, $26, $86, $22, $00 16112 16113 .db $a4, $25, $25, $a4, $29, $a2, $1d, $9c, $95 16114 16115 GroundM_P3BData: 16116 .db $82, $2c, $2c, $04, $2c, $04, $2c, $30, $85, $34, $04, $04, $00 16117 16118 .db $a4, $25, $25, $a4, $a8, $63, $04 16119 16120 ;triangle data used by both sections of third part 16121 .db $85, $0e, $1a, $84, $24, $85, $22, $14, $84, $0c 16122 16123 GroundMLdInData: 16124 .db $82, $34, $84, $34, $34, $82, $2c, $84, $34, $86, $3a, $04, $00 16125 16126 .db $a0, $21, $21, $a0, $21, $2b, $05, $a3 16127 16128 .db $82, $18, $84, $18, $18, $82, $18, $18, $04, $86, $3a, $22 16129 16130 ;noise data used by lead-in and third part sections 16131 .db $31, $90, $31, $90, $31, $71, $31, $90, $90, $90, $00 16132 16133 GroundM_P4AData: 16134 .db $82, $34, $84, $2c, $85, $22, $84, $24 16135 .db $82, $26, $36, $04, $36, $86, $26, $00 16136 16137 .db $ac, $27, $5d, $1d, $9e, $2d, $ac, $9f 16138 16139 .db $85, $14, $82, $20, $84, $22, $2c 16140 .db $1e, $1e, $82, $2c, $2c, $1e, $04 16141 16142 GroundM_P4BData: 16143 .db $87, $2a, $40, $40, $40, $3a, $36 16144 .db $82, $34, $2c, $04, $26, $86, $22, $00 16145 16146 .db $e3, $f7, $f7, $f7, $f5, $f1, $ac, $27, $9e, $9d 16147 16148 .db $85, $18, $82, $1e, $84, $22, $2a 16149 .db $22, $22, $82, $2c, $2c, $22, $04 16150 16151 DeathMusData: 16152 .db $86, $04 ;death music share data with fourth part c of ground level music 16153 16154 GroundM_P4CData: 16155 .db $82, $2a, $36, $04, $36, $87, $36, $34, $30, $86, $2c, $04, $00 16156 16157 .db $00, $68, $6a, $6c, $45 ;death music only 16158 16159 .db $a2, $31, $b0, $f1, $ed, $eb, $a2, $1d, $9c, $95 16160 16161 .db $86, $04 ;death music only 16162 16163 .db $85, $22, $82, $22, $87, $22, $26, $2a, $84, $2c, $22, $86, $14 16164 16165 ;noise data used by fourth part sections 16166 .db $51, $90, $31, $11, $00 16167 16168 CastleMusData: 16169 .db $80, $22, $28, $22, $26, $22, $24, $22, $26 16170 .db $22, $28, $22, $2a, $22, $28, $22, $26 16171 .db $22, $28, $22, $26, $22, $24, $22, $26 16172 .db $22, $28, $22, $2a, $22, $28, $22, $26 16173 .db $20, $26, $20, $24, $20, $26, $20, $28 16174 .db $20, $26, $20, $28, $20, $26, $20, $24 16175 .db $20, $26, $20, $24, $20, $26, $20, $28 16176 .db $20, $26, $20, $28, $20, $26, $20, $24 16177 .db $28, $30, $28, $32, $28, $30, $28, $2e 16178 .db $28, $30, $28, $2e, $28, $2c, $28, $2e 16179 .db $28, $30, $28, $32, $28, $30, $28, $2e 16180 .db $28, $30, $28, $2e, $28, $2c, $28, $2e, $00 16181 16182 .db $04, $70, $6e, $6c, $6e, $70, $72, $70, $6e 16183 .db $70, $6e, $6c, $6e, $70, $72, $70, $6e 16184 .db $6e, $6c, $6e, $70, $6e, $70, $6e, $6c 16185 .db $6e, $6c, $6e, $70, $6e, $70, $6e, $6c 16186 .db $76, $78, $76, $74, $76, $74, $72, $74 16187 .db $76, $78, $76, $74, $76, $74, $72, $74 16188 16189 .db $84, $1a, $83, $18, $20, $84, $1e, $83, $1c, $28 16190 .db $26, $1c, $1a, $1c 16191 16192 GameOverMusData: 16193 .db $82, $2c, $04, $04, $22, $04, $04, $84, $1c, $87 16194 .db $26, $2a, $26, $84, $24, $28, $24, $80, $22, $00 16195 16196 .db $9c, $05, $94, $05, $0d, $9f, $1e, $9c, $98, $9d 16197 16198 .db $82, $22, $04, $04, $1c, $04, $04, $84, $14 16199 .db $86, $1e, $80, $16, $80, $14 16200 16201 TimeRunOutMusData: 16202 .db $81, $1c, $30, $04, $30, $30, $04, $1e, $32, $04, $32, $32 16203 .db $04, $20, $34, $04, $34, $34, $04, $36, $04, $84, $36, $00 16204 16205 .db $46, $a4, $64, $a4, $48, $a6, $66, $a6, $4a, $a8, $68, $a8 16206 .db $6a, $44, $2b 16207 16208 .db $81, $2a, $42, $04, $42, $42, $04, $2c, $64, $04, $64, $64 16209 .db $04, $2e, $46, $04, $46, $46, $04, $22, $04, $84, $22 16210 16211 WinLevelMusData: 16212 .db $87, $04, $06, $0c, $14, $1c, $22, $86, $2c, $22 16213 .db $87, $04, $60, $0e, $14, $1a, $24, $86, $2c, $24 16214 .db $87, $04, $08, $10, $18, $1e, $28, $86, $30, $30 16215 .db $80, $64, $00 16216 16217 .db $cd, $d5, $dd, $e3, $ed, $f5, $bb, $b5, $cf, $d5 16218 .db $db, $e5, $ed, $f3, $bd, $b3, $d1, $d9, $df, $e9 16219 .db $f1, $f7, $bf, $ff, $ff, $ff, $34 16220 .db $00 ;unused byte 16221 16222 .db $86, $04, $87, $14, $1c, $22, $86, $34, $84, $2c 16223 .db $04, $04, $04, $87, $14, $1a, $24, $86, $32, $84 16224 .db $2c, $04, $86, $04, $87, $18, $1e, $28, $86, $36 16225 .db $87, $30, $30, $30, $80, $2c 16226 16227 ;square 2 and triangle use the same data, square 1 is unused 16228 UndergroundMusData: 16229 .db $82, $14, $2c, $62, $26, $10, $28, $80, $04 16230 .db $82, $14, $2c, $62, $26, $10, $28, $80, $04 16231 .db $82, $08, $1e, $5e, $18, $60, $1a, $80, $04 16232 .db $82, $08, $1e, $5e, $18, $60, $1a, $86, $04 16233 .db $83, $1a, $18, $16, $84, $14, $1a, $18, $0e, $0c 16234 .db $16, $83, $14, $20, $1e, $1c, $28, $26, $87 16235 .db $24, $1a, $12, $10, $62, $0e, $80, $04, $04 16236 .db $00 16237 16238 ;noise data directly follows square 2 here unlike in other songs 16239 WaterMusData: 16240 .db $82, $18, $1c, $20, $22, $26, $28 16241 .db $81, $2a, $2a, $2a, $04, $2a, $04, $83, $2a, $82, $22 16242 .db $86, $34, $32, $34, $81, $04, $22, $26, $2a, $2c, $30 16243 .db $86, $34, $83, $32, $82, $36, $84, $34, $85, $04, $81, $22 16244 .db $86, $30, $2e, $30, $81, $04, $22, $26, $2a, $2c, $2e 16245 .db $86, $30, $83, $22, $82, $36, $84, $34, $85, $04, $81, $22 16246 .db $86, $3a, $3a, $3a, $82, $3a, $81, $40, $82, $04, $81, $3a 16247 .db $86, $36, $36, $36, $82, $36, $81, $3a, $82, $04, $81, $36 16248 .db $86, $34, $82, $26, $2a, $36 16249 .db $81, $34, $34, $85, $34, $81, $2a, $86, $2c, $00 16250 16251 .db $84, $90, $b0, $84, $50, $50, $b0, $00 16252 16253 .db $98, $96, $94, $92, $94, $96, $58, $58, $58, $44 16254 .db $5c, $44, $9f, $a3, $a1, $a3, $85, $a3, $e0, $a6 16255 .db $23, $c4, $9f, $9d, $9f, $85, $9f, $d2, $a6, $23 16256 .db $c4, $b5, $b1, $af, $85, $b1, $af, $ad, $85, $95 16257 .db $9e, $a2, $aa, $6a, $6a, $6b, $5e, $9d 16258 16259 .db $84, $04, $04, $82, $22, $86, $22 16260 .db $82, $14, $22, $2c, $12, $22, $2a, $14, $22, $2c 16261 .db $1c, $22, $2c, $14, $22, $2c, $12, $22, $2a, $14 16262 .db $22, $2c, $1c, $22, $2c, $18, $22, $2a, $16, $20 16263 .db $28, $18, $22, $2a, $12, $22, $2a, $18, $22, $2a 16264 .db $12, $22, $2a, $14, $22, $2c, $0c, $22, $2c, $14, $22, $34, $12 16265 .db $22, $30, $10, $22, $2e, $16, $22, $34, $18, $26 16266 .db $36, $16, $26, $36, $14, $26, $36, $12, $22, $36 16267 .db $5c, $22, $34, $0c, $22, $22, $81, $1e, $1e, $85, $1e 16268 .db $81, $12, $86, $14 16269 16270 EndOfCastleMusData: 16271 .db $81, $2c, $22, $1c, $2c, $22, $1c, $85, $2c, $04 16272 .db $81, $2e, $24, $1e, $2e, $24, $1e, $85, $2e, $04 16273 .db $81, $32, $28, $22, $32, $28, $22, $85, $32 16274 .db $87, $36, $36, $36, $84, $3a, $00 16275 16276 .db $5c, $54, $4c, $5c, $54, $4c 16277 .db $5c, $1c, $1c, $5c, $5c, $5c, $5c 16278 .db $5e, $56, $4e, $5e, $56, $4e 16279 .db $5e, $1e, $1e, $5e, $5e, $5e, $5e 16280 .db $62, $5a, $50, $62, $5a, $50 16281 .db $62, $22, $22, $62, $e7, $e7, $e7, $2b 16282 16283 .db $86, $14, $81, $14, $80, $14, $14, $81, $14, $14, $14, $14 16284 .db $86, $16, $81, $16, $80, $16, $16, $81, $16, $16, $16, $16 16285 .db $81, $28, $22, $1a, $28, $22, $1a, $28, $80, $28, $28 16286 .db $81, $28, $87, $2c, $2c, $2c, $84, $30 16287 16288 VictoryMusData: 16289 .db $83, $04, $84, $0c, $83, $62, $10, $84, $12 16290 .db $83, $1c, $22, $1e, $22, $26, $18, $1e, $04, $1c, $00 16291 16292 .db $e3, $e1, $e3, $1d, $de, $e0, $23 16293 .db $ec, $75, $74, $f0, $f4, $f6, $ea, $31, $2d 16294 16295 .db $83, $12, $14, $04, $18, $1a, $1c, $14 16296 .db $26, $22, $1e, $1c, $18, $1e, $22, $0c, $14 16297 16298 ;unused space 16299 .db $ff, $ff, $ff 16300 16301 FreqRegLookupTbl: 16302 .db $00, $88, $00, $2f, $00, $00 16303 .db $02, $a6, $02, $80, $02, $5c, $02, $3a 16304 .db $02, $1a, $01, $df, $01, $c4, $01, $ab 16305 .db $01, $93, $01, $7c, $01, $67, $01, $53 16306 .db $01, $40, $01, $2e, $01, $1d, $01, $0d 16307 .db $00, $fe, $00, $ef, $00, $e2, $00, $d5 16308 .db $00, $c9, $00, $be, $00, $b3, $00, $a9 16309 .db $00, $a0, $00, $97, $00, $8e, $00, $86 16310 .db $00, $77, $00, $7e, $00, $71, $00, $54 16311 .db $00, $64, $00, $5f, $00, $59, $00, $50 16312 .db $00, $47, $00, $43, $00, $3b, $00, $35 16313 .db $00, $2a, $00, $23, $04, $75, $03, $57 16314 .db $02, $f9, $02, $cf, $01, $fc, $00, $6a 16315 16316 MusicLengthLookupTbl: 16317 .db $05, $0a, $14, $28, $50, $1e, $3c, $02 16318 .db $04, $08, $10, $20, $40, $18, $30, $0c 16319 .db $03, $06, $0c, $18, $30, $12, $24, $08 16320 .db $36, $03, $09, $06, $12, $1b, $24, $0c 16321 .db $24, $02, $06, $04, $0c, $12, $18, $08 16322 .db $12, $01, $03, $02, $06, $09, $0c, $04 16323 16324 EndOfCastleMusicEnvData: 16325 .db $98, $99, $9a, $9b 16326 16327 AreaMusicEnvData: 16328 .db $90, $94, $94, $95, $95, $96, $97, $98 16329 16330 WaterEventMusEnvData: 16331 .db $90, $91, $92, $92, $93, $93, $93, $94 16332 .db $94, $94, $94, $94, $94, $95, $95, $95 16333 .db $95, $95, $95, $96, $96, $96, $96, $96 16334 .db $96, $96, $96, $96, $96, $96, $96, $96 16335 .db $96, $96, $96, $96, $95, $95, $94, $93 16336 16337 BowserFlameEnvData: 16338 .db $15, $16, $16, $17, $17, $18, $19, $19 16339 .db $1a, $1a, $1c, $1d, $1d, $1e, $1e, $1f 16340 .db $1f, $1f, $1f, $1e, $1d, $1c, $1e, $1f 16341 .db $1f, $1e, $1d, $1c, $1a, $18, $16, $14 16342 16343 BrickShatterEnvData: 16344 .db $15, $16, $16, $17, $17, $18, $19, $19 16345 .db $1a, $1a, $1c, $1d, $1d, $1e, $1e, $1f 16346 16347 ;------------------------------------------------------------------------------------- 16348 ;INTERRUPT VECTORS 16349 16350 .dw NonMaskableInterrupt 16351 .dw Start 16352 .dw $fff0 ;unused