introducao-a-assembly
Repositório dos arquivos usados na apresentação "Introdução a Assembly" da CriptoGoma de 2020 🖥️
- git clone: git://git.pablopie.xyz/introducao-a-assembly
- Log
- Files
- Refs
- README
- LICENSE
- raw file (blob)
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