-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.asm
13053 lines (11884 loc) · 436 KB
/
main.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 (doppelheathen@gmail.com)
;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.
;-------------------------------------------------------------------------------------
.include "constants.asm"
.include "header.asm"
.org $8000
.include "system/init.asm"
.include "system/nmi.asm"
;-------------------------------------------------------------------------------------
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
;-------------------------------------------------------------------------------------
.include "engine/opermode.asm"
;-------------------------------------------------------------------------------------
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
;-------------------------------------------------------------------------------------
.include "engine/title-mode/titlescreen.asm"
.include "engine/title-mode/demo.asm"
;-------------------------------------------------------------------------------------
.include "engine/victory-mode/core.asm"
.include "engine/victory-mode/routines.asm"
.include "engine/victory-mode/setup.asm"
.include "engine/victory-mode/victory-walk.asm"
.include "engine/victory-mode/victory-message.asm"
.include "engine/victory-mode/end-world.asm"
;-------------------------------------------------------------------------------------
;data is used as tiles for numbers
;that appear when you defeat enemies
FloateyNumTileData:
.db $ff, $ff ;dummy
.db $f6, $fb ; "100"
.db $f7, $fb ; "200"
.db $f8, $fb ; "400"
.db $f9, $fb ; "500"
.db $fa, $fb ; "800"
.db $f6, $50 ; "1000"
.db $f7, $50 ; "2000"
.db $f8, $50 ; "4000"
.db $f9, $50 ; "5000"
.db $fa, $50 ; "8000"
.db $fd, $fe ; "1-UP"
;high nybble is digit number, low nybble is number to
;add to the digit of the player's score
ScoreUpdateData:
.db $ff ;dummy
.db $41, $42, $44, $45, $48
.db $31, $32, $34, $35, $38, $00
FloateyNumbersRoutine:
lda FloateyNum_Control,x ;load control for floatey number
beq EndExitOne ;if zero, branch to leave
cmp #$0b ;if less than $0b, branch
bcc ChkNumTimer
lda #$0b ;otherwise set to $0b, thus keeping
sta FloateyNum_Control,x ;it in range
ChkNumTimer:
tay ;use as Y
lda FloateyNum_Timer,x ;check value here
bne DecNumTimer ;if nonzero, branch ahead
sta FloateyNum_Control,x ;initialize floatey number control and leave
rts
DecNumTimer:
dec FloateyNum_Timer,x ;decrement value here
cmp #$2b ;if not reached a certain point, branch
bne ChkTallEnemy
cpy #$0b ;check offset for $0b
bne LoadNumTiles ;branch ahead if not found
inc NumberofLives ;give player one extra life (1-up)
lda #Sfx_ExtraLife
sta Square2SoundQueue ;and play the 1-up sound
LoadNumTiles:
lda ScoreUpdateData,y ;load point value here
lsr ;move high nybble to low
lsr
lsr
lsr
tax ;use as X offset, essentially the digit
lda ScoreUpdateData,y ;load again and this time
and #%00001111 ;mask out the high nybble
sta DigitModifier,x ;store as amount to add to the digit
jsr AddToScore ;update the score accordingly
ChkTallEnemy:
ldy Enemy_SprDataOffset,x ;get OAM data offset for enemy object
lda Enemy_ID,x ;get enemy object identifier
cmp #Spiny
beq FloateyPart ;branch if spiny
cmp #PiranhaPlant
beq FloateyPart ;branch if piranha plant
cmp #HammerBro
beq GetAltOffset ;branch elsewhere if hammer bro
cmp #GreyCheepCheep
beq FloateyPart ;branch if cheep-cheep of either color
cmp #RedCheepCheep
beq FloateyPart
cmp #TallEnemy
bcs GetAltOffset ;branch elsewhere if enemy object => $09
lda Enemy_State,x
cmp #$02 ;if enemy state defeated or otherwise
bcs FloateyPart ;$02 or greater, branch beyond this part
GetAltOffset:
ldx SprDataOffset_Ctrl ;load some kind of control bit
ldy Alt_SprDataOffset,x ;get alternate OAM data offset
ldx ObjectOffset ;get enemy object offset again
FloateyPart:
lda FloateyNum_Y_Pos,x ;get vertical coordinate for
cmp #$18 ;floatey number, if coordinate in the
bcc SetupNumSpr ;status bar, branch
sbc #$01
sta FloateyNum_Y_Pos,x ;otherwise subtract one and store as new
SetupNumSpr:
lda FloateyNum_Y_Pos,x ;get vertical coordinate
sbc #$08 ;subtract eight and dump into the
jsr DumpTwoSpr ;left and right sprite's Y coordinates
lda FloateyNum_X_Pos,x ;get horizontal coordinate
sta Sprite_X_Position,y ;store into X coordinate of left sprite
clc
adc #$08 ;add eight pixels and store into X
sta Sprite_X_Position+4,y ;coordinate of right sprite
lda #$02
sta Sprite_Attributes,y ;set palette control in attribute bytes
sta Sprite_Attributes+4,y ;of left and right sprites
lda FloateyNum_Control,x
asl ;multiply our floatey number control by 2
tax ;and use as offset for look-up table
lda FloateyNumTileData,x
sta Sprite_Tilenumber,y ;display first half of number of points
lda FloateyNumTileData+1,x
sta Sprite_Tilenumber+4,y ;display the second half
ldx ObjectOffset ;get enemy object offset and leave
rts
;-------------------------------------------------------------------------------------
.include "engine/screen/routines.asm"
.include "engine/screen/routine/init-screen.asm"
.include "engine/screen/routine/setup-intermediate.asm"
.include "engine/screen/routine/colors.asm"
.include "engine/screen/routine/status-lines.asm"
;-------------------------------------------------------------------------------------
DisplayTimeUp:
lda GameTimerExpiredFlag ;if game timer not expired, increment task
beq NoTimeUp ;control 2 tasks forward, otherwise, stay here
lda #$00
sta GameTimerExpiredFlag ;reset timer expiration flag
lda #$02 ;output time-up screen to buffer
jmp OutputInter
NoTimeUp:
inc ScreenRoutineTask ;increment control task 2 tasks forward
jmp IncSubtask
;-------------------------------------------------------------------------------------
DisplayIntermediate:
lda OperMode ;check primary mode of operation
beq NoInter ;if in title screen mode, skip this
cmp #GameOverModeValue ;are we in game over mode?
beq GameOverInter ;if so, proceed to display game over screen
lda AltEntranceControl ;otherwise check for mode of alternate entry
bne NoInter ;and branch if found
ldy AreaType ;check if we are on castle level
cpy #$03 ;and if so, branch (possibly residual)
beq PlayerInter
lda DisableIntermediate ;if this flag is set, skip intermediate lives display
bne NoInter ;and jump to specific task, otherwise
PlayerInter:
jsr DrawPlayer_Intermediate ;put player in appropriate place for
lda #$01 ;lives display, then output lives display to buffer
OutputInter:
jsr WriteGameText
jsr ResetScreenTimer
lda #$00
sta DisableScreenFlag ;reenable screen output
rts
GameOverInter:
lda #$12 ;set screen timer
sta ScreenTimer
lda #$03 ;output game over screen to buffer
jsr WriteGameText
jmp IncModeTask_B
NoInter: lda #$08 ;set for specific task and leave
sta ScreenRoutineTask
rts
;-------------------------------------------------------------------------------------
AreaParserTaskControl:
inc DisableScreenFlag ;turn off screen
TaskLoop:
jsr AreaParserTaskHandler ;render column set of current area
lda AreaParserTaskNum ;check number of tasks
bne TaskLoop ;if tasks still not all done, do another one
dec ColumnSets ;do we need to render more column sets?
bpl OutputCol
inc ScreenRoutineTask ;if not, move on to the next task
OutputCol:
lda #$06 ;set vram buffer to output rendered column set
sta VRAM_Buffer_AddrCtrl ;on next NMI
rts
;-------------------------------------------------------------------------------------
.include "engine/screen/routine/title-screen.asm"
;-------------------------------------------------------------------------------------
ClearBuffersDrawIcon:
lda OperMode ;check game mode
bne IncModeTask_B ;if not title screen mode, leave
ldx #$00 ;otherwise, clear buffer space
TScrClear:
sta VRAM_Buffer1-1,x
sta VRAM_Buffer1-1+$100,x
dex
bne TScrClear
jsr DrawMushroomIcon ;draw player select icon
IncSubtask:
inc ScreenRoutineTask ;move onto next task
rts
;-------------------------------------------------------------------------------------
WriteTopScore:
lda #$fa ;run display routine to display top score on title
jsr UpdateNumber
IncModeTask_B:
inc OperMode_Task ;move onto next mode
rts
;-------------------------------------------------------------------------------------
GameText:
TopStatusBarLine:
.db $20, $43, $05, $16, $0a, $1b, $12, $18 ; "MARIO"
.db $20, $52, $0b, $20, $18, $1b, $15, $0d ; "WORLD TIME"
.db $24, $24, $1d, $12, $16, $0e
.db $20, $68, $05, $00, $24, $24, $2e, $29 ; score trailing digit and coin display
.db $23, $c0, $7f, $aa ; attribute table data, clears name table 0 to palette 2
.db $23, $c2, $01, $ea ; attribute table data, used for coin icon in status bar
.db $ff ; end of data block
WorldLivesDisplay:
.db $21, $cd, $07, $24, $24 ; cross with spaces used on
.db $29, $24, $24, $24, $24 ; lives display
.db $21, $4b, $09, $20, $18 ; "WORLD - " used on lives display
.db $1b, $15, $0d, $24, $24, $28, $24
.db $22, $0c, $47, $24 ; possibly used to clear time up
.db $23, $dc, $01, $ba ; attribute table data for crown if more than 9 lives
.db $ff
TwoPlayerTimeUp:
.db $21, $cd, $05, $16, $0a, $1b, $12, $18 ; "MARIO"
OnePlayerTimeUp:
.db $22, $0c, $07, $1d, $12, $16, $0e, $24, $1e, $19 ; "TIME UP"
.db $ff
TwoPlayerGameOver:
.db $21, $cd, $05, $16, $0a, $1b, $12, $18 ; "MARIO"
OnePlayerGameOver:
.db $22, $0b, $09, $10, $0a, $16, $0e, $24 ; "GAME OVER"
.db $18, $1f, $0e, $1b
.db $ff
WarpZoneWelcome:
.db $25, $84, $15, $20, $0e, $15, $0c, $18, $16 ; "WELCOME TO WARP ZONE!"
.db $0e, $24, $1d, $18, $24, $20, $0a, $1b, $19
.db $24, $23, $18, $17, $0e, $2b
.db $26, $25, $01, $24 ; placeholder for left pipe
.db $26, $2d, $01, $24 ; placeholder for middle pipe
.db $26, $35, $01, $24 ; placeholder for right pipe
.db $27, $d9, $46, $aa ; attribute data
.db $27, $e1, $45, $aa
.db $ff
LuigiName:
.db $15, $1e, $12, $10, $12 ; "LUIGI", no address or length
WarpZoneNumbers:
.db $04, $03, $02, $00 ; warp zone numbers, note spaces on middle
.db $24, $05, $24, $00 ; zone, partly responsible for
.db $08, $07, $06, $00 ; the minus world
GameTextOffsets:
.db TopStatusBarLine-GameText, TopStatusBarLine-GameText
.db WorldLivesDisplay-GameText, WorldLivesDisplay-GameText
.db TwoPlayerTimeUp-GameText, OnePlayerTimeUp-GameText
.db TwoPlayerGameOver-GameText, OnePlayerGameOver-GameText
.db WarpZoneWelcome-GameText, WarpZoneWelcome-GameText
WriteGameText:
pha ;save text number to stack
asl
tay ;multiply by 2 and use as offset
cpy #$04 ;if set to do top status bar or world/lives display,
bcc LdGameText ;branch to use current offset as-is
cpy #$08 ;if set to do time-up or game over,
bcc Chk2Players ;branch to check players
ldy #$08 ;otherwise warp zone, therefore set offset
Chk2Players:
lda NumberOfPlayers ;check for number of players
bne LdGameText ;if there are two, use current offset to also print name
iny ;otherwise increment offset by one to not print name
LdGameText:
ldx GameTextOffsets,y ;get offset to message we want to print
ldy #$00
GameTextLoop:
lda GameText,x ;load message data
cmp #$ff ;check for terminator
beq EndGameText ;branch to end text if found
sta VRAM_Buffer1,y ;otherwise write data to buffer
inx ;and increment increment
iny
bne GameTextLoop ;do this for 256 bytes if no terminator found
EndGameText:
lda #$00 ;put null terminator at end
sta VRAM_Buffer1,y
pla ;pull original text number from stack
tax
cmp #$04 ;are we printing warp zone?
bcs PrintWarpZoneNumbers
dex ;are we printing the world/lives display?
bne CheckPlayerName ;if not, branch to check player's name
lda NumberofLives ;otherwise, check number of lives
clc ;and increment by one for display
adc #$01
cmp #10 ;more than 9 lives?
bcc PutLives
sbc #10 ;if so, subtract 10 and put a crown tile
ldy #$9f ;next to the difference...strange things happen if
sty VRAM_Buffer1+7 ;the number of lives exceeds 19
PutLives: sta VRAM_Buffer1+8
ldy WorldNumber ;write world and level numbers (incremented for display)
iny ;to the buffer in the spaces surrounding the dash
sty VRAM_Buffer1+19
ldy LevelNumber
iny
sty VRAM_Buffer1+21 ;we're done here
rts
CheckPlayerName:
lda NumberOfPlayers ;check number of players
beq ExitChkName ;if only 1 player, leave
lda CurrentPlayer ;load current player
dex ;check to see if current message number is for time up
bne ChkLuigi
ldy OperMode ;check for game over mode
cpy #GameOverModeValue
beq ChkLuigi
eor #%00000001 ;if not, must be time up, invert d0 to do other player
ChkLuigi:
lsr
bcc ExitChkName ;if mario is current player, do not change the name
ldy #$04
NameLoop:
lda LuigiName,y ;otherwise, replace "MARIO" with "LUIGI"
sta VRAM_Buffer1+3,y
dey
bpl NameLoop ;do this until each letter is replaced
ExitChkName:
rts
PrintWarpZoneNumbers:
sbc #$04 ;subtract 4 and then shift to the left
asl ;twice to get proper warp zone number
asl ;offset
tax
ldy #$00
WarpNumLoop:
lda WarpZoneNumbers,x ;print warp zone numbers into the
sta VRAM_Buffer1+27,y ;placeholders from earlier
inx
iny ;put a number in every fourth space
iny
iny
iny
cpy #$0c
bcc WarpNumLoop
lda #$2c ;load new buffer pointer at end of message
jmp SetVRAMOffset
;-------------------------------------------------------------------------------------
ResetSpritesAndScreenTimer:
lda ScreenTimer ;check if screen timer has expired
bne NoReset ;if not, branch to leave
jsr MoveAllSpritesOffscreen ;otherwise reset sprites now
ResetScreenTimer:
lda #$07 ;reset timer again
sta ScreenTimer
inc ScreenRoutineTask ;move onto next task
NoReset:
rts
;-------------------------------------------------------------------------------------
;$00 - temp vram buffer offset
;$01 - temp metatile buffer offset
;$02 - temp metatile graphics table offset
;$03 - used to store attribute bits
;$04 - used to determine attribute table row
;$05 - used to determine attribute table column
;$06 - metatile graphics table address low
;$07 - metatile graphics table address high
RenderAreaGraphics:
lda CurrentColumnPos ;store LSB of where we're at
and #$01
sta $05
ldy VRAM_Buffer2_Offset ;store vram buffer offset
sty $00
lda CurrentNTAddr_Low ;get current name table address we're supposed to render
sta VRAM_Buffer2+1,y
lda CurrentNTAddr_High
sta VRAM_Buffer2,y
lda #$9a ;store length byte of 26 here with d7 set
sta VRAM_Buffer2+2,y ;to increment by 32 (in columns)
lda #$00 ;init attribute row
sta $04
tax
DrawMTLoop:
stx $01 ;store init value of 0 or incremented offset for buffer
lda MetatileBuffer,x ;get first metatile number, and mask out all but 2 MSB
and #%11000000
sta $03 ;store attribute table bits here
asl ;note that metatile format is:
rol ;%xx000000 - attribute table bits,
rol ;%00xxxxxx - metatile number
tay ;rotate bits to d1-d0 and use as offset here
lda MetatileGraphics_Low,y ;get address to graphics table from here
sta $06
lda MetatileGraphics_High,y
sta $07
lda MetatileBuffer,x ;get metatile number again
asl ;multiply by 4 and use as tile offset
asl
sta $02
lda AreaParserTaskNum ;get current task number for level processing and
and #%00000001 ;mask out all but LSB, then invert LSB, multiply by 2
eor #%00000001 ;to get the correct column position in the metatile,
asl ;then add to the tile offset so we can draw either side
adc $02 ;of the metatiles
tay
ldx $00 ;use vram buffer offset from before as X
lda ($06),y
sta VRAM_Buffer2+3,x ;get first tile number (top left or top right) and store
iny
lda ($06),y ;now get the second (bottom left or bottom right) and store
sta VRAM_Buffer2+4,x
ldy $04 ;get current attribute row
lda $05 ;get LSB of current column where we're at, and
bne RightCheck ;branch if set (clear = left attrib, set = right)
lda $01 ;get current row we're rendering
lsr ;branch if LSB set (clear = top left, set = bottom left)
bcs LLeft
rol $03 ;rotate attribute bits 3 to the left
rol $03 ;thus in d1-d0, for upper left square
rol $03
jmp SetAttrib
RightCheck:
lda $01 ;get LSB of current row we're rendering
lsr ;branch if set (clear = top right, set = bottom right)
bcs NextMTRow
lsr $03 ;shift attribute bits 4 to the right
lsr $03 ;thus in d3-d2, for upper right square
lsr $03
lsr $03
jmp SetAttrib
LLeft: lsr $03 ;shift attribute bits 2 to the right
lsr $03 ;thus in d5-d4 for lower left square
NextMTRow:
inc $04 ;move onto next attribute row
SetAttrib:
lda AttributeBuffer,y ;get previously saved bits from before
ora $03 ;if any, and put new bits, if any, onto
sta AttributeBuffer,y ;the old, and store
inc $00 ;increment vram buffer offset by 2
inc $00
ldx $01 ;get current gfx buffer row, and check for
inx ;the bottom of the screen
cpx #$0d
bcc DrawMTLoop ;if not there yet, loop back
ldy $00 ;get current vram buffer offset, increment by 3
iny ;(for name table address and length bytes)
iny
iny
lda #$00
sta VRAM_Buffer2,y ;put null terminator at end of data for name table
sty VRAM_Buffer2_Offset ;store new buffer offset
inc CurrentNTAddr_Low ;increment name table address low
lda CurrentNTAddr_Low ;check current low byte
and #%00011111 ;if no wraparound, just skip this part
bne ExitDrawM
lda #$80 ;if wraparound occurs, make sure low byte stays
sta CurrentNTAddr_Low ;just under the status bar
lda CurrentNTAddr_High ;and then invert d2 of the name table address high
eor #%00000100 ;to move onto the next appropriate name table
sta CurrentNTAddr_High
ExitDrawM:
jmp SetVRAMCtrl ;jump to set buffer to $0341 and leave
;-------------------------------------------------------------------------------------
;$00 - temp attribute table address high (big endian order this time!)
;$01 - temp attribute table address low
RenderAttributeTables:
lda CurrentNTAddr_Low ;get low byte of next name table address
and #%00011111 ;to be written to, mask out all but 5 LSB,
sec ;subtract four
sbc #$04
and #%00011111 ;mask out bits again and store
sta $01
lda CurrentNTAddr_High ;get high byte and branch if borrow not set
bcs SetATHigh
eor #%00000100 ;otherwise invert d2
SetATHigh:
and #%00000100 ;mask out all other bits
ora #$23 ;add $2300 to the high byte and store
sta $00
lda $01 ;get low byte - 4, divide by 4, add offset for
lsr ;attribute table and store
lsr
adc #$c0 ;we should now have the appropriate block of
sta $01 ;attribute table in our temp address
ldx #$00
ldy VRAM_Buffer2_Offset ;get buffer offset
AttribLoop:
lda $00
sta VRAM_Buffer2,y ;store high byte of attribute table address
lda $01
clc ;get low byte, add 8 because we want to start
adc #$08 ;below the status bar, and store
sta VRAM_Buffer2+1,y
sta $01 ;also store in temp again
lda AttributeBuffer,x ;fetch current attribute table byte and store
sta VRAM_Buffer2+3,y ;in the buffer
lda #$01
sta VRAM_Buffer2+2,y ;store length of 1 in buffer
lsr
sta AttributeBuffer,x ;clear current byte in attribute buffer
iny ;increment buffer offset by 4 bytes
iny
iny
iny
inx ;increment attribute offset and check to see
cpx #$07 ;if we're at the end yet
bcc AttribLoop
sta VRAM_Buffer2,y ;put null terminator at the end
sty VRAM_Buffer2_Offset ;store offset in case we want to do any more
SetVRAMCtrl:
lda #$06
sta VRAM_Buffer_AddrCtrl ;set buffer to $0341 and leave
rts
;-------------------------------------------------------------------------------------
;$00 - used as temporary counter in ColorRotation
ColorRotatePalette:
.db $27, $27, $27, $17, $07, $17
BlankPalette:
.db $3f, $0c, $04, $ff, $ff, $ff, $ff, $00
;used based on area type
Palette3Data:
.db $0f, $07, $12, $0f
.db $0f, $07, $17, $0f
.db $0f, $07, $17, $1c
.db $0f, $07, $17, $00
ColorRotation:
lda FrameCounter ;get frame counter
and #$07 ;mask out all but three LSB
bne ExitColorRot ;branch if not set to zero to do this every eighth frame
ldx VRAM_Buffer1_Offset ;check vram buffer offset
cpx #$31
bcs ExitColorRot ;if offset over 48 bytes, branch to leave
tay ;otherwise use frame counter's 3 LSB as offset here
GetBlankPal:
lda BlankPalette,y ;get blank palette for palette 3
sta VRAM_Buffer1,x ;store it in the vram buffer
inx ;increment offsets
iny
cpy #$08
bcc GetBlankPal ;do this until all bytes are copied
ldx VRAM_Buffer1_Offset ;get current vram buffer offset
lda #$03
sta $00 ;set counter here
lda AreaType ;get area type
asl ;multiply by 4 to get proper offset
asl
tay ;save as offset here
GetAreaPal:
lda Palette3Data,y ;fetch palette to be written based on area type
sta VRAM_Buffer1+3,x ;store it to overwrite blank palette in vram buffer
iny
inx
dec $00 ;decrement counter
bpl GetAreaPal ;do this until the palette is all copied
ldx VRAM_Buffer1_Offset ;get current vram buffer offset
ldy ColorRotateOffset ;get color cycling offset
lda ColorRotatePalette,y
sta VRAM_Buffer1+4,x ;get and store current color in second slot of palette
lda VRAM_Buffer1_Offset
clc ;add seven bytes to vram buffer offset
adc #$07
sta VRAM_Buffer1_Offset
inc ColorRotateOffset ;increment color cycling offset
lda ColorRotateOffset
cmp #$06 ;check to see if it's still in range
bcc ExitColorRot ;if so, branch to leave
lda #$00
sta ColorRotateOffset ;otherwise, init to keep it in range
ExitColorRot:
rts ;leave
;-------------------------------------------------------------------------------------
;$00 - temp store for offset control bit
;$01 - temp vram buffer offset
;$02 - temp store for vertical high nybble in block buffer routine
;$03 - temp adder for high byte of name table address
;$04, $05 - name table address low/high
;$06, $07 - block buffer address low/high
BlockGfxData:
.db $45, $45, $47, $47
.db $47, $47, $47, $47
.db $57, $58, $59, $5a
.db $24, $24, $24, $24
.db $26, $26, $26, $26
RemoveCoin_Axe:
ldy #$41 ;set low byte so offset points to $0341
lda #$03 ;load offset for default blank metatile
ldx AreaType ;check area type
bne WriteBlankMT ;if not water type, use offset
lda #$04 ;otherwise load offset for blank metatile used in water
WriteBlankMT:
jsr PutBlockMetatile ;do a sub to write blank metatile to vram buffer
lda #$06
sta VRAM_Buffer_AddrCtrl ;set vram address controller to $0341 and leave
rts
ReplaceBlockMetatile:
jsr WriteBlockMetatile ;write metatile to vram buffer to replace block object
inc Block_ResidualCounter ;increment unused counter (residual code)
dec Block_RepFlag,x ;decrement flag (residual code)
rts ;leave
DestroyBlockMetatile:
lda #$00 ;force blank metatile if branched/jumped to this point
WriteBlockMetatile:
ldy #$03 ;load offset for blank metatile
cmp #$00 ;check contents of A for blank metatile
beq UseBOffset ;branch if found (unconditional if branched from 8a6b)
ldy #$00 ;load offset for brick metatile w/ line
cmp #$58
beq UseBOffset ;use offset if metatile is brick with coins (w/ line)
cmp #$51
beq UseBOffset ;use offset if metatile is breakable brick w/ line
iny ;increment offset for brick metatile w/o line
cmp #$5d
beq UseBOffset ;use offset if metatile is brick with coins (w/o line)
cmp #$52
beq UseBOffset ;use offset if metatile is breakable brick w/o line
iny ;if any other metatile, increment offset for empty block
UseBOffset:
tya ;put Y in A
ldy VRAM_Buffer1_Offset ;get vram buffer offset
iny ;move onto next byte
jsr PutBlockMetatile ;get appropriate block data and write to vram buffer
MoveVOffset:
dey ;decrement vram buffer offset
tya ;add 10 bytes to it
clc
adc #10
jmp SetVRAMOffset ;branch to store as new vram buffer offset
PutBlockMetatile:
stx $00 ;store control bit from SprDataOffset_Ctrl
sty $01 ;store vram buffer offset for next byte
asl
asl ;multiply A by four and use as X
tax
ldy #$20 ;load high byte for name table 0
lda $06 ;get low byte of block buffer pointer
cmp #$d0 ;check to see if we're on odd-page block buffer
bcc SaveHAdder ;if not, use current high byte
ldy #$24 ;otherwise load high byte for name table 1
SaveHAdder:
sty $03 ;save high byte here
and #$0f ;mask out high nybble of block buffer pointer
asl ;multiply by 2 to get appropriate name table low byte
sta $04 ;and then store it here
lda #$00
sta $05 ;initialize temp high byte
lda $02 ;get vertical high nybble offset used in block buffer routine
clc
adc #$20 ;add 32 pixels for the status bar
asl
rol $05 ;shift and rotate d7 onto d0 and d6 into carry
asl
rol $05 ;shift and rotate d6 onto d0 and d5 into carry
adc $04 ;add low byte of name table and carry to vertical high nybble
sta $04 ;and store here
lda $05 ;get whatever was in d7 and d6 of vertical high nybble
adc #$00 ;add carry
clc
adc $03 ;then add high byte of name table
sta $05 ;store here
ldy $01 ;get vram buffer offset to be used
RemBridge:
lda BlockGfxData,x ;write top left and top right
sta VRAM_Buffer1+2,y ;tile numbers into first spot
lda BlockGfxData+1,x
sta VRAM_Buffer1+3,y
lda BlockGfxData+2,x ;write bottom left and bottom
sta VRAM_Buffer1+7,y ;right tiles numbers into
lda BlockGfxData+3,x ;second spot
sta VRAM_Buffer1+8,y
lda $04
sta VRAM_Buffer1,y ;write low byte of name table
clc ;into first slot as read
adc #$20 ;add 32 bytes to value
sta VRAM_Buffer1+5,y ;write low byte of name table
lda $05 ;plus 32 bytes into second slot
sta VRAM_Buffer1-1,y ;write high byte of name
sta VRAM_Buffer1+4,y ;table address to both slots
lda #$02
sta VRAM_Buffer1+1,y ;put length of 2 in
sta VRAM_Buffer1+6,y ;both slots
lda #$00
sta VRAM_Buffer1+9,y ;put null terminator at end
ldx $00 ;get offset control bit here
rts ;and leave
;-------------------------------------------------------------------------------------
;METATILE GRAPHICS TABLE
MetatileGraphics_Low:
.db <Palette0_MTiles, <Palette1_MTiles, <Palette2_MTiles, <Palette3_MTiles
MetatileGraphics_High:
.db >Palette0_MTiles, >Palette1_MTiles, >Palette2_MTiles, >Palette3_MTiles
Palette0_MTiles:
.db $24, $24, $24, $24 ;blank
.db $27, $27, $27, $27 ;black metatile
.db $24, $24, $24, $35 ;bush left
.db $36, $25, $37, $25 ;bush middle
.db $24, $38, $24, $24 ;bush right
.db $24, $30, $30, $26 ;mountain left
.db $26, $26, $34, $26 ;mountain left bottom/middle center
.db $24, $31, $24, $32 ;mountain middle top
.db $33, $26, $24, $33 ;mountain right
.db $34, $26, $26, $26 ;mountain right bottom
.db $26, $26, $26, $26 ;mountain middle bottom
.db $24, $c0, $24, $c0 ;bridge guardrail
.db $24, $7f, $7f, $24 ;chain
.db $b8, $ba, $b9, $bb ;tall tree top, top half
.db $b8, $bc, $b9, $bd ;short tree top
.db $ba, $bc, $bb, $bd ;tall tree top, bottom half
.db $60, $64, $61, $65 ;warp pipe end left, points up
.db $62, $66, $63, $67 ;warp pipe end right, points up
.db $60, $64, $61, $65 ;decoration pipe end left, points up
.db $62, $66, $63, $67 ;decoration pipe end right, points up
.db $68, $68, $69, $69 ;pipe shaft left
.db $26, $26, $6a, $6a ;pipe shaft right
.db $4b, $4c, $4d, $4e ;tree ledge left edge
.db $4d, $4f, $4d, $4f ;tree ledge middle
.db $4d, $4e, $50, $51 ;tree ledge right edge
.db $6b, $70, $2c, $2d ;mushroom left edge
.db $6c, $71, $6d, $72 ;mushroom middle
.db $6e, $73, $6f, $74 ;mushroom right edge
.db $86, $8a, $87, $8b ;sideways pipe end top
.db $88, $8c, $88, $8c ;sideways pipe shaft top
.db $89, $8d, $69, $69 ;sideways pipe joint top
.db $8e, $91, $8f, $92 ;sideways pipe end bottom
.db $26, $93, $26, $93 ;sideways pipe shaft bottom
.db $90, $94, $69, $69 ;sideways pipe joint bottom
.db $a4, $e9, $ea, $eb ;seaplant
.db $24, $24, $24, $24 ;blank, used on bricks or blocks that are hit
.db $24, $2f, $24, $3d ;flagpole ball
.db $a2, $a2, $a3, $a3 ;flagpole shaft
.db $24, $24, $24, $24 ;blank, used in conjunction with vines
Palette1_MTiles:
.db $a2, $a2, $a3, $a3 ;vertical rope
.db $99, $24, $99, $24 ;horizontal rope
.db $24, $a2, $3e, $3f ;left pulley
.db $5b, $5c, $24, $a3 ;right pulley
.db $24, $24, $24, $24 ;blank used for balance rope
.db $9d, $47, $9e, $47 ;castle top
.db $47, $47, $27, $27 ;castle window left
.db $47, $47, $47, $47 ;castle brick wall
.db $27, $27, $47, $47 ;castle window right
.db $a9, $47, $aa, $47 ;castle top w/ brick
.db $9b, $27, $9c, $27 ;entrance top
.db $27, $27, $27, $27 ;entrance bottom
.db $52, $52, $52, $52 ;green ledge stump
.db $80, $a0, $81, $a1 ;fence
.db $be, $be, $bf, $bf ;tree trunk
.db $75, $ba, $76, $bb ;mushroom stump top
.db $ba, $ba, $bb, $bb ;mushroom stump bottom
.db $45, $47, $45, $47 ;breakable brick w/ line
.db $47, $47, $47, $47 ;breakable brick
.db $45, $47, $45, $47 ;breakable brick (not used)
.db $b4, $b6, $b5, $b7 ;cracked rock terrain
.db $45, $47, $45, $47 ;brick with line (power-up)
.db $45, $47, $45, $47 ;brick with line (vine)
.db $45, $47, $45, $47 ;brick with line (star)
.db $45, $47, $45, $47 ;brick with line (coins)
.db $45, $47, $45, $47 ;brick with line (1-up)
.db $47, $47, $47, $47 ;brick (power-up)
.db $47, $47, $47, $47 ;brick (vine)
.db $47, $47, $47, $47 ;brick (star)
.db $47, $47, $47, $47 ;brick (coins)
.db $47, $47, $47, $47 ;brick (1-up)
.db $24, $24, $24, $24 ;hidden block (1 coin)
.db $24, $24, $24, $24 ;hidden block (1-up)
.db $ab, $ac, $ad, $ae ;solid block (3-d block)
.db $5d, $5e, $5d, $5e ;solid block (white wall)
.db $c1, $24, $c1, $24 ;bridge
.db $c6, $c8, $c7, $c9 ;bullet bill cannon barrel
.db $ca, $cc, $cb, $cd ;bullet bill cannon top
.db $2a, $2a, $40, $40 ;bullet bill cannon bottom
.db $24, $24, $24, $24 ;blank used for jumpspring
.db $24, $47, $24, $47 ;half brick used for jumpspring
.db $82, $83, $84, $85 ;solid block (water level, green rock)
.db $24, $47, $24, $47 ;half brick (???)
.db $86, $8a, $87, $8b ;water pipe top
.db $8e, $91, $8f, $92 ;water pipe bottom
.db $24, $2f, $24, $3d ;flag ball (residual object)
Palette2_MTiles:
.db $24, $24, $24, $35 ;cloud left
.db $36, $25, $37, $25 ;cloud middle
.db $24, $38, $24, $24 ;cloud right
.db $24, $24, $39, $24 ;cloud bottom left
.db $3a, $24, $3b, $24 ;cloud bottom middle
.db $3c, $24, $24, $24 ;cloud bottom right
.db $41, $26, $41, $26 ;water/lava top
.db $26, $26, $26, $26 ;water/lava
.db $b0, $b1, $b2, $b3 ;cloud level terrain
.db $77, $79, $77, $79 ;bowser's bridge
Palette3_MTiles:
.db $53, $55, $54, $56 ;question block (coin)
.db $53, $55, $54, $56 ;question block (power-up)
.db $a5, $a7, $a6, $a8 ;coin
.db $c2, $c4, $c3, $c5 ;underwater coin
.db $57, $59, $58, $5a ;empty block
.db $7b, $7d, $7c, $7e ;axe
;-------------------------------------------------------------------------------------
;VRAM BUFFER DATA FOR LOCATIONS IN PRG-ROM
WaterPaletteData:
.db $3f, $00, $20
.db $0f, $15, $12, $25
.db $0f, $3a, $1a, $0f
.db $0f, $30, $12, $0f
.db $0f, $27, $12, $0f
.db $22, $16, $27, $18
.db $0f, $10, $30, $27
.db $0f, $16, $30, $27
.db $0f, $0f, $30, $10
.db $00
GroundPaletteData:
.db $3f, $00, $20