-
Notifications
You must be signed in to change notification settings - Fork 13
/
main.asm
executable file
·7769 lines (7408 loc) · 218 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
.setcpu "6502"
.include "constants.asm"
tmp1 := $0000
tmp2 := $0001
tmp3 := $0002
tmpBulkCopyToPpuReturnAddr:= $0005
patchToPpuAddr := $0014
rng_seed := $0017
spawnID := $0019
spawnCount := $001A
verticalBlankingInterval:= $0033
unused_0E := $0034 ; Always $0E
.if NWC = 1
tetriminoX := $0060
tetriminoY := $0061
currentPiece := $0062
levelNumber := $0064
fallTimer := $0065
autorepeatX := $0066
startLevel := $0067
playState := $0068
vramRow := $0069
completedRow := $006A
autorepeatY := $006E
holdDownPoints := $006F
lines := $0070
rowY := $0072
score := $0073
completedLines := $0076
lineIndex := $0077
curtainRow := $0078
startHeight := $0079
garbageHole := $007A
.else
tetriminoX := $0040 ; Player data is $20 in size. It is copied here from $60 or $80, processed, then copied back
tetriminoY := $0041
currentPiece := $0042 ; Current piece as an orientation ID
levelNumber := $0044
fallTimer := $0045
autorepeatX := $0046
startLevel := $0047
playState := $0048
vramRow := $0049 ; Next playfield row to copy. Set to $20 when playfield copy is complete
completedRow := $004A ; Row which has been cleared. 0 if none complete
autorepeatY := $004E
holdDownPoints := $004F
lines := $0050
rowY := $0052
score := $0053
completedLines := $0056
lineIndex := $0057 ; Iteration count of playState_checkForCompletedRows
curtainRow := $0058
startHeight := $0059
garbageHole := $005A ; Position of hole in received garbage
.endif
player1_tetriminoX:= $0060
player1_tetriminoY:= $0061
player1_currentPiece:= $0062
player1_levelNumber:= $0064
player1_fallTimer:= $0065
player1_autorepeatX:= $0066
player1_startLevel:= $0067
player1_playState:= $0068
player1_vramRow := $0069
player1_completedRow:= $006A
player1_autorepeatY:= $006E
player1_holdDownPoints:= $006F
player1_lines := $0070
player1_rowY := $0072
player1_score := $0073
player1_completedLines:= $0076
player1_lineIndex := $0077
player1_curtainRow:= $0078
player1_startHeight:= $0079
player1_garbageHole:= $007A
player2_tetriminoX:= $0080
player2_tetriminoY:= $0081
player2_currentPiece:= $0082
player2_levelNumber:= $0084
player2_fallTimer:= $0085
player2_autorepeatX:= $0086
player2_startLevel:= $0087
player2_playState:= $0088
player2_vramRow := $0089
player2_completedRow:= $008A
player2_autorepeatY:= $008E
player2_holdDownPoints:= $008F
player2_lines := $0090
player2_rowY := $0092
player2_score := $0093
player2_completedLines:= $0096
player2_curtainRow:= $0098
player2_startHeight:= $0099
player2_garbageHole:= $009A
spriteXOffset := $00A0
spriteYOffset := $00A1
spriteIndexInOamContentLookup:= $00A2
outOfDateRenderFlags:= $00A3 ; Bit 0-lines 1-level 2-score 6-stats 7-high score entry letter
twoPlayerPieceDelayCounter:= $00A4 ; 0 is not delaying
twoPlayerPieceDelayPlayer:= $00A5
twoPlayerPieceDelayPiece:= $00A6 ; The future value of nextPiece, once the delay completes
gameModeState := $00A7 ; For values, see playState_checkForCompletedRows
generalCounter := $00A8 ; canon is legalScreenCounter2
generalCounter2 := $00A9
generalCounter3 := $00AA
generalCounter4 := $00AB
generalCounter5 := $00AC
selectingLevelOrHeight:= $00AD ; 0-level, 1-height
originalY := $00AE
dropSpeed := $00AF
tmpCurrentPiece := $00B0 ; Only used as a temporary
frameCounter := $00B1
oamStagingLength:= $00B3
newlyPressedButtons:= $00B5 ; Active player's buttons
heldButtons := $00B6 ; Active player's buttons
activePlayer := $00B7 ; Which player is being processed (data in $40)
playfieldAddr := $00B8 ; HI byte is leftPlayfield in canon. Current playfield being processed: $0400 (left; 1st player) or $0500 (right; 2nd player)
allegro := $00BA
pendingGarbage := $00BB ; Garbage waiting to be delivered to the current player. This is exchanged with pendingGarbageInactivePlayer when swapping players.
pendingGarbageInactivePlayer := $00BC ; canon is totalGarbage
renderMode := $00BD
numberOfPlayers := $00BE
nextPiece := $00BF ; Stored by its orientation ID
gameMode := $00C0 ; 0=legal, 1=title, 2=type menu, 3=level menu, 4=play and ending and high score, 5=demo, 6=start demo
gameType := $00C1 ; A=0, B=1
musicType := $00C2 ; 0-3; 3 is off
sleepCounter := $00C3 ; canon is legalScreenCounter1
ending := $00C4
ending_customVars:= $00C5 ; Different usages depending on Type A and B and Type B concert
ending_currentSprite:= $00CC
ending_typeBCathedralFrameDelayCounter:= $00CD
demo_heldButtons:= $00CE
demo_repeats := $00CF
demoButtonsAddr := $00D1 ; Current address within demoButtonsTable
demoIndex := $00D3
highScoreEntryNameOffsetForLetter:= $00D4 ; Relative to current row
highScoreEntryRawPos:= $00D5 ; High score position 0=1st type A, 1=2nd... 4=1st type B... 7=4th/extra type B
highScoreEntryNameOffsetForRow:= $00D6 ; Relative to start of table
highScoreEntryCurrentLetter:= $00D7
lineClearStatsByType:= $00D8 ; bcd. one entry for each of single, double, triple, tetris
totalScore := $00DC
displayNextPiece:= $00DF
AUDIOTMP1 := $00E0
AUDIOTMP2 := $00E1
AUDIOTMP3 := $00E2
AUDIOTMP4 := $00E3
AUDIOTMP5 := $00E4
musicChanTmpAddr:= $00E6
music_unused2 := $00EA ; Always 0
soundRngSeed := $00EB ; Set, but not read
currentSoundEffectSlot:= $00ED ; Temporary
musicChannelOffset:= $00EE ; Temporary. Added to $4000-3 for MMIO
currentAudioSlot:= $00EF ; Temporary
unreferenced_buttonMirror := $00F1 ; Mirror of $F5-F8
newlyPressedButtons_player1:= $00F5 ; $80-a $40-b $20-select $10-start $08-up $04-down $02-left $01-right
newlyPressedButtons_player2:= $00F6
heldButtons_player1:= $00F7
heldButtons_player2:= $00F8
joy1Location := $00FB ; normal=0; 1 or 3 for expansion
ppuScrollY := $00FC ; Set to 0 many places, but not read
ppuScrollX := $00FD ; Set to 0 many places, but not read
currentPpuMask := $00FE
currentPpuCtrl := $00FF
stack := $0100
oamStaging := $0200 ; format: https://wiki.nesdev.com/w/index.php/PPU_programmer_reference#OAM
statsByType := $03F0
playfield := $0400
playfieldForSecondPlayer:= $0500
musicStagingSq1Lo:= $0680
musicStagingSq1Hi:= $0681
audioInitialized:= $0682
musicPauseSoundEffectLengthCounter:= $0683
musicStagingSq2Lo:= $0684
musicStagingSq2Hi:= $0685
musicStagingTriLo:= $0688
musicStagingTriHi:= $0689
resetSq12ForMusic:= $068A ; 0-off. 1-sq1. 2-sq1 and sq2
musicPauseSoundEffectCounter:= $068B
musicStagingNoiseLo:= $068C
musicStagingNoiseHi:= $068D
musicDataNoteTableOffset:= $0690 ; AKA start of musicData, of size $0A
musicDataDurationTableOffset:= $0691
musicDataChanPtr:= $0692
musicChanControl:= $069A ; high 3 bits are for LO offset behavior. Low 5 bits index into musicChanVolControlTable, minus 1. Technically size 4, but usages of the next variable 'cheat' since that variable's first index is unused
musicChanVolume := $069D ; Must not use first index. First and second index are unused. High nibble always used; low nibble may be used depending on control and frame
musicDataChanPtrDeref:= $06A0 ; deref'd musicDataChanPtr+musicDataChanPtrOff
musicDataChanPtrOff:= $06A8
musicDataChanInstructionOffset:= $06AC
musicDataChanInstructionOffsetBackup:= $06B0
musicChanNoteDurationRemaining:= $06B4
musicChanNoteDuration:= $06B8
musicChanProgLoopCounter:= $06BC ; As driven by bytecode instructions
musicStagingSq1Sweep:= $06C0 ; Used as if size 4, but since Tri/Noise does nothing when written for sweep, the other two entries can have any value without changing behavior
musicChanNote:= $06C3
musicChanInhibit:= $06C8 ; Always zero
musicTrack_dec := $06CC ; $00-$09
musicChanVolFrameCounter:= $06CD ; Pos 0/1 are unused
musicChanLoFrameCounter:= $06D1 ; Pos 3 unused
soundEffectSlot0FrameCount:= $06D5 ; Number of frames
soundEffectSlot0FrameCounter:= $06DA ; Current frame
soundEffectSlot0SecondaryCounter:= $06DF ; nibble index into noiselo_/noisevol_table
soundEffectSlot1SecondaryCounter:= $06E0
soundEffectSlot2SecondaryCounter:= $06E1
soundEffectSlot3SecondaryCounter:= $06E2
soundEffectSlot0TertiaryCounter:= $06E3
soundEffectSlot1TertiaryCounter:= $06E4
soundEffectSlot2TertiaryCounter:= $06E5
soundEffectSlot3TertiaryCounter:= $06E6
soundEffectSlot0Tmp:= $06E7
soundEffectSlot1Tmp:= $06E8
soundEffectSlot2Tmp:= $06E9
soundEffectSlot3Tmp:= $06EA
soundEffectSlot0Init:= $06F0 ; NOISE sound effect. 2-game over curtain. 3-ending rocket. For mapping, see soundEffectSlot0Init_table
soundEffectSlot1Init:= $06F1 ; SQ1 sound effect. Menu, move, rotate, clear sound effects. For mapping, see soundEffectSlot1Init_table
soundEffectSlot2Init:= $06F2 ; SQ2 sound effect. For mapping, see soundEffectSlot2Init_table
soundEffectSlot3Init:= $06F3 ; TRI sound effect. For mapping, see soundEffectSlot3Init_table
soundEffectSlot4Init:= $06F4 ; Unused. Assume meant for DMC sound effect. Uses some data from slot 2
musicTrack := $06F5 ; $FF turns off music. $00 continues selection. $01-$0A for new selection
soundEffectSlot0Playing:= $06F8 ; Used if init is zero
soundEffectSlot1Playing:= $06F9
soundEffectSlot2Playing:= $06FA
soundEffectSlot3Playing:= $06FB
soundEffectSlot4Playing:= $06FC
currentlyPlayingMusicTrack:= $06FD ; Copied from musicTrack
unreferenced_soundRngTmp:= $06FF
highScoreNames := $0700
highScoreScoresA:= $0730
highScoreScoresB:= $073C
highScoreLevels := $0748
initMagic := $0750 ; Initialized to a hard-coded number. When resetting, if not correct number then it knows this is a cold boot
.feature force_range ; allows -1 vs <-1 (used in orientationTable)
.segment "PRG_chunk1": absolute
; incremented to reset MMC1 reg
initRam:ldx #$00
jmp initRamContinued
nmi: pha
txa
pha
tya
pha
lda #$00
sta oamStagingLength
jsr render
dec sleepCounter
lda sleepCounter
cmp #$FF
bne @jumpOverIncrement
inc sleepCounter
@jumpOverIncrement:
jsr copyOamStagingToOam
lda frameCounter
clc
adc #$01
sta frameCounter
lda #$00
adc frameCounter+1
sta frameCounter+1
.if NWC <> 1
ldx #rng_seed
ldy #$02
jsr generateNextPseudorandomNumber
.endif
lda #$00
sta ppuScrollX
sta PPUSCROLL
sta ppuScrollY
sta PPUSCROLL
lda #$01
sta verticalBlankingInterval
jsr pollControllerButtons
pla
tay
pla
tax
pla
irq: rti
render: lda renderMode
jsr switch_s_plus_2a
.addr render_mode_legal_and_title_screens
.addr render_mode_menu_screens
.addr render_mode_congratulations_screen
.addr render_mode_play_and_demo
.addr render_mode_ending_animation
initRamContinued:
ldy #$06
sty tmp2
ldy #$00
sty tmp1
lda #$00
@zeroOutPages:
sta (tmp1),y
dey
bne @zeroOutPages
dec tmp2
bpl @zeroOutPages
lda initMagic
cmp #$12
bne @initHighScoreTable
lda initMagic+1
cmp #$34
bne @initHighScoreTable
lda initMagic+2
cmp #$56
bne @initHighScoreTable
lda initMagic+3
cmp #$78
bne @initHighScoreTable
lda initMagic+4
cmp #$9A
bne @initHighScoreTable
jmp @continueWarmBootInit
ldx #$00
; Only run on cold boot
@initHighScoreTable:
lda defaultHighScoresTable,x
cmp #$FF
beq @continueColdBootInit
sta highScoreNames,x
inx
jmp @initHighScoreTable
@continueColdBootInit:
lda #$12
sta initMagic
lda #$34
sta initMagic+1
lda #$56
sta initMagic+2
lda #$78
sta initMagic+3
lda #$9A
sta initMagic+4
@continueWarmBootInit:
ldx #$89
stx rng_seed
dex
stx rng_seed+1
ldy #$00
sty ppuScrollX
sty PPUSCROLL
ldy #$00
sty ppuScrollY
sty PPUSCROLL
.if NWC = 1
lda #$80
.else
lda #$90
.endif
sta currentPpuCtrl
sta PPUCTRL
lda #$06
sta PPUMASK
.if NWC = 1
sta currentPpuMask
.endif
jsr LE006
jsr updateAudio2
lda #$C0
sta stack
lda #$80
sta stack+1
lda #$35
sta stack+3
lda #$AC
sta stack+4
jsr updateAudioWaitForNmiAndDisablePpuRendering
jsr disableNmi
lda #$20
jsr LAA82
lda #$24
jsr LAA82
lda #$28
jsr LAA82
lda #$2C
jsr LAA82
lda #tileEmpty
ldx #>playfield
ldy #>playfieldForSecondPlayer
jsr memset_page
jsr waitForVBlankAndEnableNmi
jsr updateAudioWaitForNmiAndResetOamStaging
jsr updateAudioWaitForNmiAndEnablePpuRendering
jsr updateAudioWaitForNmiAndResetOamStaging
lda #$0E
sta unused_0E
lda #$00
sta gameModeState
sta gameMode
lda #$01
sta numberOfPlayers
lda #$00
sta frameCounter+1
@mainLoop:
jsr branchOnGameMode
cmp gameModeState
bne @checkForDemoDataExhaustion
jsr updateAudioWaitForNmiAndResetOamStaging
@checkForDemoDataExhaustion:
lda gameMode
cmp #$05
bne @continue
lda demoButtonsAddr+1
cmp #>demoTetriminoTypeTable
bne @continue
lda #>demoButtonsTable
sta demoButtonsAddr+1
lda #$00
sta frameCounter+1
lda #$01
sta gameMode
@continue:
jmp @mainLoop
gameMode_playAndEndingHighScore_jmp:
jsr gameMode_playAndEndingHighScore
rts
branchOnGameMode:
lda gameMode
jsr switch_s_plus_2a
.addr gameMode_legalScreen
.addr gameMode_titleScreen
.addr gameMode_gameTypeMenu
.addr gameMode_levelMenu
.addr gameMode_playAndEndingHighScore_jmp
.addr gameMode_playAndEndingHighScore_jmp
.addr gameMode_startDemo
gameModeState_updatePlayer1:
jsr makePlayer1Active
jsr branchOnPlayStatePlayer1
jsr stageSpriteForCurrentPiece
jsr savePlayer1State
jsr stageSpriteForNextPiece
inc gameModeState
rts
gameModeState_updatePlayer2:
lda numberOfPlayers
cmp #$02
bne @ret
jsr makePlayer2Active
jsr branchOnPlayStatePlayer2
jsr stageSpriteForCurrentPiece
jsr savePlayer2State
@ret: inc gameModeState
rts
gameMode_playAndEndingHighScore:
lda gameModeState
jsr switch_s_plus_2a
.addr gameModeState_initGameBackground
.addr gameModeState_initGameState
.addr gameModeState_updateCountersAndNonPlayerState
.addr gameModeState_handleGameOver
.addr gameModeState_updatePlayer1
.addr gameModeState_updatePlayer2
.addr gameModeState_checkForResetKeyCombo
.addr gameModeState_startButtonHandling
.addr gameModeState_vblankThenRunState2
branchOnPlayStatePlayer1:
lda playState
jsr switch_s_plus_2a
.addr playState_unassignOrientationId
.addr playState_playerControlsActiveTetrimino
.addr playState_lockTetrimino
.addr playState_checkForCompletedRows
.addr playState_noop
.addr playState_updateLinesAndStatistics
.addr playState_bTypeGoalCheck
.addr playState_receiveGarbage
.addr playState_spawnNextTetrimino
.addr playState_noop
.addr playState_updateGameOverCurtain
.addr playState_incrementPlayState
playState_playerControlsActiveTetrimino:
jsr shift_tetrimino
jsr rotate_tetrimino
jsr drop_tetrimino
rts
branchOnPlayStatePlayer2:
lda playState
jsr switch_s_plus_2a
.addr playState_unassignOrientationId
.addr playState_player2ControlsActiveTetrimino
.addr playState_lockTetrimino
.addr playState_checkForCompletedRows
.addr playState_noop
.addr playState_updateLinesAndStatistics
.addr playState_bTypeGoalCheck
.addr playState_receiveGarbage
.addr playState_spawnNextTetrimino
.addr playState_noop
.addr playState_updateGameOverCurtain
.addr playState_incrementPlayState
playState_player2ControlsActiveTetrimino:
jsr shift_tetrimino
jsr rotate_tetrimino
jsr drop_tetrimino
rts
gameMode_legalScreen:
jsr updateAudio2
lda #$00
sta renderMode
.if NWC = 1
lda #$FF
.else
jsr updateAudioWaitForNmiAndDisablePpuRendering
jsr disableNmi
lda #CHR_TITLE_MENU
jsr changeCHRBank0
lda #CHR_TITLE_MENU
jsr changeCHRBank1
jsr bulkCopyToPpu
.addr legal_screen_palette
jsr bulkCopyToPpu
.addr legal_screen_nametable
jsr waitForVBlankAndEnableNmi
jsr updateAudioWaitForNmiAndResetOamStaging
jsr updateAudioWaitForNmiAndEnablePpuRendering
jsr updateAudioWaitForNmiAndResetOamStaging
lda #$00
.endif
ldx #>oamStaging
ldy #>oamStaging
jsr memset_page
.if NWC <> 1
lda #LEGAL_SLEEP_TIME
jsr sleep_for_a_vblanks
lda #LEGAL_SLEEP_TIME
sta generalCounter
@waitForStartButton:
lda newlyPressedButtons_player1
cmp #BUTTON_START
beq @continueToNextScreen
jsr updateAudioWaitForNmiAndResetOamStaging
dec generalCounter
bne @waitForStartButton
@continueToNextScreen:
.endif
inc gameMode
rts
gameMode_titleScreen:
jsr updateAudio2
lda #$00
sta renderMode
sta $D0
sta displayNextPiece
jsr updateAudioWaitForNmiAndDisablePpuRendering
jsr disableNmi
.if NWC = 1
lda #$80
sta PPUCTRL
sta currentPpuCtrl
.else
lda #CHR_TITLE_MENU
jsr changeCHRBank0
lda #CHR_TITLE_MENU
jsr changeCHRBank1
.endif
jsr bulkCopyToPpu
.addr menu_palette
jsr bulkCopyToPpu
.addr title_screen_nametable
.if NWC = 1
lda $7005
sta generalCounter
@shuffleSeed:
ldx #rng_seed
ldy #$02
jsr generateNextPseudorandomNumber
dec generalCounter
bne @shuffleSeed
.endif
jsr waitForVBlankAndEnableNmi
jsr updateAudioWaitForNmiAndResetOamStaging
jsr updateAudioWaitForNmiAndEnablePpuRendering
jsr updateAudioWaitForNmiAndResetOamStaging
.if NWC = 1
lda #$FF
.else
lda #$00
.endif
ldx #>oamStaging
ldy #>oamStaging
jsr memset_page
lda #$00
sta frameCounter+1
.if NWC = 1
lda #$80
jsr sleep_for_a_vblanks
.else
@waitForStartButton:
jsr updateAudioWaitForNmiAndResetOamStaging
lda newlyPressedButtons_player1
cmp #BUTTON_START
beq @startButtonPressed
lda frameCounter+1
cmp #$05
beq @timeout
jmp @waitForStartButton
; Show menu screens
@startButtonPressed:
lda #$02
sta soundEffectSlot1Init
.endif
inc gameMode
rts
.if NWC <> 1
; Start demo
@timeout:
lda #$02
sta soundEffectSlot1Init
lda #$06
sta gameMode
rts
.endif
render_mode_legal_and_title_screens:
lda currentPpuCtrl
and #$FC
sta currentPpuCtrl
.if NWC <> 1
lda #$00
sta ppuScrollX
sta PPUSCROLL
sta ppuScrollY
sta PPUSCROLL
rts
lda #$00
sta player1_levelNumber
lda #$00
sta gameType
lda #$04
lda gameMode
.endif
rts
gameMode_gameTypeMenu:
.if NWC <> 1
inc initRam
lda #MMC1_4KCHR_32KPRG_H_MIRROR
jsr setMMC1Control
.endif
lda #$01
sta renderMode
.if NWC <> 1
jsr updateAudioWaitForNmiAndDisablePpuRendering
jsr disableNmi
jsr bulkCopyToPpu
.addr menu_palette
jsr bulkCopyToPpu
.addr game_type_menu_nametable
lda #CHR_TITLE_MENU
jsr changeCHRBank0
lda #CHR_TITLE_MENU
jsr changeCHRBank1
jsr waitForVBlankAndEnableNmi
jsr updateAudioWaitForNmiAndResetOamStaging
jsr updateAudioWaitForNmiAndEnablePpuRendering
jsr updateAudioWaitForNmiAndResetOamStaging
ldx musicType
lda musicSelectionTable,x
jsr setMusicTrack
L830B: lda #$FF
ldx #>oamStaging
ldy #>oamStaging
jsr memset_page
lda newlyPressedButtons_player1
cmp #BUTTON_RIGHT
bne @rightNotPressed
lda #$01
sta gameType
lda #$01
sta soundEffectSlot1Init
jmp @leftNotPressed
@rightNotPressed:
lda newlyPressedButtons_player1
cmp #BUTTON_LEFT
bne @leftNotPressed
lda #$00
sta gameType
lda #$01
sta soundEffectSlot1Init
@leftNotPressed:
lda newlyPressedButtons_player1
cmp #BUTTON_DOWN
bne @downNotPressed
lda #$01
sta soundEffectSlot1Init
lda musicType
cmp #$03
beq @upNotPressed
inc musicType
ldx musicType
lda musicSelectionTable,x
jsr setMusicTrack
@downNotPressed:
lda newlyPressedButtons_player1
cmp #BUTTON_UP
bne @upNotPressed
lda #$01
sta soundEffectSlot1Init
lda musicType
beq @upNotPressed
dec musicType
ldx musicType
lda musicSelectionTable,x
jsr setMusicTrack
@upNotPressed:
lda newlyPressedButtons_player1
cmp #BUTTON_START
bne @startNotPressed
lda #$02
sta soundEffectSlot1Init
.endif
inc gameMode
rts
.if NWC <> 1
@startNotPressed:
lda newlyPressedButtons_player1
cmp #BUTTON_B
bne @bNotPressed
lda #$02
sta soundEffectSlot1Init
lda #$00
sta frameCounter+1
dec gameMode
rts
@bNotPressed:
ldy #$00
lda gameType
asl a
sta generalCounter
asl a
adc generalCounter
asl a
asl a
asl a
asl a
clc
adc #$3F
sta spriteXOffset
lda #$3F
sta spriteYOffset
lda #$01
sta spriteIndexInOamContentLookup
lda frameCounter
and #MENU_CURSOR_MASK
bne @flickerCursorPair1
lda #$02
sta spriteIndexInOamContentLookup
@flickerCursorPair1:
jsr loadSpriteIntoOamStaging
lda musicType
asl a
asl a
asl a
asl a
clc
adc #$8F
sta spriteYOffset
lda #$53
sta spriteIndexInOamContentLookup
lda #$67
sta spriteXOffset
lda frameCounter
and #MENU_CURSOR_MASK
bne @flickerCursorPair2
lda #$02
sta spriteIndexInOamContentLookup
@flickerCursorPair2:
jsr loadSpriteIntoOamStaging
jsr updateAudioWaitForNmiAndResetOamStaging
jmp L830B
.endif
gameMode_levelMenu:
.if NWC <> 1
inc initRam
lda #MMC1_4KCHR_32KPRG_H_MIRROR
jsr setMMC1Control
jsr updateAudio2
.endif
lda #$01
sta renderMode
.if NWC <> 1
jsr updateAudioWaitForNmiAndDisablePpuRendering
jsr disableNmi
lda #CHR_TITLE_MENU
jsr changeCHRBank0
lda #CHR_TITLE_MENU
jsr changeCHRBank1
jsr bulkCopyToPpu
.addr menu_palette
jsr bulkCopyToPpu
.addr level_menu_nametable
lda gameType
bne @skipTypeBHeightDisplay
jsr bulkCopyToPpu
.addr height_menu_nametablepalette_patch
@skipTypeBHeightDisplay:
jsr showHighScores
jsr waitForVBlankAndEnableNmi
jsr updateAudioWaitForNmiAndResetOamStaging
lda #$00
sta PPUSCROLL
lda #$00
sta PPUSCROLL
jsr updateAudioWaitForNmiAndEnablePpuRendering
jsr updateAudioWaitForNmiAndResetOamStaging
lda #$00
sta originalY
sta dropSpeed
@forceStartLevelToRange:
lda player1_startLevel
cmp #$0A
bcc gameMode_levelMenu_processPlayer1Navigation
sec
sbc #$0A
sta player1_startLevel
jmp @forceStartLevelToRange
gameMode_levelMenu_processPlayer1Navigation:
.endif
lda #$00
sta activePlayer
lda player1_startLevel
sta startLevel
lda player1_startHeight
sta startHeight
lda originalY
sta selectingLevelOrHeight
lda newlyPressedButtons_player1
sta newlyPressedButtons
.if NWC <> 1
jsr gameMode_levelMenu_handleLevelHeightNavigation
.endif
lda startLevel
sta player1_startLevel
lda startHeight
sta player1_startHeight
lda selectingLevelOrHeight
sta originalY
.if NWC <> 1
lda newlyPressedButtons_player1
cmp #BUTTON_START
bne @checkBPressed
lda heldButtons_player1
cmp #BUTTON_A+BUTTON_START
bne @startAndANotPressed
lda player1_startLevel
clc
adc #$0A
sta player1_startLevel
@startAndANotPressed:
.endif
lda #$00
sta gameModeState
.if NWC <> 1
lda #$02
sta soundEffectSlot1Init
.endif
inc gameMode
rts
.if NWC <> 1
@checkBPressed:
lda newlyPressedButtons_player1
cmp #BUTTON_B
bne @chooseRandomHole_player1
lda #$02
sta soundEffectSlot1Init
dec gameMode
rts
@chooseRandomHole_player1:
ldx #rng_seed
ldy #$02
jsr generateNextPseudorandomNumber
lda rng_seed
and #$0F
cmp #$0A
bpl @chooseRandomHole_player1
sta player1_garbageHole
@chooseRandomHole_player2:
ldx #rng_seed
ldy #$02
jsr generateNextPseudorandomNumber
lda rng_seed
and #$0F
cmp #$0A
bpl @chooseRandomHole_player2
sta player2_garbageHole
jsr updateAudioWaitForNmiAndResetOamStaging
jmp gameMode_levelMenu_processPlayer1Navigation
; Starts by checking if right pressed
gameMode_levelMenu_handleLevelHeightNavigation:
lda newlyPressedButtons
cmp #BUTTON_RIGHT
bne @checkLeftPressed
lda #$01
sta soundEffectSlot1Init
lda selectingLevelOrHeight
bne @rightPressedForHeightSelection
lda startLevel
cmp #$09
beq @checkLeftPressed
inc startLevel
jmp @checkLeftPressed
@rightPressedForHeightSelection:
lda startHeight
cmp #$05
beq @checkLeftPressed
inc startHeight
@checkLeftPressed:
lda newlyPressedButtons
cmp #BUTTON_LEFT
bne @checkDownPressed
lda #$01
sta soundEffectSlot1Init
lda selectingLevelOrHeight
bne @leftPressedForHeightSelection
lda startLevel
beq @checkDownPressed
dec startLevel
jmp @checkDownPressed
@leftPressedForHeightSelection:
lda startHeight
beq @checkDownPressed
dec startHeight
@checkDownPressed:
lda newlyPressedButtons
cmp #BUTTON_DOWN
bne @checkUpPressed
lda #$01
sta soundEffectSlot1Init
lda selectingLevelOrHeight
bne @downPressedForHeightSelection
lda startLevel
cmp #$05
bpl @checkUpPressed
clc
adc #$05
sta startLevel
jmp @checkUpPressed
@downPressedForHeightSelection:
lda startHeight
cmp #$03
bpl @checkUpPressed
inc startHeight
inc startHeight
inc startHeight
@checkUpPressed:
lda newlyPressedButtons
cmp #BUTTON_UP
bne @checkAPressed
lda #$01
sta soundEffectSlot1Init
lda selectingLevelOrHeight
bne @upPressedForHeightSelection
lda startLevel
cmp #$05
bmi @checkAPressed
sec
sbc #$05
sta startLevel
jmp @checkAPressed
@upPressedForHeightSelection:
lda startHeight
cmp #$03
bmi @checkAPressed
dec startHeight
dec startHeight