-
Notifications
You must be signed in to change notification settings - Fork 34
/
Copy pathgenemedic.asm
2930 lines (2268 loc) · 100 KB
/
genemedic.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
; This is Gene Medic version 1.0
; by Jason H. Moore, Ph.D.
; File genemedic.asm
; Last updated 12/29/17
; Compile with DASM genemedic.asm -ogenemedic.bin -f3
; The goal of the game is to save a patient with a common
; disease such as cancer by editing mutations in their
; genes. Mutations can have known or unknown consequences upon
; editing (joystick down) with some revealed by consulting the
; electronic health record powerup (computer) or the scientific
; literature (text) power ups (joystick up). Each gene edit costs
; money that can be replenished with the money bag power up.
; The goal is to improve the patient health ("P" top score)
; before the financial resources are exhausted ("$" lower score).
; For more information see http://GeneMedic.org
; Special thanks to Andrew Davies and Darrell Spice for their
; great tutorials that helped with the development of this
; edutainment game. Thanks to countless others who posted
; useful comments and code on various websites and forums
; including the Stella mailing list and AtariAge.com.
; I also found the book "Making games for the Atari 2600"
; by Steven Hugg to be useful. I have tried to comment the code
; as much as I possibly can without writing my own book.
; Programming for the 2600 is not easy! You have been warned :)
; ****************************************************************
; Tell the assembler we are working with the 6502 processor
; ****************************************************************
PROCESSOR 6502
; ****************************************************************
; Include the vcs header file with the 2600 register and
; memory map definitions. Also include the macro file that
; has some useful functions such as 'sleep' used here.
; ****************************************************************
INCLUDE VCS.h
INCLUDE macro.h
; ****************************************************************
; Constants set here
; ****************************************************************
P0HEIGHT = 55 ; height of player 0 sprite in scanlines
PUHEIGHT = 16 ; height of powerup sprite in scanlines
SCHEIGHT = 14 ; height of the score playfield
CYTHEIGHT = 14 ; height of the cytoplasm playfield
DNAHEIGHT = 11 ; height of the DNA helix playfield
HHEIGHT = 36 ; height of the heart playfield
GHEIGHT = 14 ; height of the gene name playfield
JHEIGHT = 14 ; height of the jockey name playfield
XMAX = 150 ; max X position to right for player
XMIN = 5 ; min X position to left for player
P0XSTART = 20 ; player 0 starting X position
P0YSTART = 80 ; player 0 starting Y position
P1RESX = 38 ; player 1 starting X position for research
; powerup - in sleep cycles
P1MONX = 54 ; player 1 starting X position for money
; bag powerup - in sleep cycles
P1EHRX = 22 ; player 1 starting X position for EHR
; powerup - in sleep cycles
M0X = 28 ; missile 0 starting X position for in sleep
; cycles. This M is used to queue position
; of nucleotide to edit triggering tricorder
; graphic above player 0.
; ****************************************************************
; Tell the assembler the memory locations of our game variables.
; ds is a pseudo-op for assembler that stands for 'define space'
; ds 1 = one byte and ds 2 = two bytes. Remember that the 2600
; only has 128 bytes of RAM!
; ****************************************************************
SEG.U variables ; pseudo-op for the assembler
ORG $80 ; the starting location of RAM
P0X ds 1 ; player 0 X coordinate
P0Y ds 1 ; player 0 Y coordinate
P0Pos ds 1 ; byte for P0 fine (high nibble) and
; course (low nibble) horizontal position
P0LineColor ds 1 ; player 0 color slice for that scanline
P0GfxFlag ds 1 ; flag for which P0 graphic to use
; 1 = no tricorder
; 2 = health tricorder [+]
; 3 = harm tricorder [!]
; 4 = unknown tricorder [-]
; 5 = context dependent tricorder [::]
ResearchFlag ds 1 ; flag for drawing the research powerup
; 1 = draw, 0 = don't draw
EHRFlag ds 1 ; flag for drawing the EHR powerup
; 1 = draw, 0 = don't draw
MoneyFlag ds 1 ; flag for drawing the money bag powerup
; 1 = draw, 0 = don't draw
NewLevelFlag ds 1 ; time to draw a new playfield? This gets set
; to 1 when player reaches right side of screen
ScreenFlag ds 1 ; which screen to draw? A or B?
; this help set up alternating screens with
; different playfield graphics for sense
; of movement through the cell
PScore ds 1 ; this is the patient health score
DScore ds 1 ; this is the dollars score
DScoreFLag ds 1 ; flag to indicate whether DScore = 0
; to prevent going negative
ScoreGfx ds 1 ; holds the current score graphics line
UpFlag ds 1 ; flag for joystick moved up (for power ups)
DownFlag ds 1 ; flag for joystick moved down (for edits)
MutationFlag ds 1 ; holds info about nature of mutation
; set each screen by a random number
; 0 = no mutation
; 1 = mutation - known, helpful
; 2 = mutation - known, harmful
; 3 = mutation - unknown, helpful
; 4 = mutation - unknown, harmful
; 5 = mutation - context dependent, helpful
; 6 = mutation - context dependent, harmful
EditFlag ds 1 ; keeps track of successful edits
; so score only modified once per screen
MBUsedFlag ds 1 ; keeps track of whether money bag powerup
; has been used for that screen. Only used once.
WinFlag ds 1 ; this indicates that the player has won the game
LoseFlag ds 1 ; this indicates that the player has lost the game
EndSoundFlag ds 1 ; flag indicating final sound effects are done
; so they only play once at end of game
RandomPU ds 1 ; holds 8-bit pseudo random number for powerups
RandomMut ds 1 ; holds 8-bit pseudo random number for mutations
GameLaunchFlag ds 1 ; a flag for indicating the game has booted.
; this is used to indicate the start screen
; should be drawn until fire button pressed
SFX_LEFT ds 1 ; index for sound effect functions
SFX_RIGHT ds 1 ; index for sound effect functions
; ****************************************************************
; Tell the assembler where the origin (ORG) of the program is
; ROM is memory location $F000 to $FFFF
; ****************************************************************
SEG ; pseudo-op to set current segment
ORG $F000 ; let the assembler know start address
; ****************************************************************
; This initializes the RAM and TIA registers by writing 0s.
; It also initalizes the stack pointer to $FF. This code comes
; from Andrew Davie's tutorial. Note just clearing the RAM did
; not work well on the original 2600 hardware.
; ****************************************************************
Reset
ldx #0 ; load the X register with a 0
txa ; transfer X to accumulator
Clear
dex ; decrement X register
txs ; transfer X to stack pointer
pha ; push accumulator onto stack
bne Clear ; branch to Clear if accumulator != 0
; ****************************************************************
; This initializes the random seed for the GetRandom subroutine
; Using two different random numbers to prevent dependencies
; between powerups and mutations.
; ****************************************************************
SetRandomSeed
lda INTIM ; this is an unknown value
eor #$FF ; XOR to flip bits so seed is not 0
sta RandomPU ; Seed for GetRandomPU
lda INTIM ; this is an unknown value
eor #$FF ; XOR to flip bits so seed is not 0
sta RandomMut ; Seed for GetRandomMut
; ****************************************************************
; This initializes the game launch flag for drawing start screen
; when game is first booted.
; ****************************************************************
lda #1 ; load accumulator with a 1
sta GameLaunchFlag ; set game launch flag to draw start
; screen first time through until
; reset switch is pressed then flag=0
jmp PlayfieldInit ; bypass the next bit of code on boot
; ****************************************************************
; This is marks the place in the code to jump to when starting a
; game after fire button is pressed from start screen.
; ****************************************************************
StartHere
lda #0 ; load accumulator with a 0
sta GameLaunchFlag ; reset game launch flag so start
; screen isn't drawn anymore after
; reset switch is pressed
; ****************************************************************
; This initializes the player and playfield color registers
; ****************************************************************
PlayfieldInit
lda #$80 ; load accumulator with dark blue
sta COLUBK ; store in the background color memory
lda #1 ; load the accumulator with 1
sta CTRLPF ; storing a 1 here indicates a
; reflected playfield
lda #$1E ; Load the player 1 color
sta COLUP1 ; set player 1 color register
; ****************************************************************
; This initializes the player X and Y position.
; ****************************************************************
lda #P0XSTART ; load accumulator with X position
sta P0X ; set P0 starting X position
lda #P0YSTART ; load accumulator with Y position
sta P0Y ; set P0 starting Y position
; ****************************************************************
; This initializes the patient (top) and money (bottom) scores
; that are presented to the player as colors. Brighter yellow for
; the patient score is an indication of health. Brighter yellow
; for the money score means more resources have been spent on the
; patient. The game is over when the max patient score (win), min
; patient score (loss), or max money score (loss) are reached.
; ****************************************************************
lda #1 ; load accumulator with 1
sta PScore ; set left score to 1
lda #1 ; load accumulator with 1
sta DScore ; set left score to 1
; ****************************************************************
; This initializes the game flags for tracking score and powerups.
; Most are set to 0 with only th P0 graphics flag set to 1.
; ****************************************************************
lda #1 ; load accumulator with flag = 1 to start
sta P0GfxFlag ; set player 0 graphic flag = 1
; this draws player without tricorder.
; tricorder comes on when P0 collides
; with M0 that is present when mutation
; flag > 0
lda #0 ; load accumulator with flag = 0 to start
sta UpFlag ; set joystick Up Flag = 0
sta DownFlag ; set joystick Down Flag = 0
sta EHRFlag ; set EHR Flag = 0
sta MoneyFlag ; set Money Bag Flag = 0
sta ResearchFlag ; set Research Flag = 0
sta ScreenFlag ; set Screen Flag = 0
sta EditFlag ; set Edit Flag = 0 to start, no edit
sta MBUsedFlag ; set Money Bag used Flag = 0 to start
sta WinFlag ; set the Win Flag = 0 to start, 1=win
sta LoseFlag ; set the Lose Flag = 0 to start, 1=lose
sta MutationFlag ; set Flag = 0, no mutation first screen
sta EndSoundFlag ; set Flag = 0 until end of game
sta NewLevelFlag ; time to draw new playfield? This gets
; set to 1 when player reaches right side
; of screen
; ****************************************************************
; The first part of a 2600 program is the vertical sync that
; tells the TV to get ready to draw a picture. The CPU needs
; to wait for three scanlines for the TV to get ready. This
; is done with VSYNC to start this process and WSYNC (x2) to
; wait for the scanlines to finish.
; ****************************************************************
VerticalSYNC ; The start of the new frame
; Main game loop starts here
lda #2 ; load the accumulator with a 2
sta VSYNC ; store 2 in VSYNC memory to start
sta WSYNC ; wait for scanline to finish
sta WSYNC ; second WSYNC (need at least 2)
lda #0 ; load the accumulator with a 0
sta VSYNC ; store 0 in VSYNC memory to end
; ****************************************************************
; The second part of a 2600 program is the vertical blank
; with an additional 37 scanlines of time before the screen
; is actually drawn on the TV. Can do some game computations
; here but must count the scanlines to add up to 37 total.
; 37 scanlines at 76 cycles each works out to be 43 ticks of
; the timer. Will set TIM64T to 43 to keep track of cycles.
; Will use this time for horizontal positioning of the sprites.
; Will also use this time for game calculations.
; ****************************************************************
lda #43 ; we have 43 ticks of the timer here
sta TIM64T ; start the timer for VBLANC
GameOverCheck1 ; check win & lose flags for game over
; if game over skip game calculations
; so no player movement during final screen
lda WinFlag ; load accumulator with Win Flag
beq GameOverCheck2 ; check lose flag if win flag = 0
jmp VerticalBLANK ; skip game calcs if flag = 1
GameOverCheck2
lda LoseFlag ; load accumulator with Lose Flag
beq NewLevelCheck ; move on to game calcs if lose flag = 0
jmp VerticalBLANK ; skip game calcs if flag = 1
NewLevelCheck ; time to draw new level with new random
; powerup and gameplay flags?
lda NewLevelFlag ; if flag = 1 then it is a new level
bne NewLevel ; if flag = 0 then do game calculations
jmp CheckWin ; but skip setting new gameplay flags
; using jmp here to avoid error due to
; a branch that is too long.
; the new level flag gets set to 1 when
; the player gets to far right side of PF
NewLevel ; start here when it is a new level
; and new flags need to be set
; e.g. new powerup flags get set here
SetEditFlag ; set this flag to 0 on new level
lda #0 ; load accumulator with a 0
sta EditFlag ; make sure Edit Flag = 0 on new screen
; this flag prevents multiple changes to
; score on same screen.
sta MBUsedFlag ; reset this flag that keeps track of
; whether the money bag powerup was used
sta WSYNC ; wait for finish of scanline
SetScreenFlag ; set the flag used to track which screen
; is drawn for cyto and nuc parts of PF
; this flag alternates between 0 and 1
lda ScreenFlag ; load accumulator with flag value
beq ScreenFlagSet1 ; flag is zero so need to change to 1
ScreenFlagSet0 ; flag = 1 so change to a 0
lda #0 ; load accumulator with a 0
sta ScreenFlag ; flag is 1 so need to change to 0
jmp SetPUFlags ; done setting helix flag
ScreenFlagSet1 ; change this flag to a 1
lda #1 ; load accumulator with a 1
sta ScreenFlag ; flag is 0 so need to change to 1
SetPUFlags ; set new flags for the powerups
lda #0 ; make sure all flags are reset to 0
; when a new level starts
sta EHRFlag ; set flag to 0
sta ResearchFlag ; set flag to 0
sta MoneyFlag ; set flag to 0
jsr GetRandomPU ; generate a random number from 0-255
; number is returned in accumulator
and #%00000011 ; limit number to 0-3
tax ; transfer accumulator to X register
sta WSYNC ; wait for finish of scanline
CheckEHR ; display EHR powerup?
cpx #0 ; if Random=0 then set EHR flag to 1
bne CheckRes ; if != 1 check research powerup
lda #1 ; load accumulator with a 1
sta EHRFlag ; set flag = 1
sta WSYNC ; wait for finish of scanline
CheckRes ; display research powerup?
cpx #1 ; if Random=1 then set research flag to 1
bne CheckMon ; now check money bag powerup
lda #1 ; load accumulator with a 1
sta ResearchFlag ; set flag = 1
sta WSYNC ; wait for finish of scanline
CheckMon ; display money bag powerup?
cpx #2 ; if Random=2 then set money bag flag to 1
bne CheckDone ; done setting powerup flags
lda #1 ; load accumulator with a 1
sta MoneyFlag ; set flag = 1
sta WSYNC ; wait for finish of scanline
CheckDone ; if Random=3 then no powerup
; on that screen and all powerup
; flags = 0
lda #0 ; load accumulator with a 0
sta NewLevelFlag ; set this flag = 0 to reset this flag
; no longer a new level
SetMutFlags ; set new flags for the mutation
lda #0 ; make sure flag is reset to 0
sta MutationFlag ; set flag to 0
jsr GetRandomMut ; generate a random number from 0-255
; number is returned in accumulator
and #%00000111 ; limit number to 0-7
sta MutationFlag ; store value in mutation flag
sta WSYNC ; wait for finish of scanline
CheckWin ; is the game over? winner?
ldx PScore ; load the X register with the patient score
cpx #9 ; is the patient healed?
bne CheckLose1 ; branch if PScore != 9, not healed
lda #1 ; load accumulator with a 1
sta WinFlag ; winner! Set WinFlag = 1
jmp VerticalBLANK ; move on to draw final screen
CheckLose1 ; check for loss based on patient score = 0
sta WSYNC ; wait for finish of scanline
lda PScore ; load accumulator with the patient score
bne CheckLose2 ; branch if PScore != 0
lda #1 ; load accumulator with a 1
sta LoseFlag ; loser! Set LoseFlag = 1
jmp VerticalBLANK ; move on to draw final screen
CheckLose2 ; check for loss based on dollar score = 9
ldx DScore ; load the X register with the patient score
cpx #9 ; is the player out of money?
bne MinDScore ; branch if DScore != 9
lda #1 ; load accumulator with a 1
sta LoseFlag ; loser! Set LoseFlag = 1
jmp VerticalBLANK ; move on to draw final screen
MinDScore ; this keep the DScore from falling below 1
lda DScore ; load X register with DScore value
bne SetM0X ; branch if X != 0
lda #1 ; load accumulator with a 1
sta DScore ; store the 1 in DScore to prevent 0
SetM0X ; setting the X position for missile 0
sleep M0X ; sleep this many cycles to set M0
; in middle of screen
sta RESM0 ; trigger X position for missile 0
; this never changes and is used as
; position marker for player 0
sta WSYNC ; wait for finish of scanline
; next section sets the horizontal
; position of player 1 with unique
; X positions for each powerup.
; no fine positioning needed here
; because P1 is static
SetP1XEHR ; set powerup X pos if EHR flag = 1
lda EHRFlag ; load the accumulator with EHR flag
beq SetP1XRes ; jump to next if flag = 0
sleep P1EHRX ; delay to horizontally position sprite
sta RESP1 ; set sprite X pos by writing to RESP1
sta WSYNC ; wait for finish of scanline
jmp StartHMoveP0 ; go to next section for P0 positioning
SetP1XRes ; set powerup X pos if research flag = 1
sta WSYNC ; wait for finish of scanline
lda ResearchFlag ; load the accumulator with research flag
beq SetP1XMon ; jump to next if flag = 0
sleep P1RESX ; delay to horizontally position sprite
sta RESP1 ; set sprite X pos by writing to RESP1
sta WSYNC ; wait for finish of scanline
jmp StartHMoveP0 ; go to next section for P0 positioning
SetP1XMon ; set powerup X pos if money bag flag = 1
sta WSYNC ; wait for finish of scanline
lda MoneyFlag ; load the accumulator with money bag flag
beq StartHMoveP0 ; jump to next if flag = 0
sleep P1MONX ; delay to horizontally position sprite
sta RESP1 ; set sprite X pos by writing to RESP1
StartHMoveP0 ; process P0 horizontal move here
; this bit of code from Battlezone game
; commonly used for horizontal positioning
sta WSYNC ; wait for finish of scanline
; timing of this WSYNC and next important
; for correct horizontal movement
lda P0X ; load accumulator with P0 X position
sec ; set the carry flag
DivideLoop ; divide by 15
sbc #15 ; subtract 15
bcs DivideLoop ; branch until negative
eor #7 ; calculate the fine offset
asl ; arithmetic shift left
asl
asl
asl
sta RESP0 ; fix the course position
sta WSYNC ; wait for finish of scanline
sta HMP0 ; set the fine offset
CheckMoneyBag ; is the money bag powerup activated?
lda MoneyFlag ; check if money bag powerup present
beq CheckP0M0Col1 ; if=0 then powerup not present
lda UpFlag ; is the jostick up?
beq CheckP0M0Col1 ; if=0 then joystick not up and skip
lda MBUsedFlag ; has the money bag powerul been used?
beq MBUse ; branch to use money bag if flag = 0
jmp CheckP0M0Col1 ; jump because powerup already used
MBUse
dec DScore ; powerup activated - decrease DScore
lda #1 ; load accumulator with a 1
sta MBUsedFlag ; store the 1 in MBUsedFlag so can't use
ldy #sfxPING ; select ping sound effect
jsr SFX_TRIGGER ; execute the sound
CheckP0M0Col1 ; This checks for a P0 - M0 collision
lda #1 ; load the accumulator with a 1
sta P0GfxFlag ; set the P0 graphics flag to
; tricorder off
lda #%01000000 ; load the D6 bit in accumulator
bit CXM0P ; bit compare to what is in col register
bne Collision1 ; branch if collision = 1
jmp VerticalBLANK ; skip to Vertical BLANK if no collision
; using jmp here because branch too long
Collision1
lda MutationFlag ; load accumulator with mutation flag
tax ; transfer accumulator to X register
cpx #7 ; compare X register to 7
bne Collision2 ; branch if != 7
ldx #5 ; change the 7 to a 5
; need to do something with the 7 since
; we only have 6 choices to map below
; this creates a slight excess of context
; dependent mutations that are helpful
; this is ok because I like these best
Collision2
lda DownFlag ; load accumulator with down joystick flag
tay ; transfer accumulator to Y register
cpx #0 ; is there a mutation? no mutation = 0
bne MutationYes ; if yes process type of mutation
jmp MutationDone ; if no jump to mutation done and VBLANK
MutationYes
sta WSYNC ; wait for finish of scanline
MutKnownPos ; known helpful mutation?
cpx #1 ; is mutation flag = 1?
bne MutKnownNeg ; branch if flag != 1
lda #2 ; load the accumulator with 2
sta P0GfxFlag ; set the P0 graphics flag
; to have right tricorder visible
lda EditFlag ; load edit flag to see if edit already made
beq MKPEdit ; branch if edit = 0
jmp MutationDone ; otherwise we are done
MKPEdit ; let's edit if joystick is down
cpy #1 ; is joystick down? Did a tay earlier
bne MutKnownNeg ; skip if not
inc PScore ; helpful editing has occured, inc score
inc DScore ; money used to edit, inc score
ldy #sfxHELP ; select sound effect for helpful edit
jsr SFX_TRIGGER ; execute the sound
lda #1 ; load accumulator with a 1
sta EditFlag ; reset edit flag = 1 to indicate no more
; edits this level
sta WSYNC ; wait for finish of scanline
jmp MutationDone ; all done - move on to VBLANK
MutKnownNeg ; known harmful mutation?
cpx #2 ; is mutation flag = 2?
bne MutUnknownPos ; branch if flag != 2
lda #3 ; load the accumulator with 3
sta P0GfxFlag ; set the P0 graphics flag
; to have right tricorder visible
lda EditFlag ; load edit flag to see if edit already made
beq MKNEdit ; branch if edit = 0
jmp MutationDone ; otherwise we are done
MKNEdit ; let's edit if joystick is down
cpy #1 ; is joystick down?
bne MutUnknownPos ; skip if not
dec PScore ; harmful editing has occured, dec score
inc DScore ; money used to edit, inc score
ldy #sfxHARM ; select sound effect for harmful edit
jsr SFX_TRIGGER ; execute the sound
lda #1 ; load accumulator with a 1
sta EditFlag ; reset edit flag = 1 to indicate no more
; edits this level
sta WSYNC ; wait for finish of scanline
jmp MutationDone ; all done - move on to VBLANK
MutUnknownPos ; unknown positive mutation - player must guess
; or use research powerup to reveal effect
cpx #3 ; is mutation flag = 3?
bne MutUnknownNeg ; branch if flag != 3
lda ResearchFlag ; now check if research powerup present
beq MUPP04 ; if=0 then powerup not present
; and player doesn't know type of mutation
lda UpFlag ; is the jostick up?
beq MUPP04 ; if=0 then not up and draw tricorder 4
; with symbol for unknown mutation
lda #2 ; load the accumulator with 2
sta P0GfxFlag ; set the P0 graphics flag
; to have right helpful tricorder visible
jmp MUPP02 ; skip the next section
MUPP04 ; draw the neutral tricorder since
; mutation unknown to player
lda #4 ; load the accumulator with 4
sta P0GfxFlag ; set the P0 graphics flag
; to have right tricorder visible
MUPP02
lda EditFlag ; load edit flag to see if edit already made
beq MUPEdit ; branch if edit = 0
jmp MutationDone ; otherwise we are done
MUPEdit ; let's edit if joystick is down
cpy #1 ; is joystick down?
bne MutUnknownNeg ; skip if not
inc PScore ; helpful editing has occured, inc score
inc DScore ; money used to edit, inc score
ldy #sfxHELP ; select sound effect for helpful edit
jsr SFX_TRIGGER ; execute the sound
lda #1 ; load accumulator with a 1
sta EditFlag ; reset edit flag = 1 to indicate no more
; edits this level
sta WSYNC ; wait for finish of scanline
jmp MutationDone ; all done - move on to VBLANK
MutUnknownNeg ; unknown positive mutation - player must guess
; or use research powerup to reveal effect
cpx #4 ; is mutation flag = 4?
bne MutContextPos ; branch if flag != 4
lda ResearchFlag ; now check if research powerup present
beq MUNP04 ; if=0 then powerup not present
; and player doesn't know type of mutation
lda UpFlag ; is the jostick up?
beq MUNP04 ; if=0 then not up and draw tricorder 4
; with symbol for context-dependent mutation
lda #3 ; load the accumulator with 3
sta P0GfxFlag ; set the P0 graphics flag
; to have right helpful tricorder visible
jmp MUNP03 ; skip the next section
MUNP04 ; draw the neutral tricorder since
; mutation unknown to player
lda #4 ; load the accumulator with 4
sta P0GfxFlag ; set the P0 graphics flag
; to have right tricorder visible
MUNP03
lda EditFlag ; load edit flag to see if edit already made
beq MUNEdit ; branch if edit = 0
jmp MutationDone ; otherwise we are done
MUNEdit ; let's edit if joystick is down
cpy #1 ; is joystick down?
bne MutContextPos ; skip if not
dec PScore ; harmful editing has occured, dec score
inc DScore ; money used to edit, inc score
ldy #sfxHARM ; select sound effect for harmful edit
jsr SFX_TRIGGER ; execute the sound
lda #1 ; load accumulator with a 1
sta EditFlag ; reset edit flag = 1 to indicate no more
; edits this level
sta WSYNC ; wait for finish of scanline
jmp MutationDone ; all done - move on to VBLANK
MutContextPos ; context-dependent positive mutation
; player must guess or use EHR powerup
cpx #5 ; is mutation flag = 5?
bne MutContextNeg ; branch if flag != 5
lda EHRFlag ; now check if EHR powerup present
beq MCPP05 ; if=0 then powerup not present
; and player doesn't know type of mutation
lda UpFlag ; is the jostick up?
beq MCPP05 ; if=0 then not up and draw tricorder 5
; with symbol for context-depentent mutation
lda #2 ; load the accumulator with 2
sta P0GfxFlag ; set the P0 graphics flag
; to have right helpful tricorder visible
jmp MCPP02 ; skip the next section
MCPP05 ; draw the neutral tricorder since
; mutation unknown to player
lda #5 ; load the accumulator with 5
sta P0GfxFlag ; set the P0 graphics flag
; to have right tricorder visible
MCPP02
lda EditFlag ; load edit flag to see if edit already made
beq MCPEdit ; branch if edit = 0
jmp MutationDone ; otherwise we are done
MCPEdit ; let's edit if joystick is down
cpy #1 ; is joystick down?
bne MutContextNeg ; skip if not
inc PScore ; helpful editing has occured, inc score
inc DScore ; money used to edit, inc score
ldy #sfxHELP ; select sound effect for helpful edit
jsr SFX_TRIGGER ; execute the sound
lda #1 ; load accumulator with a 1
sta EditFlag ; reset edit flag = 1 to indicate no more
; edits this level
sta WSYNC ; wait for finish of scanline
jmp MutationDone ; all done - move on to VBLANK
MutContextNeg ; context-dependent negative mutation
; player must guess or use EHR powerup
cpx #6 ; is mutation flag = 6?
bne MutationDone ; branch to done if flag != 6
; this allow nothing to happen when flag = 7
lda EHRFlag ; now check if EHR powerup present
beq MCNP05 ; if=0 then powerup not present
; and player doesn't know type of mutation
lda UpFlag ; is the jostick up?
beq MCNP05 ; if=0 then not up and draw tricorder 5
; with symbol for context-depentent mutation
lda #3 ; load the accumulator with 3
sta P0GfxFlag ; set the P0 graphics flag
; to have right helpful tricorder visible
jmp MCNP03 ; skip the next section
MCNP05 ; draw the neutral tricorder since
; mutation unknown to player
lda #5 ; load the accumulator with 5
sta P0GfxFlag ; set the P0 graphics flag
; to have right tricorder visible
MCNP03
lda EditFlag ; load edit flag to see if edit already made
beq MCNEdit ; branch if edit = 0
jmp MutationDone ; otherwise we are done
MCNEdit ; let's edit if joystick is down
cpy #1 ; is joystick down?
bne MutationDone ; skip if not
dec PScore ; harmful editing has occured, dec score
inc DScore ; money used to edit, inc score
ldy #sfxHARM ; select sound effect for harmful edit
jsr SFX_TRIGGER ; execute the sound
lda #1 ; load accumulator with a 1
sta EditFlag ; reset edit flag = 1 to indicate no more
; edits this level
sta WSYNC ; wait for finish of scanline
MutationDone ; all done checking mutations
sta CXCLR ; clear the collision registers
sta WSYNC ; wait for finish of scanline
VerticalBLANK ; The finish vertical blank loop
; This burns the scanlines left over
lda INTIM ; load timer value into accumulator
bne VerticalBLANK ; loop if timer not zero
sta WSYNC ; wait for scanline to finish
sta HMOVE ; finalize the horizontal move of P0
; this triggers execution of motion
; degree of move controlled by HMP0/1
; must be done at this point in code
sta WSYNC ; wait for scanline to finish
sta VBLANK ; store 0 in VBLANK memory to end
; and start drawing the picture by
; turning the TV beam on
; ****************************************************************
; The third part of a 2600 program is the screen drawing
; routine that is often called the kernel. This is where
; the players, missiles, and playfield are drawn. There
; are 192 scanlines to be drawn with 228 color clocks per line.
; There are 192 * 228 = 43,776 color clocks. There are 3
; machine cycles per color clock. This give 14,592 machine
; cycles. Dividing this by 64 gives 228 timer ticks. So, we store
; 228 in TIM64T to start the timer that is used to draw screen.
; We are using the Y register to keep track of 192 scanlines.
; We will draw the screen in chunks starting with the scores.
; ****************************************************************
ldy #192 ; load the Y register with 192
; this is the first scanline at top
; of the screen
lda #228 ; 228 timer ticks
sta TIM64T ; start the timer
lda #$00 ; load accumulator with PF color
sta COLUPF ; set the PF color to black
lda #%11111111 ; draw solid pattern for initial PF
sta PF0 ; set the PF0 register
sta PF1 ; set the PF1 register
sta PF2 ; set the PF2 register
DrawScreenTop ; Draw very top part of screen in black
dey ; decrement Y to burn a scanline
sta WSYNC ; wait for scanline to finish
cpy #188 ; Have 4 score scanlines passed?
bne DrawScreenTop ; loop until Y = 188 then set up the
; score part of PF
ldx PScore ; Load X with current patient score
lda PScoreColor,x ; use the score to get the PF color
sta COLUPF ; score-based color of PF for patient
dey ; burn scanline 187 to create border
sta WSYNC ; wait for scanline to finish
ldx (#SCHEIGHT-1) ; set up the score line counter
DrawPScore ; Draw score for patient health
lda PatientGfx,x ; Load patient graphics for this line
sta PF1 ; Draw the "P" for patient
SLEEP 21 ; kill time so this is only part of PF
; drawn
lda BlankGfx1,x ; Blank graphics to erase 2nd PF1
sta PF1 ; Draw the blank PF1 graphics
dex ; decrement our line counter
dey ; decrement the Y register
cpy #173 ; Have 14 score scanlines passed?
sta WSYNC ; wait for scanline to finish
bne DrawPScore ; loop until Y = 173 then set up the
; dollar score part of PF
dey ; burn scanline 172 to create border
sta WSYNC ; wait for scanline to finish
lda #$00 ; load accumulator with PF color
sta COLUPF ; set the PF color to black
DrawDivider1 ; Draw 4 scanline black divider between
; the patient and dollar scores
dey ; decrement the Y register