-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.asm
1941 lines (1738 loc) · 43.2 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
processor 6502
include "include/vcs.h"
include "include/macro.h"
; TODO Implement Bird AI and gamemode where p2 can be Bird
; implement collision
; implement room loading where each transition takes you to a different room
; implement level counter
; implement lives counter
; implement room amount to ocean based on level
; implement way for turtle to shoot in the direction pressed
; when bird is hit it loses control for a few frames and is pushed back
; collision detection based on just a few sample pixels. edges and center of edges?
; and the current x and y position
; each ball collection will reduce score by 1, when score is 0 -> go to next map and reduce lvl counter
; load a new random map
; maybe even randomize each part of the map?
; at a certain level make it so that bird duplicates
; TODO fix ball
; make ball change positon after 10 seconds
PAL = 0
NTSC = 1
SECAM = 2
SYSTEM = NTSC ; change this to PAL or NTSC or SECAM
; ---------- Variables
SEG.U vars
ORG $80 ; start of RAM
Framecount ds 1 ; animation counter location
Score ds 1 ; holdds 2 digit score, sotred as BCD
Timer ds 1 ;
Level: ds 1 ; level counter
Lives: ds 1 ; live counter
; digit graphic data
DigitOnes ds 3 ; DigitiOnes = score, DigitOnes+1 = timer DigitOnes+2 = level
DigitTens ds 3
; graphic data to be put into PF1
ScoreGfx ds 1
TimerGfx ds 1
Temp ds 4 ; 4 bytes of temp storage
; object X positions in $89-8C
ObjectX: ds 5 ; player0, player1, missile0, missile1, ball
; object Y positions in $8D-90
ObjectY: ds 5 ; player0, player1, missile0, missile1, ball
; DoDraw storage in $91-92
TurtleDraw: ds 1 ; used for drawing player0
BirdDraw: ds 1 ; used for drawing player1
M0Draw: ds 1
; DoDraw Graphic Pointer in $93-94
TurtlePtr: ds 2 ; used for drawing player0
BirdPtr: ds 2 ; used for drawing player1
; used for Bird AI to keep track of movement
BirdAICounter ds 1
BirdLeftRightStatus ds 1
BirdUpDownStatus ds 1
; map pointer used for map rendering
MapPtr0: ds 2 ; used for drawing map
MapPtr1: ds 2 ; used for drawing map
MapPtr2: ds 2 ; used for drawing map
; map counter for each PF, currently all of those are the same
CurrentMap: ds 3 ; map counter - must increment by 6 for new map to fully load
RandMap: ds 1 ; map timer, increments by one each frame until MAPCOUNT is reached
; then resets to 0. used to get a random map
MapsCleared: ds 1 ; amount of clreaded maps this level
AnimationTimer ds 4 ; animation timer for p0, p1, m0, m1
PreviousX ds 4
PreviousY ds 4
M0RespawnTimer ds 1 ; ball will change location after x secnds (each time Framecount rolls over this is dec)
GameState ds 1 ; gamestate. 0 = playing 1 = intro 2 = ocean reached animation
ColourCycle ds 1 ; used for pause screen
; Music Pointers
; these point to memory locations that are to be played when music is on
; music should be input in reverse order because of the way the counter works!
SoundEnabled ds 1 ; set to how many frames are to be played
SoundEnabled2 ds 1
SoundTrackPtr ds 2 ; points to sound to be played
SoundTrackPtr2 ds 2 ; points to sound to be played
SoundSpeed ds 1 ; speed of sound
SoundControl ds 2 ; sound control
SoundControl2 ds 2 ; sound control
; used by Random for an 8 bit random number
Rand8 ds 1
; ---------- Constants
SEG.U constants
TIMETOCHANGE = 20 ; speed of animation
; height of the arena (gameplay area). Since we're using a 2 line kernel,
; actual height will be twice this. Also, we're using 0-89 for the
; scanlines so actual height is 180 = 90*2
PFHEIGHT = 89 ; 180 scanlines of playfield h
NULL = 0 ; just 0
P0STARTX = $4D
P0STARTY = $32
P1STARTX = 0
P1STARTY = 0
M0HEIGHT = 4
M0RESPAWNT = 255
MAPCOUNT = 10
OFFSETPERMAP = 6
; music volumes
; music speed
;S_FULLSPEED = 0 ; no delay at all
;S_HALFSPEED = $1 ; and fore very other frame
;S_QUARTERSPEED = %11111 ; and for every 4th frame
;===============================================================================
; Define Start of Cartridge
;===============================================================================
SEG CODE ; define code segment
; ----------
ORG $F800 ; 4k carts start at $F000
; Init, run once only!
Start
Clear
; clear all ram and registers
ldx #0
lda #0
; CLEAN_START is a macro found in macro.h
; it sets all RAM, TIA registers and CPU registers to 0
CLEAN_START
Reset
; seed the random number generator
lda INTIM ; unknown value
sta Rand8 ; use as seed
eor #$FF ; both seed values cannot be 0, so flip the bits
; set up srptie pos
ldx #0
; reset lives, map and level to 0
stx CurrentMap
jsr NextMap
jsr NextLevel
ldx #2
stx Lives ; 2 lives
jsr ResetPPositions
jsr SetM0Pos
ldx #1 ; only set intro state here
stx GameState
StartOfFrame
; start of new frame
inc Framecount
ldx RandMap
inx
cpx #MAPCOUNT+1
bne MapCountRandNotReached
ldx #0
MapCountRandNotReached
stx RandMap
; compare Framecount to 0
ldx Framecount
cpx #0
bne FramesNotRolledOver
dec M0RespawnTimer ; dec this if rolled over
; compare to 0
ldx M0RespawnTimer
cpx #0
bne FramesNotRolledOver
; reset ball
jsr SetM0Pos
FramesNotRolledOver
jsr VerticalSync
jsr VerticalBlank
jsr Picture
jsr Overscan
jmp StartOfFrame
VerticalSync
; start vblank processing
lda #2
ldx #49
sta WSYNC
sta VSYNC
stx TIM64T ; set timer to go off in 41 scanlines (49 * 64) / 76
sta CTRLPF ; D1=1, playfield now in SCORE mode
sta WSYNC
sta WSYNC ; 3 scanlines of WSYNC
lda #0
sta PF0
sta PF1
sta GRP0
sta GRP1
sta GRP0
sta WSYNC
sta VSYNC
lda #%00100000 ;
sta NUSIZ0 ; set missile0 to be 2x
; if the level is greater than 0x10 doulbe the bird
ldx Level
cpx #$10
beq DoubleBird
cpx #$FF
beq TrippleBird
jmp BirdDifficultyDone
DoubleBird
lda #%00000001 ; 2 closely rendered copies
sta NUSIZ1
jmp BirdDifficultyDone
TrippleBird
lda #%00000011 ; 3 closely rendered copies
sta NUSIZ1
jmp BirdDifficultyDone
BirdDifficultyDone
Sleep12 ; jsr here to sleep for 12 cycles
rts
VerticalBlank
jsr Random ; just call this to seed
; game logic call
jsr ProcessJoystick
jsr GameProgress
jsr PositionObjects
ldx #1
stx Temp+3 ; score colours
jsr SetObjectColours
jsr PrepScoreForDisplay
ldx #0 ; channel 1
jsr SoundHandle
ldx #1 ; channel 2
jsr SoundHandle
rts
Picture
; turn on display
sta WSYNC ; Wait for SYNC (halts CPU until end of scanline)
lda INTIM ; check vertical sync timer
bne Picture
sta VBLANK ; turn on the display
; check for gamestate 2
ldx GameState
cpx #2 ; level clear
bne GameStateNot2Picture
; draw 197 lines
ldx #192
GameStateLevelClearLoop
dex
sta WSYNC
bne GameStateLevelClearLoop
ldx Temp+1
dex
bne GameState2NotDone
; set gamestate to 0 again
ldx #0
stx GameState
GameState2NotDone
stx Temp+1
rts
GameStateNot2Picture
; draw score 5 lines
ldx #5
scoreLoop
ldy DigitTens ; get the tens digit offset for the score
lda DigitGfx,y
and #$F0 ; remove the graphics for the ones digit
sta ScoreGfx ; and save it
ldy DigitOnes ; get the ones digit offset foir the score
lda DigitGfx,y ; use it to load the digit gfx
and #$0F ; remove 10s digit
ora ScoreGfx ; merge with tens digit and gfx
sta ScoreGfx ; and save it
sta WSYNC
;---------------------------------------
sta PF1 ; update pf1 for score display
ldy DigitTens+1 ; get the left digit offset
lda DigitGfx,y ; use it to load
and #$F0 ; remove the gfx for the ones digit
sta TimerGfx ; and save it
ldy DigitOnes+1 ; get the ones offset from the timer
lda DigitGfx,y ; use it to load gfx
and #$0F ; remove the gfx for the tens digit
ora TimerGfx ; merge
sta TimerGfx ; and save it
jsr Sleep12 ; waste some cycles
sta PF1 ; update playfield for timer display
ldy ScoreGfx ; preload for next line
sta WSYNC ; wait for the end of scanline
;---------------------------------------
sty PF1 ; update playfield for the score display
inc DigitTens ; advance to the next line of gfx data
inc DigitTens+1 ; advance to the next line of gfx data
inc DigitOnes ; advance to the next line of gfx data
inc DigitOnes+1 ; advance to the next line of gfx data
jsr Sleep12
dex ; decrease loop counter
sta PF1 ; update playfield
bne scoreLoop ; if dex != 0 then loop
sta WSYNC
;---------------------------------------
stx PF1 ; blank out PF1 - x must be zero here!
sta WSYNC
; restore bg colour
ldx #0
stx Temp+3 ; gameplay colours
jsr SetObjectColours
sta WSYNC ; space between score and arena
lda #1 ; 2 2
sta CTRLPF ; 3 5 - turn off SCORE mode and turn on REFLECT
; load map counter into x
ldx CurrentMap
; now we load the map ptrs
lda RoomTable,x ; store room1layout in MapPtr as a ptr
sta MapPtr0
lda RoomTable+1,x ;
sta MapPtr0+1 ; store the rest in MapPtr+1
ldx CurrentMap+1
lda RoomTable+2,x ; store room1layout in MapPtr as a ptr
sta MapPtr1
lda RoomTable+3,x ;
sta MapPtr1+1 ; store the rest in MapPtr+1
ldx CurrentMap+2
lda RoomTable+4,x ; store room1layout in MapPtr as a ptr
sta MapPtr2
lda RoomTable+5,x ;
sta MapPtr2+1 ; store the rest in MapPtr+1
; Do 192 scanlines of colour-changing (our picture)
ldy #PFHEIGHT ; draw the screen for 192 lines
ldx #0
stx Temp ; store map counter in temp
pfLoop
tya ; 2 29 - 2LK loop counter in A for testing
and #%11 ; 2 31 - test for every 4th time through the loop,
bne SkipMapCounter ; 2 33 (3 34) branch if not 4th time
inc Temp ; 2 35 - if 4th time, increase Temp so new playfield data is used
SkipMapCounter
; continuation of line 2 of the 2LK
; this precalculates data that's used on line 1 of the 2LK
lda #TURTLEHEIGHT-1 ; height of turtle sprite - 1
dcp TurtleDraw ; Decrement TurtleDraw and compare with height
bcs DoDrawGrp0 ; if carry is set then turtle is on current scanline
lda #0 ; otherwise use 0 to turn off p0
.byte $2C ; $2C = BIT with absolute addressing, trick that
; causes the lda (Turtle),y to be skipped
DoDrawGrp0 ;
lda (TurtlePtr),y ; load shape
sta WSYNC ; wait for line
sta GRP0 ; update player0 to draw turtle
; store current y in temp+1
sty Temp+1
ldy Temp ; load map counter
; draw playfield
lda (MapPtr0),y ; playfiled pattern test
sta PF0
lda (MapPtr1),y ; playfiled pattern test
sta PF1
lda (MapPtr2),y ; playfiled pattern test
sta PF2
; restore y
ldy Temp+1
; ball stuff
ldx #1 ; d1=0 so ball will be off
lda #M0HEIGHT-1 ; height of m0 gfx
dcp M0Draw ; decrement and compare
bcs DoEnableM0
.byte $24
DoEnableM0
inx ; d1=1 so ball will be on
; precalculate date for next line
lda #TURTLEHEIGHT-1 ; height of gfx
dcp BirdDraw ; decrement BirdDraw
bcs DoDrawGrp1
lda #0
.byte $2C
DoDrawGrp1
lda (BirdPtr),y
sta WSYNC
; start of line 2 of the 2LK
sta GRP1
stx ENAM0 ; enable m0
dey ; decrease the loop counter
bne pfLoop ; branch if more left to draw
lda #0
sta PF0
sta PF1
sta PF2
rts ; return
; 30 scanlines of overscan
Overscan
sta WSYNC
lda #2
sta VBLANK
lda #32 ; set time for 27 scanlines 32 = ((27 * 76) / 64)
sta TIM64T ; timer will go off after 27 scanlines
jsr CollisionDetection
lda #1
overscanLoop
sta WSYNC
lda INTIM ; check timer
bpl overscanLoop
rts
ProcessJoystick
; first we check the reset button
lda SWCHB
lsr
bcs ResetNotPressed ; if reset is hit, literally reset
jmp Start
ResetNotPressed
lsr ; D1 is now in C
bcs SelectNotPressed
ldx GameState
cpx #1
beq SelectPressedStartGame
lda #1
sta GameState ; pause game
jmp SelectNotPressed
SelectPressedStartGame
lda #0
sta GameState ; game is now running
SelectNotPressed
; then we check fire button, it will start/pause the game
ldx GameState ; load gamestate to see what is happening
cpx #0
beq JoystickPlaying ; playing input only
rts ; otherwise return now
JoystickPlaying
; now we store old x and y
ldx ObjectX
stx PreviousX
ldx ObjectY
stx PreviousY
ldx ObjectX+1
stx PreviousX+1
ldx ObjectY+1
stx PreviousY+1
; load Temp with 0 to enable collision
ldx #0
stx Temp
; game logic here
lda SWCHA ; input registr
asl ; test bit 0, left joy - right input
bcs Player1RightNotPressed ; this operation sets the carry for the fromer bit that fell off
ldy #0 ; right presses
ldx #0
jsr MoveObject
Player1RightNotPressed
asl ; test bit 1, left joy - left input
bcs Player1LeftNotPressed
ldy #0 ; left presses
ldx #1
jsr MoveObject
Player1LeftNotPressed
asl ; test bit 1, left joy - down input
bcs Player1DownNotPressed
ldy #0
ldx #2
jsr MoveObject
Player1DownNotPressed
asl ; test bit 2, left joy - up input
bcs Player1UpNotPressed
ldy #0
ldx #3
jsr MoveObject
Player1UpNotPressed
; set Temp to 1 to disable collision for bird
ldx #1
stx Temp
; check left difficulty switch
bit SWCHB ; state of Right Difficult in N (negative flag)
; state of Left Difficult in V (overflow flag)
bvc LeftIsBJoy
jsr BirdAI
rts ; Left is A, return now TODO insert AI sub call here
LeftIsBJoy
; left is b, player 2 can control the bird
asl ; test bit, right joy - right input
bcs Player2RightNotPressed
ldy #1
ldx #0
jsr MoveObject
Player2RightNotPressed
asl ; test bit 1, right joy - left input
bcs Player2LeftNotPressed
ldy #1
ldx #1
jsr MoveObject
Player2LeftNotPressed
asl ; test bit 1, right joy - down input
bcs Player2DownNotPressed
ldy #1
ldx #2
jsr MoveObject
Player2DownNotPressed
asl ; test bit 2, right joy - up input
bcs Player2UpNotPressed
ldy #1
ldx #3
jsr MoveObject
Player2UpNotPressed
rts
; this sub handles the bird AI
BirdAI
ldx #0
cpx BirdAICounter
bne MoveBirdAI ; jump to keep doing what we are doing
bit SWCHB ; only do non-random pattern on difficulty b
bmi RandomBirdAI ; means switch is A
; ldx Level ; if level is smaller than 5 random moves
; cpx #5
; bmi RandomBirdAI ; if smaller than this jmp
ldx ObjectX ; player x
cpx ObjectX+1 ; bird x
bmi MoveLeftStructuredBird
ldx #0
stx BirdLeftRightStatus
jmp LeftRightBirdAIDone
MoveLeftStructuredBird
ldx #1
stx BirdLeftRightStatus
jmp LeftRightBirdAIDone
RandomBirdAI
; first we call Random to determine which direction bird moves
jsr Random
; if random is even move left, else right
lda #1 ; bit mask
and Rand8
beq EvenLeftRightBirdAI; is even
OddLeftRightBirdAI
; odd bird AI
ldx #0
stx BirdLeftRightStatus
jmp LeftRightBirdAIDone
; even bird AI
EvenLeftRightBirdAI
ldx #1
stx BirdLeftRightStatus
LeftRightBirdAIDone
jsr Random
; if random is even move up, else down
lda #1 ; bit mask
and Rand8
beq EvenUpDownBirdAI; is even
OddUpDownBirdAI
; odd
ldx #0
stx BirdUpDownStatus
jmp UpDownBirdAIDone
; even
EvenUpDownBirdAI
ldx #1
stx BirdUpDownStatus
UpDownBirdAIDone
;lda #20 ; bird ai will follow this pattern for 20 frames
;clc
;adc Rand8
lda Rand8
clc
sbc Level ; bird pattern changes in Rand8 minus Level time
sta BirdAICounter
MoveBirdAI
dec BirdAICounter
; now we check where to move bird
ldx #1
cpx BirdUpDownStatus
beq MoveBirdUp
; odd bird AI
ldy #1
ldx #2
jsr MoveObject
jmp UpDownBirdMoveDone
MoveBirdUp
ldy #1
ldx #3
jsr MoveObject
UpDownBirdMoveDone
ldx #1
cpx BirdLeftRightStatus
beq MoveBirdLeft
; move right
ldy #1
ldx #0
jsr MoveObject
jmp leftRightBirdMoveDone
MoveBirdLeft
ldy #1
ldx #1
jsr MoveObject
leftRightBirdMoveDone
rts
GameProgress
; first we check if required maps for next level have been reached
ldx Level ; maps for level are always Level
;inx ; maps for level are always Level+1
cpx MapsCleared ; if it is the same next level
beq NextLevelProg
ldx Score
cpx #0 ; if score is 0 advance to the next stage
beq NextMapProg
jmp ProgressDone
NextMapProg
jsr NextMap
jsr ResetPPositions
jmp ProgressDone
NextLevelProg
jsr NextLevel
ProgressDone
rts
;====================
; This sub checks for collision based on inputs stored
; Expected inputs:
; y - object's address offset, p0, p1, m0, m1
; x - move left, right, up, or down
; set Temp memory address to 1 to ignore collision.
; every other value will NOT ignore collision
; Retruns: 0, 1 or 2 in x
; 0 = no collision
; 1 = wall collision
; 2 = bird collision
; 3 = missile collision
;====================
MoveObject
cpx #0
beq RightCollision
cpx #1
beq LeftCollision
cpx #2
beq UpCollision
jmp DownCollision
RightCollision
; right pressed code
ldx ObjectX,y
inx
cpx #160
bne SaveXRight ; save X if we're not at the edge
ldx #0 ; warp to other edge
SaveXRight
stx ObjectX,y
ldx #1
stx REFP0,y ; makes turtle image face right
rts;jmp MoveDone
LeftCollision
; left pressed code
ldx ObjectX,y
dex
cpx #255 ; test for edge of screen
bne SaveXLeft
ldx #159 ; warp to toher side
SaveXLeft
stx ObjectX,y
ldx #0
stx REFP0,y ; makes turtle image face left
rts;jmp MoveDone
DownCollision
; up pressed code
ldx ObjectY,y
inx
cpx #PFHEIGHT+3 ; used to be $60 - works with $FF too because this is the edge of the screen
bne SaveYUp
ldx #0
SaveYUp
stx ObjectY,y
rts;jmp MoveDone
UpCollision
; down pressed code
ldx ObjectY,y
dex
cpx #$0
bne SaveYDown
ldx #PFHEIGHT
SaveYDown
stx ObjectY,y
MoveDone
rts
;====================
; This sub checks for collision based on inputs stored
; Expected inputs:
; y = object's address offset
; Retruns: nothing
;====================
RestorePos
lda PreviousX,y
sta ObjectX,y
lda PreviousY,y
sta ObjectY,y
rts
;====================
; This sub checks for collision based on inputs stored
; Expected inputs:
; y - object's address offset, p0, p1, m0, m1
; set Temp memory address to 1 to ignore collision.
; set Temp to 2 to not restore position
; every other value will NOT ignore collision
; Retruns: 0 or 1 in Temp and Temp+1
; 0 = no collision
; 1 = collision
; returns Temp for lower bit and Temp+1 for higher bit
; those mean different things depending on the check performed
;====================
CollisionDetection
; First we check collision for p0 and pf
bit CXP0FB ; N = player0/playfield, V=player0/ball
bpl NoP0PFCollision ; if N is off, then player did not collide with playfield
ldy #0 ; 0th object is player0
jsr RestorePos
NoP0PFCollision
; now we check collision between p1 and pf only if difficulty right switch is b
bit SWCHB
bpl NoP1PFCollision ; means switch is off
; if it is on do collision
bit CXP1FB
bpl NoP1PFCollision
ldy #1 ; p1
jsr RestorePos
ldx #0
stx BirdAICounter ; make bird change position
NoP1PFCollision
; now we check collision between p0 and p1
bit CXPPMM
bpl NoP0P1Collision
dec Lives ; kill p0
lda #0 ; if lives is 0 - reset the game
cmp Lives
bne NoReset
jmp Reset
NoReset
jsr ResetPPositions
jsr SetM0Pos
ldx #0 ; first song
ldy #BIRDHITPLAYERTRACKSIZE
jsr PlaySong
NoP0P1Collision ; p0 and p1 did not collide!
; now we dio p0 m0 collision. m0 must be collected by turtle to advance
; each time collision happens m0 will get a new position
bit CXM0P
bvc NoP0M0Collision
ldx Score
cpx #0
beq NoP0M0Collision ; prevent underflow!
; dec score. if screen is left and score is 0 continue
dex
stx Score
jsr SetM0Pos ; new position for m0
ldx #2 ; second song
ldy #FOODCOLLECTEDTRACKSIZE
jsr PlaySong
NoP0M0Collision
; now we check if m0 is in a wall
bit CXM0FB
bpl NoM0PFCollision ; if it is reloacte
jsr SetM0Pos
NoM0PFCollision
CollisionDone
lda #1
sta CXCLR ; clear collision
rts
; Plays the Intro noise
SoundHandle
ldy SoundEnabled,x
cpy #0 ; if it is 0, clear song and return
beq ClearSongSet
jsr Sound ; else we call sound
rts
ClearSongSet
jsr ClearSong
rts
Sound
ldy SoundEnabled,x
cpx #0
bne LoadTrackPtr2
lda (SoundTrackPtr),y
sta AUDF0,x
lda (SoundControl),y ; get the combined Control and Volume value
jmp TrackPtrLoaded
LoadTrackPtr2
lda (SoundTrackPtr2),y
sta AUDF0,x
lda (SoundControl2),y
TrackPtrLoaded
sta AUDV0,x ; update the Volume register
lsr
lsr
lsr
lsr ; the lower nibble is control
sta AUDC0,x
; dec every 2nd frame
;lda Framecount
;and SoundSpeed
;beq DoNotDecSound
dec SoundEnabled,x
DoNotDecSound
rts
ClearSong
; song done, now we quit
lda #0
sta AUDC0,x
sta AUDF0,x
sta AUDV0,x
rts
; subroutine
; inputs: x = offset in track and control table
; y = track length
PlaySong
lda #SoundTrackTable,x
sta SoundTrackPtr
lda #SoundTrackTable+1,x
sta SoundTrackPtr+1
sty SoundEnabled
lda #SoundControlTable,x
sta SoundControl
lda #SoundControlTable+1,x
sta SoundControl+1
rts
SetM0Pos
ldx 2
jsr RandomLocation
lda #M0RESPAWNT ; load ball respawn time
; dec level from that
clc
sbc Level
sta M0RespawnTimer
rts
;===============================================================================
; RandomLocation
; --------------
; call with X to set to the object to randomly position:
; 1 - player1
; 2 - missile0
; 3 - missile1
; 4 - ball
;
; X position
; ----------
; There are 160 pixels across the screen. There's also a border that takes up
; 4 pixels on each side, plus the player objects span 8 pixels. That gives us
; a range of 160 - 4*2 - 8 = 144 possible positions to place an object. Due to
; due to the Arena border we need to shift that 4 to the right so the X position
; can be anything from 4-148.
;
; Y position
; ----------
; Y position needs to be between 25-169
;===============================================================================
RandomLocation:
jsr Random ; get a random value between 0-255
and #127 ; limit range to 0-127
sta Temp ; save it
jsr Random ; get a random value between 0-255
and #15 ; limit range to 0-15
clc ; must clear carry for add
adc Temp ; add in random # from 0-127 for range of 0-142
adc #5 ; add 5 for range of 5-147
sta ObjectX,x ; save the random X position
jsr Random ; get a random value between 0-255
and #127 ; limit range to 0-127
sta Temp ; save it
jsr Random ; get a random value between 0-255
and #15 ; limit range to 0-15
clc ; must clear carry for add
adc Temp ; add in random # from 0-127 for range of 0-142
adc #26 ; add 26 for range of 26-168
sta ObjectY,x ; save the random Y position
rts
ResetPPositions
ldx #P0STARTX
stx ObjectX
ldy #P0STARTY
sty ObjectY
ldx P1STARTX
stx ObjectX+1
ldx P1STARTY
stx ObjectY+1
rts
NextLevel
inc Level
inc Lives
lda #3
clc
adc Level
; score is 3 + level
sta Score
ldx #0
stx MapsCleared
ldx #2 ; store 2 in gamestate to play level clear animation and play the tune
stx GameState
ldx #LEVELCLEARTRACKSIZE*2
stx Temp+1 ; used for frame counter for blank screen
ldx #4 ; third song
ldy #LEVELCLEARTRACKSIZE
jsr PlaySong
rts
NextMap
inc Lives
inc MapsCleared