-
Notifications
You must be signed in to change notification settings - Fork 3
/
smb.asm
16355 lines (14772 loc) · 734 KB
/
smb.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;SMBDIS.ASM - A COMPREHENSIVE SUPER MARIO BROS. DISASSEMBLY
; https://gist.github.com/1wErt3r/4048722
;by doppelganger ([email protected])
;edited and collected by Jakiki6
;This file is provided for your own use as-is.
;There are so many people I have to thank for this, that taking all the credit for
;myself would be an unforgivable act of arrogance. Without their help this would
;probably not be possible. So I thank all the peeps in the nesdev scene whose insight into
;the 6502 and the NES helped me learn how it works (you guys know who you are, there's no
;way I could have done this without your help), as well as the authors of x816 and SMB
;Utility, and the reverse-engineers who did the original Super Mario Bros. Hacking Project,
;which I compared notes with but did not copy from. Last but certainly not least, I thank
;Nintendo for creating this game and the NES, without which this disassembly would
;only be theory.
;-------------------------------------------------------------------------------------
;DEFINES
;NES specific hardware defines
PPU_CTRL_REG1 = $2000
PPU_CTRL_REG2 = $2001
PPU_STATUS = $2002
PPU_SPR_ADDR = $2003
PPU_SPR_DATA = $2004
PPU_SCROLL_REG = $2005
PPU_ADDRESS = $2006
PPU_DATA = $2007
SND_REGISTER = $4000
SND_SQUARE1_REG = $4000
SND_SQUARE2_REG = $4004
SND_TRIANGLE_REG = $4008
SND_NOISE_REG = $400c
SND_DELTA_REG = $4010
SND_MASTERCTRL_REG = $4015
SPR_DMA = $4014
JOYPAD_PORT = $4016
JOYPAD_PORT1 = $4016
JOYPAD_PORT2 = $4017
; GAME SPECIFIC DEFINES
ObjectOffset = $08
FrameCounter = $09
SavedJoypadBits = $06fc
SavedJoypad1Bits = $06fc
SavedJoypad2Bits = $06fd
JoypadBitMask = $074a
JoypadOverride = $0758
A_B_Buttons = $0a
PreviousA_B_Buttons = $0d
Up_Down_Buttons = $0b
Left_Right_Buttons = $0c
GameEngineSubroutine = $0e
Mirror_PPU_CTRL_REG1 = $0778
Mirror_PPU_CTRL_REG2 = $0779
OperMode = $0770
OperMode_Task = $0772
ScreenRoutineTask = $073c
GamePauseStatus = $0776
GamePauseTimer = $0777
DemoAction = $0717
DemoActionTimer = $0718
TimerControl = $0747
IntervalTimerControl = $077f
Timers = $0780
SelectTimer = $0780
PlayerAnimTimer = $0781
JumpSwimTimer = $0782
RunningTimer = $0783
BlockBounceTimer = $0784
SideCollisionTimer = $0785
JumpspringTimer = $0786
GameTimerCtrlTimer = $0787
ClimbSideTimer = $0789
EnemyFrameTimer = $078a
FrenzyEnemyTimer = $078f
BowserFireBreathTimer = $0790
StompTimer = $0791
AirBubbleTimer = $0792
ScrollIntervalTimer = $0795
EnemyIntervalTimer = $0796
BrickCoinTimer = $079d
InjuryTimer = $079e
StarInvincibleTimer = $079f
ScreenTimer = $07a0
WorldEndTimer = $07a1
DemoTimer = $07a2
Sprite_Data = $0200
Sprite_Y_Position = $0200
Sprite_Tilenumber = $0201
Sprite_Attributes = $0202
Sprite_X_Position = $0203
ScreenEdge_PageLoc = $071a
ScreenEdge_X_Pos = $071c
ScreenLeft_PageLoc = $071a
ScreenRight_PageLoc = $071b
ScreenLeft_X_Pos = $071c
ScreenRight_X_Pos = $071d
PlayerFacingDir = $33
DestinationPageLoc = $34
VictoryWalkControl = $35
ScrollFractional = $0768
PrimaryMsgCounter = $0719
SecondaryMsgCounter = $0749
HorizontalScroll = $073f
VerticalScroll = $0740
ScrollLock = $0723
ScrollThirtyTwo = $073d
Player_X_Scroll = $06ff
Player_Pos_ForScroll = $0755
ScrollAmount = $0775
AreaData = $e7
AreaDataLow = $e7
AreaDataHigh = $e8
EnemyData = $e9
EnemyDataLow = $e9
EnemyDataHigh = $ea
AreaParserTaskNum = $071f
ColumnSets = $071e
CurrentPageLoc = $0725
CurrentColumnPos = $0726
BackloadingFlag = $0728
BehindAreaParserFlag = $0729
AreaObjectPageLoc = $072a
AreaObjectPageSel = $072b
AreaDataOffset = $072c
AreaObjOffsetBuffer = $072d
AreaObjectLength = $0730
StaircaseControl = $0734
AreaObjectHeight = $0735
MushroomLedgeHalfLen = $0736
EnemyDataOffset = $0739
EnemyObjectPageLoc = $073a
EnemyObjectPageSel = $073b
MetatileBuffer = $06a1
BlockBufferColumnPos = $06a0
CurrentNTAddr_Low = $0721
CurrentNTAddr_High = $0720
AttributeBuffer = $03f9
LoopCommand = $0745
DisplayDigits = $07d7
TopScoreDisplay = $07d7
ScoreAndCoinDisplay = $07dd
PlayerScoreDisplay = $07dd
GameTimerDisplay = $07f8
DigitModifier = $0134
VerticalFlipFlag = $0109
FloateyNum_Control = $0110
ShellChainCounter = $0125
FloateyNum_Timer = $012c
FloateyNum_X_Pos = $0117
FloateyNum_Y_Pos = $011e
FlagpoleFNum_Y_Pos = $010d
FlagpoleFNum_YMFDummy = $010e
FlagpoleScore = $010f
FlagpoleCollisionYPos = $070f
StompChainCounter = $0484
VRAM_Buffer1_Offset = $0300
VRAM_Buffer1 = $0301
VRAM_Buffer2_Offset = $0340
VRAM_Buffer2 = $0341
VRAM_Buffer_AddrCtrl = $0773
Sprite0HitDetectFlag = $0722
DisableScreenFlag = $0774
DisableIntermediate = $0769
ColorRotateOffset = $06d4
TerrainControl = $0727
AreaStyle = $0733
ForegroundScenery = $0741
BackgroundScenery = $0742
CloudTypeOverride = $0743
BackgroundColorCtrl = $0744
AreaType = $074e
AreaAddrsLOffset = $074f
AreaPointer = $0750
PlayerEntranceCtrl = $0710
GameTimerSetting = $0715
AltEntranceControl = $0752
EntrancePage = $0751
NumberOfPlayers = $077a
WarpZoneControl = $06d6
ChangeAreaTimer = $06de
MultiLoopCorrectCntr = $06d9
MultiLoopPassCntr = $06da
FetchNewGameTimerFlag = $0757
GameTimerExpiredFlag = $0759
PrimaryHardMode = $076a
SecondaryHardMode = $06cc
WorldSelectNumber = $076b
WorldSelectEnableFlag = $07fc
ContinueWorld = $07fd
CurrentPlayer = $0753
PlayerSize = $0754
PlayerStatus = $0756
OnscreenPlayerInfo = $075a
NumberofLives = $075a ;used by current player
HalfwayPage = $075b
LevelNumber = $075c ;the actual dash number
Hidden1UpFlag = $075d
CoinTally = $075e
WorldNumber = $075f
AreaNumber = $0760 ;internal number used to find areas
CoinTallyFor1Ups = $0748
OffscreenPlayerInfo = $0761
OffScr_NumberofLives = $0761 ;used by offscreen player
OffScr_HalfwayPage = $0762
OffScr_LevelNumber = $0763
OffScr_Hidden1UpFlag = $0764
OffScr_CoinTally = $0765
OffScr_WorldNumber = $0766
OffScr_AreaNumber = $0767
BalPlatformAlignment = $03a0
Platform_X_Scroll = $03a1
PlatformCollisionFlag = $03a2
YPlatformTopYPos = $0401
YPlatformCenterYPos = $58
BrickCoinTimerFlag = $06bc
StarFlagTaskControl = $0746
PseudoRandomBitReg = $07a7
WarmBootValidation = $07ff
SprShuffleAmtOffset = $06e0
SprShuffleAmt = $06e1
SprDataOffset = $06e4
Player_SprDataOffset = $06e4
Enemy_SprDataOffset = $06e5
Block_SprDataOffset = $06ec
Alt_SprDataOffset = $06ec
Bubble_SprDataOffset = $06ee
FBall_SprDataOffset = $06f1
Misc_SprDataOffset = $06f3
SprDataOffset_Ctrl = $03ee
Player_State = $1d
Enemy_State = $1e
Fireball_State = $24
Block_State = $26
Misc_State = $2a
Player_MovingDir = $45
Enemy_MovingDir = $46
SprObject_X_Speed = $57
Player_X_Speed = $57
Enemy_X_Speed = $58
Fireball_X_Speed = $5e
Block_X_Speed = $60
Misc_X_Speed = $64
Jumpspring_FixedYPos = $58
JumpspringAnimCtrl = $070e
JumpspringForce = $06db
SprObject_PageLoc = $6d
Player_PageLoc = $6d
Enemy_PageLoc = $6e
Fireball_PageLoc = $74
Block_PageLoc = $76
Misc_PageLoc = $7a
Bubble_PageLoc = $83
SprObject_X_Position = $86
Player_X_Position = $86
Enemy_X_Position = $87
Fireball_X_Position = $8d
Block_X_Position = $8f
Misc_X_Position = $93
Bubble_X_Position = $9c
SprObject_Y_Speed = $9f
Player_Y_Speed = $9f
Enemy_Y_Speed = $a0
Fireball_Y_Speed = $a6
Block_Y_Speed = $a8
Misc_Y_Speed = $ac
SprObject_Y_HighPos = $b5
Player_Y_HighPos = $b5
Enemy_Y_HighPos = $b6
Fireball_Y_HighPos = $bc
Block_Y_HighPos = $be
Misc_Y_HighPos = $c2
Bubble_Y_HighPos = $cb
SprObject_Y_Position = $ce
Player_Y_Position = $ce
Enemy_Y_Position = $cf
Fireball_Y_Position = $d5
Block_Y_Position = $d7
Misc_Y_Position = $db
Bubble_Y_Position = $e4
SprObject_Rel_XPos = $03ad
Player_Rel_XPos = $03ad
Enemy_Rel_XPos = $03ae
Fireball_Rel_XPos = $03af
Bubble_Rel_XPos = $03b0
Block_Rel_XPos = $03b1
Misc_Rel_XPos = $03b3
SprObject_Rel_YPos = $03b8
Player_Rel_YPos = $03b8
Enemy_Rel_YPos = $03b9
Fireball_Rel_YPos = $03ba
Bubble_Rel_YPos = $03bb
Block_Rel_YPos = $03bc
Misc_Rel_YPos = $03be
SprObject_SprAttrib = $03c4
Player_SprAttrib = $03c4
Enemy_SprAttrib = $03c5
SprObject_X_MoveForce = $0400
Enemy_X_MoveForce = $0401
SprObject_YMF_Dummy = $0416
Player_YMF_Dummy = $0416
Enemy_YMF_Dummy = $0417
Bubble_YMF_Dummy = $042c
SprObject_Y_MoveForce = $0433
Player_Y_MoveForce = $0433
Enemy_Y_MoveForce = $0434
Block_Y_MoveForce = $043c
DisableCollisionDet = $0716
Player_CollisionBits = $0490
Enemy_CollisionBits = $0491
SprObj_BoundBoxCtrl = $0499
Player_BoundBoxCtrl = $0499
Enemy_BoundBoxCtrl = $049a
Fireball_BoundBoxCtrl = $04a0
Misc_BoundBoxCtrl = $04a2
EnemyFrenzyBuffer = $06cb
EnemyFrenzyQueue = $06cd
Enemy_Flag = $0f
Enemy_ID = $16
PlayerGfxOffset = $06d5
Player_XSpeedAbsolute = $0700
FrictionAdderHigh = $0701
FrictionAdderLow = $0702
RunningSpeed = $0703
SwimmingFlag = $0704
Player_X_MoveForce = $0705
DiffToHaltJump = $0706
JumpOrigin_Y_HighPos = $0707
JumpOrigin_Y_Position = $0708
VerticalForce = $0709
VerticalForceDown = $070a
PlayerChangeSizeFlag = $070b
PlayerAnimTimerSet = $070c
PlayerAnimCtrl = $070d
DeathMusicLoaded = $0712
FlagpoleSoundQueue = $0713
CrouchingFlag = $0714
MaximumLeftSpeed = $0450
MaximumRightSpeed = $0456
SprObject_OffscrBits = $03d0
Player_OffscreenBits = $03d0
Enemy_OffscreenBits = $03d1
FBall_OffscreenBits = $03d2
Bubble_OffscreenBits = $03d3
Block_OffscreenBits = $03d4
Misc_OffscreenBits = $03d6
EnemyOffscrBitsMasked = $03d8
Cannon_Offset = $046a
Cannon_PageLoc = $046b
Cannon_X_Position = $0471
Cannon_Y_Position = $0477
Cannon_Timer = $047d
Whirlpool_Offset = $046a
Whirlpool_PageLoc = $046b
Whirlpool_LeftExtent = $0471
Whirlpool_Length = $0477
Whirlpool_Flag = $047d
VineFlagOffset = $0398
VineHeight = $0399
VineObjOffset = $039a
VineStart_Y_Position = $039d
Block_Orig_YPos = $03e4
Block_BBuf_Low = $03e6
Block_Metatile = $03e8
Block_PageLoc2 = $03ea
Block_RepFlag = $03ec
Block_ResidualCounter = $03f0
Block_Orig_XPos = $03f1
BoundingBox_UL_XPos = $04ac
BoundingBox_UL_YPos = $04ad
BoundingBox_DR_XPos = $04ae
BoundingBox_DR_YPos = $04af
BoundingBox_UL_Corner = $04ac
BoundingBox_LR_Corner = $04ae
EnemyBoundingBoxCoord = $04b0
PowerUpType = $39
FireballBouncingFlag = $3a
FireballCounter = $06ce
FireballThrowingTimer = $0711
HammerEnemyOffset = $06ae
JumpCoinMiscOffset = $06b7
Block_Buffer_1 = $0500
Block_Buffer_2 = $05d0
HammerThrowingTimer = $03a2
HammerBroJumpTimer = $3c
Misc_Collision_Flag = $06be
RedPTroopaOrigXPos = $0401
RedPTroopaCenterYPos = $58
XMovePrimaryCounter = $a0
XMoveSecondaryCounter = $58
CheepCheepMoveMFlag = $58
CheepCheepOrigYPos = $0434
BitMFilter = $06dd
LakituReappearTimer = $06d1
LakituMoveSpeed = $58
LakituMoveDirection = $a0
FirebarSpinState_Low = $58
FirebarSpinState_High = $a0
FirebarSpinSpeed = $0388
FirebarSpinDirection = $34
DuplicateObj_Offset = $06cf
NumberofGroupEnemies = $06d3
BlooperMoveCounter = $a0
BlooperMoveSpeed = $58
BowserBodyControls = $0363
BowserFeetCounter = $0364
BowserMovementSpeed = $0365
BowserOrigXPos = $0366
BowserFlameTimerCtrl = $0367
BowserFront_Offset = $0368
BridgeCollapseOffset = $0369
BowserGfxFlag = $036a
BowserHitPoints = $0483
MaxRangeFromOrigin = $06dc
BowserFlamePRandomOfs = $0417
PiranhaPlantUpYPos = $0417
PiranhaPlantDownYPos = $0434
PiranhaPlant_Y_Speed = $58
PiranhaPlant_MoveFlag = $a0
FireworksCounter = $06d7
ExplosionGfxCounter = $58
ExplosionTimerCounter = $a0
;sound related defines
Squ2_NoteLenBuffer = $07b3
Squ2_NoteLenCounter = $07b4
Squ2_EnvelopeDataCtrl = $07b5
Squ1_NoteLenCounter = $07b6
Squ1_EnvelopeDataCtrl = $07b7
Tri_NoteLenBuffer = $07b8
Tri_NoteLenCounter = $07b9
Noise_BeatLenCounter = $07ba
Squ1_SfxLenCounter = $07bb
Squ2_SfxLenCounter = $07bd
Sfx_SecondaryCounter = $07be
Noise_SfxLenCounter = $07bf
PauseSoundQueue = $fa
Square1SoundQueue = $ff
Square2SoundQueue = $fe
NoiseSoundQueue = $fd
AreaMusicQueue = $fb
EventMusicQueue = $fc
Square1SoundBuffer = $f1
Square2SoundBuffer = $f2
NoiseSoundBuffer = $f3
AreaMusicBuffer = $f4
EventMusicBuffer = $07b1
PauseSoundBuffer = $07b2
MusicData = $f5
MusicDataLow = $f5
MusicDataHigh = $f6
MusicOffset_Square2 = $f7
MusicOffset_Square1 = $f8
MusicOffset_Triangle = $f9
MusicOffset_Noise = $07b0
NoteLenLookupTblOfs = $f0
DAC_Counter = $07c0
NoiseDataLoopbackOfs = $07c1
NoteLengthTblAdder = $07c4
AreaMusicBuffer_Alt = $07c5
PauseModeFlag = $07c6
GroundMusicHeaderOfs = $07c7
AltRegContentFlag = $07ca
;-------------------------------------------------------------------------------------
;CONSTANTS
;sound effects constants
Sfx_SmallJump = %10000000
Sfx_Flagpole = %01000000
Sfx_Fireball = %00100000
Sfx_PipeDown_Injury = %00010000
Sfx_EnemySmack = %00001000
Sfx_EnemyStomp = %00000100
Sfx_Bump = %00000010
Sfx_BigJump = %00000001
Sfx_BowserFall = %10000000
Sfx_ExtraLife = %01000000
Sfx_PowerUpGrab = %00100000
Sfx_TimerTick = %00010000
Sfx_Blast = %00001000
Sfx_GrowVine = %00000100
Sfx_GrowPowerUp = %00000010
Sfx_CoinGrab = %00000001
Sfx_BowserFlame = %00000010
Sfx_BrickShatter = %00000001
;music constants
Silence = %10000000
StarPowerMusic = %01000000
PipeIntroMusic = %00100000
CloudMusic = %00010000
CastleMusic = %00001000
UndergroundMusic = %00000100
WaterMusic = %00000010
GroundMusic = %00000001
TimeRunningOutMusic = %01000000
EndOfLevelMusic = %00100000
AltGameOverMusic = %00010000
EndOfCastleMusic = %00001000
VictoryMusic = %00000100
GameOverMusic = %00000010
DeathMusic = %00000001
;enemy object constants
GreenKoopa = $00
BuzzyBeetle = $02
RedKoopa = $03
HammerBro = $05
Goomba = $06
Bloober = $07
BulletBill_FrenzyVar = $08
GreyCheepCheep = $0a
RedCheepCheep = $0b
Podoboo = $0c
PiranhaPlant = $0d
GreenParatroopaJump = $0e
RedParatroopa = $0f
GreenParatroopaFly = $10
Lakitu = $11
Spiny = $12
FlyCheepCheepFrenzy = $14
FlyingCheepCheep = $14
BowserFlame = $15
Fireworks = $16
BBill_CCheep_Frenzy = $17
Stop_Frenzy = $18
Bowser = $2d
PowerUpObject = $2e
VineObject = $2f
FlagpoleFlagObject = $30
StarFlagObject = $31
JumpspringObject = $32
BulletBill_CannonVar = $33
RetainerObject = $35
TallEnemy = $09
;other constants
World1 = 0
World2 = 1
World3 = 2
World4 = 3
World5 = 4
World6 = 5
World7 = 6
World8 = 7
Level1 = 0
Level2 = 1
Level3 = 2
Level4 = 3
WarmBootOffset = <$07d6
ColdBootOffset = <$07fe
TitleScreenDataOffset = $1ec0
SoundMemory = $07b0
SwimTileRepOffset = PlayerGraphicsTable + $9e
MusicHeaderOffsetData = MusicHeaderData - 1
MHD = MusicHeaderData
A_Button = %10000000
B_Button = %01000000
Select_Button = %00100000
Start_Button = %00010000
Up_Dir = %00001000
Down_Dir = %00000100
Left_Dir = %00000010
Right_Dir = %00000001
TitleScreenModeValue = 0
GameModeValue = 1
VictoryModeValue = 2
GameOverModeValue = 3
;-------------------------------------------------------------------------------------
;DIRECTIVES
; .index 8
; .mem 8
.db $4e, $45, $53, $1a, $02, $01, $01, $00, $00, $00, $00, $00, $00, $00, $00, $00
.org $8000
;-------------------------------------------------------------------------------------
Start:
sei ;pretty standard 6502 type init here
cld
lda #%00010000 ;init PPU control register 1
sta PPU_CTRL_REG1
ldx #$ff ;reset stack pointer
txs
VBlank1: lda PPU_STATUS ;wait two frames
bpl VBlank1
VBlank2: lda PPU_STATUS
bpl VBlank2
ldy #ColdBootOffset ;load default cold boot pointer
ldx #$05 ;this is where we check for a warm boot
WBootCheck: lda TopScoreDisplay,x ;check each score digit in the top score
cmp #10 ;to see if we have a valid digit
bcs ColdBoot ;if not, give up and proceed with cold boot
dex
bpl WBootCheck
lda WarmBootValidation ;second checkpoint, check to see if
cmp #$a5 ;another location has a specific value
bne ColdBoot
ldy #WarmBootOffset ;if passed both, load warm boot pointer
ColdBoot: jsr InitializeMemory ;clear memory using pointer in Y
sta SND_DELTA_REG+1 ;reset delta counter load register
sta OperMode ;reset primary mode of operation
lda #$a5 ;set warm boot flag
sta WarmBootValidation
sta PseudoRandomBitReg ;set seed for pseudorandom register
lda #%00001111
sta SND_MASTERCTRL_REG ;enable all sound channels except dmc
lda #%00000110
sta PPU_CTRL_REG2 ;turn off clipping for OAM and background
jsr MoveAllSpritesOffscreen
jsr InitializeNameTables ;initialize both name tables
inc DisableScreenFlag ;set flag to disable screen output
lda Mirror_PPU_CTRL_REG1
ora #%10000000 ;enable NMIs
jsr WritePPUReg1
EndlessLoop: jmp EndlessLoop ;endless loop, need I say more?
;-------------------------------------------------------------------------------------
;$00 - vram buffer address table low, also used for pseudorandom bit
;$01 - vram buffer address table high
VRAM_AddrTable_Low:
.db <VRAM_Buffer1, <WaterPaletteData, <GroundPaletteData
.db <UndergroundPaletteData, <CastlePaletteData, <VRAM_Buffer1_Offset
.db <VRAM_Buffer2, <VRAM_Buffer2, <BowserPaletteData
.db <DaySnowPaletteData, <NightSnowPaletteData, <MushroomPaletteData
.db <MarioThanksMessage, <LuigiThanksMessage, <MushroomRetainerSaved
.db <PrincessSaved1, <PrincessSaved2, <WorldSelectMessage1
.db <WorldSelectMessage2
VRAM_AddrTable_High:
.db >VRAM_Buffer1, >WaterPaletteData, >GroundPaletteData
.db >UndergroundPaletteData, >CastlePaletteData, >VRAM_Buffer1_Offset
.db >VRAM_Buffer2, >VRAM_Buffer2, >BowserPaletteData
.db >DaySnowPaletteData, >NightSnowPaletteData, >MushroomPaletteData
.db >MarioThanksMessage, >LuigiThanksMessage, >MushroomRetainerSaved
.db >PrincessSaved1, >PrincessSaved2, >WorldSelectMessage1
.db >WorldSelectMessage2
VRAM_Buffer_Offset:
.db <VRAM_Buffer1_Offset, <VRAM_Buffer2_Offset
NonMaskableInterrupt:
lda Mirror_PPU_CTRL_REG1 ;disable NMIs in mirror reg
and #%01111111 ;save all other bits
sta Mirror_PPU_CTRL_REG1
and #%01111110 ;alter name table address to be $2800
sta PPU_CTRL_REG1 ;(essentially $2000) but save other bits
lda Mirror_PPU_CTRL_REG2 ;disable OAM and background display by default
and #%11100110
ldy DisableScreenFlag ;get screen disable flag
bne ScreenOff ;if set, used bits as-is
lda Mirror_PPU_CTRL_REG2 ;otherwise reenable bits and save them
ora #%00011110
ScreenOff: sta Mirror_PPU_CTRL_REG2 ;save bits for later but not in register at the moment
and #%11100111 ;disable screen for now
sta PPU_CTRL_REG2
ldx PPU_STATUS ;reset flip-flop and reset scroll registers to zero
lda #$00
jsr InitScroll
sta PPU_SPR_ADDR ;reset spr-ram address register
lda #$02 ;perform spr-ram DMA access on $0200-$02ff
sta SPR_DMA
ldx VRAM_Buffer_AddrCtrl ;load control for pointer to buffer contents
lda VRAM_AddrTable_Low,x ;set indirect at $00 to pointer
sta $00
lda VRAM_AddrTable_High,x
sta $01
jsr UpdateScreen ;update screen with buffer contents
ldy #$00
ldx VRAM_Buffer_AddrCtrl ;check for usage of $0341
cpx #$06
bne InitBuffer
iny ;get offset based on usage
InitBuffer: ldx VRAM_Buffer_Offset,y
lda #$00 ;clear buffer header at last location
sta VRAM_Buffer1_Offset,x
sta VRAM_Buffer1,x
sta VRAM_Buffer_AddrCtrl ;reinit address control to $0301
lda Mirror_PPU_CTRL_REG2 ;copy mirror of $2001 to register
sta PPU_CTRL_REG2
jsr SoundEngine ;play sound
jsr ReadJoypads ;read joypads
jsr PauseRoutine ;handle pause
jsr UpdateTopScore
lda GamePauseStatus ;check for pause status
lsr
bcs PauseSkip
lda TimerControl ;if master timer control not set, decrement
beq DecTimers ;all frame and interval timers
dec TimerControl
bne NoDecTimers
DecTimers: ldx #$14 ;load end offset for end of frame timers
dec IntervalTimerControl ;decrement interval timer control,
bpl DecTimersLoop ;if not expired, only frame timers will decrement
lda #$14
sta IntervalTimerControl ;if control for interval timers expired,
ldx #$23 ;interval timers will decrement along with frame timers
DecTimersLoop: lda Timers,x ;check current timer
beq SkipExpTimer ;if current timer expired, branch to skip,
dec Timers,x ;otherwise decrement the current timer
SkipExpTimer: dex ;move onto next timer
bpl DecTimersLoop ;do this until all timers are dealt with
NoDecTimers: inc FrameCounter ;increment frame counter
PauseSkip: ldx #$00
ldy #$07
lda PseudoRandomBitReg ;get first memory location of LSFR bytes
and #%00000010 ;mask out all but d1
sta $00 ;save here
lda PseudoRandomBitReg+1 ;get second memory location
and #%00000010 ;mask out all but d1
eor $00 ;perform exclusive-OR on d1 from first and second bytes
clc ;if neither or both are set, carry will be clear
beq RotPRandomBit
sec ;if one or the other is set, carry will be set
RotPRandomBit: ror PseudoRandomBitReg,x ;rotate carry into d7, and rotate last bit into carry
inx ;increment to next byte
dey ;decrement for loop
bne RotPRandomBit
lda Sprite0HitDetectFlag ;check for flag here
beq SkipSprite0
Sprite0Clr: lda PPU_STATUS ;wait for sprite 0 flag to clear, which will
and #%01000000 ;not happen until vblank has ended
bne Sprite0Clr
lda GamePauseStatus ;if in pause mode, do not bother with sprites at all
lsr
bcs Sprite0Hit
jsr MoveSpritesOffscreen
jsr SpriteShuffler
Sprite0Hit: lda PPU_STATUS ;do sprite #0 hit detection
and #%01000000
beq Sprite0Hit
ldy #$14 ;small delay, to wait until we hit horizontal blank time
HBlankDelay: dey
bne HBlankDelay
SkipSprite0: lda HorizontalScroll ;set scroll registers from variables
sta PPU_SCROLL_REG
lda VerticalScroll
sta PPU_SCROLL_REG
lda Mirror_PPU_CTRL_REG1 ;load saved mirror of $2000
pha
sta PPU_CTRL_REG1
lda GamePauseStatus ;if in pause mode, do not perform operation mode stuff
lsr
bcs SkipMainOper
jsr OperModeExecutionTree ;otherwise do one of many, many possible subroutines
SkipMainOper: lda PPU_STATUS ;reset flip-flop
pla
ora #%10000000 ;reactivate NMIs
sta PPU_CTRL_REG1
rti ;we are done until the next frame!
;-------------------------------------------------------------------------------------
PauseRoutine:
lda OperMode ;are we in victory mode?
cmp #VictoryModeValue ;if so, go ahead
beq ChkPauseTimer
cmp #GameModeValue ;are we in game mode?
bne ExitPause ;if not, leave
lda OperMode_Task ;if we are in game mode, are we running game engine?
cmp #$03
bne ExitPause ;if not, leave
ChkPauseTimer: lda GamePauseTimer ;check if pause timer is still counting down
beq ChkStart
dec GamePauseTimer ;if so, decrement and leave
rts
ChkStart: lda SavedJoypad1Bits ;check to see if start is pressed
and #Start_Button ;on controller 1
beq ClrPauseTimer
lda GamePauseStatus ;check to see if timer flag is set
and #%10000000 ;and if so, do not reset timer (residual,
bne ExitPause ;joypad reading routine makes this unnecessary)
lda #$2b ;set pause timer
sta GamePauseTimer
lda GamePauseStatus
tay
iny ;set pause sfx queue for next pause mode
sty PauseSoundQueue
eor #%00000001 ;invert d0 and set d7
ora #%10000000
bne SetPause ;unconditional branch
ClrPauseTimer: lda GamePauseStatus ;clear timer flag if timer is at zero and start button
and #%01111111 ;is not pressed
SetPause: sta GamePauseStatus
ExitPause: rts
;-------------------------------------------------------------------------------------
;$00 - used for preset value
SpriteShuffler:
ldy AreaType ;load level type, likely residual code
lda #$28 ;load preset value which will put it at
sta $00 ;sprite #10
ldx #$0e ;start at the end of OAM data offsets
ShuffleLoop: lda SprDataOffset,x ;check for offset value against
cmp $00 ;the preset value
bcc NextSprOffset ;if less, skip this part
ldy SprShuffleAmtOffset ;get current offset to preset value we want to add
clc
adc SprShuffleAmt,y ;get shuffle amount, add to current sprite offset
bcc StrSprOffset ;if not exceeded $ff, skip second add
clc
adc $00 ;otherwise add preset value $28 to offset
StrSprOffset: sta SprDataOffset,x ;store new offset here or old one if branched to here
NextSprOffset: dex ;move backwards to next one
bpl ShuffleLoop
ldx SprShuffleAmtOffset ;load offset
inx
cpx #$03 ;check if offset + 1 goes to 3
bne SetAmtOffset ;if offset + 1 not 3, store
ldx #$00 ;otherwise, init to 0
SetAmtOffset: stx SprShuffleAmtOffset
ldx #$08 ;load offsets for values and storage
ldy #$02
SetMiscOffset: lda SprDataOffset+5,y ;load one of three OAM data offsets
sta Misc_SprDataOffset-2,x ;store first one unmodified, but
clc ;add eight to the second and eight
adc #$08 ;more to the third one
sta Misc_SprDataOffset-1,x ;note that due to the way X is set up,
clc ;this code loads into the misc sprite offsets
adc #$08
sta Misc_SprDataOffset,x
dex
dex
dex
dey
bpl SetMiscOffset ;do this until all misc spr offsets are loaded
rts
;-------------------------------------------------------------------------------------
OperModeExecutionTree:
lda OperMode ;this is the heart of the entire program,
jsr JumpEngine ;most of what goes on starts here
.dw TitleScreenMode
.dw GameMode
.dw VictoryMode
.dw GameOverMode
;-------------------------------------------------------------------------------------
MoveAllSpritesOffscreen:
ldy #$00 ;this routine moves all sprites off the screen
.db $2c ;BIT instruction opcode
MoveSpritesOffscreen:
ldy #$04 ;this routine moves all but sprite 0
lda #$f8 ;off the screen
SprInitLoop: sta Sprite_Y_Position,y ;write 248 into OAM data's Y coordinate
iny ;which will move it off the screen
iny
iny
iny
bne SprInitLoop
rts
;-------------------------------------------------------------------------------------
TitleScreenMode:
lda OperMode_Task
jsr JumpEngine
.dw InitializeGame
.dw ScreenRoutines
.dw PrimaryGameSetup
.dw GameMenuRoutine
;-------------------------------------------------------------------------------------
WSelectBufferTemplate:
.db $04, $20, $73, $01, $00, $00
GameMenuRoutine:
ldy #$00
lda SavedJoypad1Bits ;check to see if either player pressed
ora SavedJoypad2Bits ;only the start button (either joypad)
cmp #Start_Button
beq StartGame
cmp #A_Button+Start_Button ;check to see if A + start was pressed
bne ChkSelect ;if not, branch to check select button
StartGame: jmp ChkContinue ;if either start or A + start, execute here
ChkSelect: cmp #Select_Button ;check to see if the select button was pressed
beq SelectBLogic ;if so, branch reset demo timer
ldx DemoTimer ;otherwise check demo timer
bne ChkWorldSel ;if demo timer not expired, branch to check world selection
sta SelectTimer ;set controller bits here if running demo
jsr DemoEngine ;run through the demo actions
bcs ResetTitle ;if carry flag set, demo over, thus branch
jmp RunDemo ;otherwise, run game engine for demo
ChkWorldSel: ldx WorldSelectEnableFlag ;check to see if world selection has been enabled
beq NullJoypad
cmp #B_Button ;if so, check to see if the B button was pressed
bne NullJoypad
iny ;if so, increment Y and execute same code as select
SelectBLogic: lda DemoTimer ;if select or B pressed, check demo timer one last time
beq ResetTitle ;if demo timer expired, branch to reset title screen mode
lda #$18 ;otherwise reset demo timer
sta DemoTimer
lda SelectTimer ;check select/B button timer
bne NullJoypad ;if not expired, branch
lda #$10 ;otherwise reset select button timer
sta SelectTimer