-
Notifications
You must be signed in to change notification settings - Fork 2
/
alien-inv.s
2198 lines (1993 loc) · 66.5 KB
/
alien-inv.s
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
; V I C A L I E N - I N V A D E R S
;
; by Davide Bucci, April-June 2018
;
; This program is a Space-Invaders clone that runs on an unexpanded VIC-20
;
; The objective is not to reproduce perfectly the original game, but more to
; propose something very playable and quite fast on the VIC-20. Arcadia has been
; inspiring, even if its gameplay is very different from Space Invaders.
;
; A certain amount of work has been done to ensure that the graphics is smooth
; enough so that the effect hopefully appears to be quite polished.
; It is my first attempt to write a complete game in 6502 assembly language,
; even if I learnt that language many years ago and I used it mainly as a
; complement for BASIC programs.
;
; The assembler used is ca65
;
; The bombs drop by aliens are described by four arrays:
; BombSpeed, BombPosX, BombPosY and BombPosOY. Their name should be quite
; self-descriptive, except for BombPosOY, that contains the old position of the
; bombs (Y coordinate, as bombs fall vertically) and is used for erasing the
; bombs when they are to be drawn at the new coordinate. Speed is positive for
; bombs falling.
;
; The cannon shots operates with a very similar principle with respect to bombs
; and are described by FireSpeed, FirePosX, FirePosY and FirePosOY. The only
; difference is that a positive speed means that shots move upwards.
;
; A speed of $FF (or 255 in decimal) means that the bomb is exploding and
; should be destroyed; i.e. erased from the screen and then deactivated, by
; putting a final speed of 0.
;
; Plenty of things are done during the IRQ handling routine, synchronized with
; the PAL refresh rate of the monitor. For this reason, expect a lot of flicker
; in the aliens when this game is played on a NTSC machine. Differences between
; PAL and NTSC include a different screen height that is 31 rows for PAL
; machines and 28 for NTSC ones.
; Difficulty-related constants
BOMBPROB = $F0 ; Higher = less bombs falling
SHIPPROB = $F5 ; Higher = less mother ships
PERIODS = 5 ; Initial difficulty (less=faster)
; General constants
NMBOMBS = 8 ; Maximum number of bombs falling at the same time
NMSHOTS = 2 ; Maximum number of cannon shots at the same time
MAXMOTHERS = 10 ; Maximum number of mother ships in a level
; General-use addresses
GRCHARS1 = $1C00 ; Address of user-defined characters. Since in the
; unexpanded VIC the screen matrix starts at
; $1E00, there are 512 bytes free, i.e. 64 chars
; that can be defined. That leaves 3059 bytes free
; for the machine language code (counting the
; 752 SYS4109 stub in BASIC that launches the
; program.
; Colour constants for the VIC 20
BLACK = $00
WHITE = $01
RED = $02
CYAN = $03
MAGENTA = $04
GREEN = $05
BLUE = $06
YELLOW = $07
MULTICOLOUR = $08
; KERNAL routines used
GETIN = $FFE4
; Page-0 addresses used (for indirect indexed addressing and other things)
LAB_01 = $01
LAB_02 = $02
LAB_03 = $03
LAB_04 = $04
LAB_05 = $05
LAB_06 = $06
LAB_07 = $07
LAB_08 = $08
LAB_09 = $09
LAB_0A = $0A
LAB_0B = $0B
CharCode = LAB_03 ; Employed in DrawChar
PosX = LAB_04
PosY = LAB_05
Colour = LAB_06 ; Colour to be used by the printing routines
tmpindex = LAB_07 ; Temporary variables
tmpx = LAB_08
tmpy = LAB_09
tmp4 = LAB_0A
SPRITECH = $0C ; Pointer to the group of 4 ch. for a sprite (word)
CHRPTR = $0E ; Pointer to the original ch. in a sprite (word)
SpriteX = $10 ; X position (offset in a char) of a sprite (byte)
SpriteY = $11 ; Y position (offset in a char) of a sprite (byte)
CharShr = $12 ; Employed in LoadSprite
AlienPosYc= $13 ; Vertical alien position (in characters)
PixPosX = $14 ; Position in characters
ColourRead= $15 ; Colour read by GetChar
POSCHARPT = $1A ; Pointer for a character in memory (word)
POSCOLPT = $1C ; Pointer for a colour in memory (word)
Bombcntr = $1E ; Counter for bomb interrupts
PixPosXO = $1F ; Old Position in characters
TmpScan = $20 ; Used in raster line sync code
Random = $21 ; Position where to store a random word by GetRand
IrqCn = $23 ; Counter for interrupt
keyin = $24 ; Last key typed.
Val = $25 ; Used for the BCD conversion (word)
Res = $27 ; The result of the BCD conversion (3 bytes)
Joystick = $2A ; Different from zero if the joystick was used
MotherPos = $2B ; Position of the mother ship ($FD: no ship)
Period = $2C ; Higher = slower alien movement
MotherCntr= $2D ; Counter for slowing down mother ship
AliensR1s = $2E ; Presence of aliens in row 1
AliensR2s = $2F ; Same for row 2
AliensR3s = $30 ; Same for row 3
AliensR = $31 ; Presence of aliens in row (temporary)
AliensR1 = $32 ; Presence of aliens in row 1 (temporary)
AliensR2 = $33 ; Same for row 2 (temporary)
AliensR3 = $34 ; Same for row 3 (temporary)
AlienCode1= $35 ; Character for alien row 1
AlienCode2= $36 ; Character for alien row 2
AlienCode3= $37 ; Character for alien row 3
AlienPosX = $38 ; Horizontal position of aliens (in pixels)
AlienMaxX = $39 ; Maximum position in X of aliens (in characters)
AlienMinX = $3A ; Minimum position in X of aliens (in characters)
AlienPosY = $3B ; Vertical position of aliens (in pixels)
AlienPosYO= $3C ; Previous (old) vertical position of aliens (px)
AlienCurrY= $3D ; Vertical (temp) position of alien being drawn (ch)
Direction = $3E ; The first bit indicates aliens' X direction
CannonPos = $3F ; Horizontal position of the cannon (in characters)
OldCannonP= $40 ; Old position of the cannon
Win = $41 ; If 1, the level is won. If $FF, game over
Score = $42 ; Current score (divided by 10) (word)
HiScore = $44 ; High Score (divided by 10) (word)
BombPeriod= $46 ; Period for updating bomb positions
Level = $47 ; Current level
tmpp = $48 ; Temp for bomb dropping position
ExplMXpos = $49 ; Hor. position (ch) of the explosion on mother sh.
ExplMCnt = $4A ; Mother ship explosion counter
CannonYPos= $4B
AlienYLimit=$4C
BunkerY = $4D
VoiceBase = $4E
Voice1ptr = $4F
Voice1ctr = $50
Loop1ctr = $51
Loop1str = $52
Voice1drt = $53
Voice1nod = $54
Voice2ptr = $55
Voice2ctr = $56
Loop2ctr = $57
Loop2str = $58
Voice2drt = $59
Voice2nod = $60
tprnd1 = $61
tprnd2 = $62
mothercntr= $63
aliencntr = $64
INITVALC=$ede4
; VIC-chip addresses
VICSCRHO = $9000 ; Horizontal position of the screen
VICSCRVE = $9001 ; Vertical position of the screen
VICCOLNC = $9002 ; Screen width in columns and video memory addr.
VICROWNC = $9003 ; Screen height, 8x8 or 8x16 chars, scan line addr.
VICRAST = $9004 ; Bits 8-1 of the current raster line
VICCHGEN = $9005 ; Character gen. and video matrix addresses.
GEN1 = $900A ; First sound generator
GEN2 = $900B ; Second sound generator
GEN3 = $900C ; Third sound generator
NOISE = $900D ; Noise sound generator
VOLUME = $900E ; Volume and additional colour info
VICCOLOR = $900F ; Screen and border colours
PORTAVIA1 = $9111 ; Port A 6522 (joystick)
PORTAVIA1d = $9113 ; Port A 6522 (joystick)
PORTBVIA2 = $9120 ; Port B 6522 2 value (joystick)
PORTBVIA2d = $9122 ; Port B 6522 2 direction (joystick
MEMSCR = $1E00 ; Start address of the screen memory (unexp. VIC)
MEMCLR = $9600 ; Start address of the colour memory (unexp. VIC)
REPEATKE = $028A ; Repeat all keys
VOICE1 = GEN1 ; Voice 1 for music
VOICE2 = GEN2 ; Voice 2 for music
EFFECTS = GEN3 ; Sound effects (not noise)
.export main
.segment "STARTUP"
.segment "LOWCODE"
.segment "INIT"
.segment "GRCHARS"
.segment "CODE"
; Main program here.
main: jsr Init ; Init the game (load graphic chars, etc...)
restart: jsr StartGame ; Set the starting values of game variables
mainloop: lda Joystick
beq @ccc
jsr ShortDelay
lda #$00
sta Joystick
@ccc: jsr GETIN ; Main loop waiting for keyboard events
sta keyin
lda #$ff
sta Joystick
lda PORTAVIA1
and #%00010000 ; Left
beq left
lda PORTBVIA2
and #%10000000 ; Right
beq right
lda PORTAVIA1
and #%00100000 ; Fire
beq fire
lda #$00
sta Joystick
lda keyin
beq mainloop
cmp #$0D ; Wait for return if the game stopped
bne @norestart
lda Win ; If the game has stopped, restart
bne restart
@norestart: lda keyin
cmp #$58 ; X: increase position of the cannon (right)
beq right
cmp #$5A ; Z: decrease position of the cannon (left)
beq left
cmp #$20 ; Space: fire!
beq fire
cmp #$4D ; M toggle music on/off
bne mainloop
lda VoiceBase
eor #$80
sta VoiceBase
@continue4: jmp mainloop
right: inc CannonPos
lda CannonPos
cmp #15
bcc @continue
lda #15
sta CannonPos
@continue: jmp mainloop
left: dec CannonPos
bmi @zeroc
jmp mainloop
@zeroc: lda #0
sta CannonPos
jmp mainloop
fire: lda Win ; If the game has stopped, restart
bne restart
ldx #0 ; Search for the first free shot
@search: lda FireSpeed,X ; (i.e. whose speed = 0)
beq @found
inx
cpx #NMSHOTS
bne @search
jmp mainloop ; No enough shots allowed in parallel. Abort fire.
@found: lda CannonPos
sta FirePosX,X ; Put the actual cannon position in the X coord.
lda CannonYPos ; Shoot from the last line
sta FirePosY,X
lda #1
sta FireSpeed,X
jmp mainloop
; INIT - INIT - INIT - INIT - INIT - INIT - INIT - INIT - INIT - INIT - INIT
;
; Initialization code: prepare the screen to the correct size, center it and
; load the graphic chars and configure the IRQ handler.
;
; INIT - INIT - INIT - INIT - INIT - INIT - INIT - INIT - INIT - INIT - INIT
Init:
lda #$80 ; Autorepeat on on the keyboard
sta REPEATKE
lda #$08 ; Define screen colour and background (black)
sta VICCOLOR
lda #$90 ; Set a 16 column-wide screen
sta VICCOLNC
lda INITVALC
cmp #$05
beq CenterScreenNTSC ; Load the screen settings
bne CenterScreenPAL
ContInit: sty VICSCRVE ; Centre the screen vertically...
stx VICSCRHO ; ... and horizontally
lda #$FF ; Move the character generator address to $1C00
sta VICCHGEN ; while leaving ch. 128-255 to their original pos.
jsr MovCh ; Load the graphic chars
sei ; Configure the interrupt handler
lda INITVALC
cmp #$05
beq SyncNTSC ; Load the screen settings
bne SyncPAL
ContInit1: stx $9125 ; Set up the timer
sta $9126
lda #<IrqHandler; And the IRQ handler
sta $0314
lda #>IrqHandler
sta $0315
cli
jsr ZeroScore ; A contains 0 after this routine
sta Level
sta HiScore ; Zero the high score
sta HiScore+1
sta PORTAVIA1d ; Prepare VIAs for joystick
lda #$7F
sta PORTBVIA2d
rts
; Synchronize raster on PAL systems
SyncPAL:
; Data for PAL machines. See for example:
; http://www.antimon.org/dl/c64/code/stable.txt
LINES_PAL = 312
CYCLES_PER_LINE_PAL = 71
TIMER_VALUE_PAL = LINES_PAL * CYCLES_PER_LINE_PAL - 2
@loopsync: lda VICRAST ; Synchronization loop
cmp #112
bne @loopsync
lda #<TIMER_VALUE_PAL
ldx #>TIMER_VALUE_PAL
jmp ContInit1
SyncNTSC:
; Data for NTSC machines. See for example:
; http://www.antimon.org/dl/c64/code/stable.txt
LINES_NTSC = 261
CYCLES_PER_LINE_NTSC = 65
TIMER_VALUE_NTSC = LINES_NTSC * CYCLES_PER_LINE_NTSC - 2
@loopsync: lda VICRAST ; Synchronization loop
cmp #70
bne @loopsync
lda #<TIMER_VALUE_NTSC
ldx #>TIMER_VALUE_NTSC
jmp ContInit1
; Screen init value for PAL and NTSC
CenterScreenPAL:
lda #30
sta CannonYPos
lda #$3E ; Set a 31 row-high column
sta VICROWNC
lda #29
sta BunkerY
lda #25*8
sta AlienYLimit
ldx #$12
ldy #$16
jmp ContInit
CenterScreenNTSC:
lda #26
sta CannonYPos
lda #$36 ; Set a 27 row-high column
sta VICROWNC
lda #25
sta BunkerY
lda #22*8
sta AlienYLimit
ldx #$0A
ldy #$10
jmp ContInit
StartGame:
sei
lda #$2F ; Turn on the volume, set multicolour add. colour 2
sta VOLUME
lda #17
sta AlienPosY ; Initial position of aliens
lda #$FD ; No mother ship visible
sta MotherPos
lda #$FF
sta AlienPosYO
sta AliensR1s
sta AliensR2s
sta AliensR3s
sta OldCannonP
lda #8
sta CannonPos ; Initial position of the cannon
lda #$00
sta Direction
sta mothercntr
sta Win
sta IrqCn
sta NOISE
sta AlienPosX
jsr ConfLevel
ldx #NMBOMBS ; Clear all bombs
@loopg: LDA #$0
sta BombSpeed-1,X
lda #$FF
sta BombPosOY-1,X
lda #EMPTY
dex
bne @loopg
ldx #NMSHOTS ; Clear all shoots
@loopp: lda #$00
sta FireSpeed-1,x
sta FirePosX-1,x
sta FirePosY-1,x
sta FirePosOY-1,x
sta FireColOver-1,X
lda #EMPTY
sta FireChOver-1,X
dex
bne @loopp
lda #EMPTY ; Clear the screen
jsr CLS
lda #BLACK
jsr PaintColour
jsr DrawShield
lda #24
sta aliencntr
lda #32
bit VoiceBase
bpl @musicok
lda #$A0
@musicok: sta VoiceBase
jsr draw1l
cli
rts
; Put zero in the current score
ZeroScore: lda #$00
sta Score
sta Score+1
rts
; Configure the level (in Level)
ConfLevel: ldx Level
cpx #NUMLEVEL-1
bmi @validlevel
ldx #NUMLEVEL-1
stx Level
@validlevel:lda LevelsBomb,x
sta BombPeriod
lda LevelsPer,x
sta Period
rts
; Draw the shields in the following positions:
; ------------1---
; 0---4---8---2---
; ** ** ** **
DrawShield: ldx #1
ldy BunkerY
lda Period
sta Colour
lda #BLOCK
jsr DrawChar
inx
jsr DrawChar
ldx #5
jsr DrawChar
inx
jsr DrawChar
ldx #9
jsr DrawChar
inx
jsr DrawChar
ldx #13
jsr DrawChar
inx
jsr DrawChar
ldx #1
dey
lda #BLOCKL
jsr DrawChar
inx
lda #BLOCKR
jsr DrawChar
ldx #5
lda #BLOCKL
jsr DrawChar
inx
lda #BLOCKR
jsr DrawChar
ldx #9
lda #BLOCKL
jsr DrawChar
inx
lda #BLOCKR
jsr DrawChar
ldx #13
lda #BLOCKL
jsr DrawChar
inx
lda #BLOCKR
jmp DrawChar
draw1l:
lda Score ; Load the current score and convert it to BCD
sta Val
lda Score+1
sta Val+1
jsr Bin2BCD
lda #WHITE
ldx #0
jsr PrintRes
lda #(48+$80) ; Write a zero, to multiply x10 the score
jsr DrawChar
lda Score+1
cmp HiScore+1
bcc @noupdate
beq @nou
jsr UpdateHiSc
@nou: lda Score ; Update the high score if needed
cmp HiScore
bcc @noupdate
jsr UpdateHiSc
@noupdate: lda HiScore ; Load the current hi score and convert it to BCD
sta Val
lda HiScore+1
sta Val+1
jsr Bin2BCD
ldx #09
lda #CYAN
jsr PrintRes
lda #(48+$80) ; Write an additional zero
jmp DrawChar
UpdateHiSc: lda Score ; Update the high score
sta HiScore
lda Score+1
sta HiScore+1
rts
PrintRes: sta Colour
ldy #0
lda Res+2 ; Print all the BCD chars
jsr PrintBCD
lda Res+1
jsr PrintBCD
lda Res
jsr PrintBCD
rts
; Copy the graphic chars. They are subjected to be changed during the pixel-by
; pixel movement, so that routine moves only the characters not used as sprites
MovCh: ldx #(LASTCH+1)*8+1
@loop: lda DefChars-1,x
sta GRCHARS1-1,x
dex
bne @loop
rts
; IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ
;
; This is the interrupt handler, called 50 times each second when the VIC-20
; is a PAL unit or 60 when NTSC. It does the following things:
;
; 1 - Calculate positions of the aliens and, if necessary, redraw them
; 2 - Calculate positions of the falling bombs and fire shoots and draw them
; 3 - Update the position of the cannon and draw it
; 5 - Check for collisions and handle explosions
; 6 - Call the sound drivers
; 7 - Jump to the original IRQ handler (for scanning the keyboard, etc).
;
; The user interface is handled outside of the interrupt, in the main program
; loop and the communication with the IRQ handler is made by a set of
; appropriate flags. This approach has the following advantages:
;
; - The speed of the aliens and of the cannon is controlled very precisely as
; the IRQ handler is called at a predictable and stable rate.
; - The code for the visualization and for the user interface (i.e. recognizing
; keyboard and joystick movements) is kept separate.
;
; The main drawback is that the IRQ is supposed to do many things and it should
; be doing that VERY RAPIDLY in order not to mess with the calling order. As a
; rule of thumb, I would say that the IRQ should be completed in less than 5 ms
; at least most of times.
;
; IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ - IRQ
IrqHandler: pha
txa ; Save registers
pha
tya
pha
@still:
ldx #$00
lda MotherPos ; If the mother ship is present, do not mute
cmp #$FD ; sound effects
bne @nomute ; If the mother ship is present, do not mute SFX
stx EFFECTS
@nomute: lda ExplMCnt
beq @noerMexp
dec ExplMCnt
bne @noerMexp
ldx ExplMXpos
ldy #1
lda #EMPTY
jsr DrawChar
@noerMexp: bit Win ; If Win <0 stop the game
bpl @contirq
jmp @exitirq
@contirq: lda IrqCn
cmp Period ; Execute every PERIOD/60 of second
beq @contint
jmp @cont3
@contint: ldx #$00
stx IrqCn
stx NOISE
lda AlienPosY
pha
lsr
lsr
bit VoiceBase ; If music is active, VoiceBase is linked to the
bmi @nomusic ; vertical alien position (music becomes higher
sta VoiceBase ; pitched when aliens scroll down).
@nomusic: lsr
sta AlienPosYc ; Calculate Y pos of aliens in characters
pla
and #7
sta SpriteY ; Calculate Y shift in pixel
jsr EraseAliens
jsr FallBombs ; Make bombs fall. Aliens will be on top of bombs
bit Win ; If Win <0 stop the game
bmi @exitirq
bit Direction ; Increment or decrement the X position,
bmi @negative ; depending on the Direction value
@positive: inc AlienPosX ; The position should be increased
jmp @cont
@negative: dec AlienPosX ; The position should be decreased
@cont: ldx AlienPosY ; Check if the aliens came to bottom of screen
lda AliensR3s
bne @comp
inx
inx
lda AliensR2s
bne @comp
inx
inx
@comp: cpx AlienYLimit
bne @draw
jsr GameOver ; In this case, the game is finished
@draw: lda PixPosX
ror
bcs @altaliens
ldx #ALIEN1
ldy #ALIEN3
bcc @normal
@altaliens: ldx #ALIEN2
ldy #ALIEN4
@normal: stx AlienCode1
sty AlienCode2
jsr DrawAliens
lda AlienMaxX ; Check if the direction should be reversed
cmp #15
bmi @cont2
lda #128
sta Direction ; Invert the direction
inc AlienPosY ; Increment the Y position of the aliens
@cont2: lda AlienMinX ; Check if the direction should be reversed
cmp #1
bcs @cont3 ; Check for the pixel position too
lda SpriteX
bne @cont3
lda #0
sta Direction ; Invert the direction
inc AlienPosY ; Increment the Y position of the aliens
@cont3: lda CannonPos ; Check if the cannon position has changed
cmp OldCannonP
beq @nochange
jsr ClearCannon ; If yes, redraw it
@nochange: jsr DrawCannon
jsr MoveShoots ; Update the position of cannon shots
inc IrqCn
jsr MotherSh ; Check if we should enter the mother ship
@exitirq: bit Win
bmi @nomusic1
jsr Music1
jsr Music2
@nomusic1:
pla ; Restore registers
tay
pla
tax
pla
jmp $EABF ; Jump to the standard IRQ handling routine
; Decide wether the mother ship enters the screen and handle its movement.
MotherSh: lda MotherPos
cmp #$FD
bne @moveship
lda Random+1 ; Get a random number and check if it is less
cmp #SHIPPROB ; than a given threshold
bcc @exitsh
lda mothercntr
cmp #MAXMOTHERS
beq @exitsh
lda #$0F
sta MotherPos
inc mothercntr
lda #0
sta MotherCntr
rts
@moveship: ldx MotherCntr
cpx #3
beq @move
inc MotherCntr
rts
@move: ldx #0
stx MotherCntr
tax
ldy #$1
lda #$A0
clc
adc MotherPos
sta EFFECTS
lda #YELLOW
sta Colour
lda #EMPTY ; Erase the ship in the previous position
inx
inx
jsr DrawChar ; Erase
dec MotherPos
beq @exitsh
ldx MotherPos ; Draw the ship
lda #MOTHER1
jsr DrawChar
inx
lda #MOTHER2
jsr DrawChar
inx
lda #MOTHER3
jsr DrawChar
@exitsh: rts
; Draw the cannon on the screen, at the current position, contained in
; CannonPos (in characters).
DrawCannon: lda CannonPos
sta OldCannonP
tax
ldy CannonYPos ; Vertical position of the cannon
lda #WHITE ; Cannon in white
sta Colour
lda #CANNON ; Cannon char
jmp DrawChar
; Clear the cannon on the screen, at the current position, contained in
; OldCannonP (in characters).
ClearCannon:
ldx OldCannonP
ldy CannonYPos ; Vertical position of the cannon
lda #WHITE ; Cannon in white
sta Colour
lda #EMPTY ; Space
jmp DrawChar
; Erase aliens. Calculate AlienCurrY (in chars)
EraseAliens:
ldy AlienPosYc
dey
lda #<MEMSCR ; Inlined version of CharPos without colour
sta POSCHARPT
lda #>MEMSCR
sta POSCHARPT+1
tya
asl ; 16 columns per line. Multiply!
asl
asl
asl ; If it shifts an 1 in the carry, this means that
bcc @nocorr ; we need to write in the bottom-half of the screen
inc POSCHARPT+1
clc
@nocorr: adc POSCHARPT
sta POSCHARPT
lda INITVALC
cmp #$05
bne @skipwait ; Achieving flicker-less alien representation in
jsr Waitrast ; NTSC is more difficult, we need to wait in some
@skipwait: ldy #16*7 ; situations.
lda #EMPTY
@loop: sta (POSCHARPT),y ; A little bit of loop unrolling
dey
sta (POSCHARPT),y
dey
sta (POSCHARPT),y
dey
sta (POSCHARPT),y
dey
sta (POSCHARPT),y
dey
sta (POSCHARPT),y
dey
sta (POSCHARPT),y
dey
sta (POSCHARPT),y
dey
bne @loop
sta (POSCHARPT),y
rts
; Calculate SpriteX and PixPos from AlienPosX. It is a little tricky, as
; AlienPosX can be negative and some corrections have to be done.
CalcXpos: lda AlienPosX ; Calculate the position in characters
bpl @positive
eor #$FF ; Calculate the two complement
clc
adc #1
pha
and #7
sta SpriteX
pla
@positive: lsr ; Divide by 8
lsr
lsr
sta PixPosX ; Store it in PixPosX
lda AlienPosX ; Calculate the position in characters
bpl @positive1
lda #$FF
sec
sbc PixPosX
sta PixPosX
lda #7
sec
sbc SpriteX
sta SpriteX
rts
@positive1: and #7
sta SpriteX
rts
; Draw aliens on the screen. They are several lines with at most 8 aliens
; each. The presence of an alien in the first row is given by bits in the
; AliensR1 byte. An alien is present at the beginning of the game (or level)
; and can be destroyed when hit. In this case, the corresponding bit in the
; AliensR1 byte is set to 0. Same for AliensR2 and AliensR3.
; The vertical position of the aliens should be in AliensPosY (in pixels) and
; AlienCurrY (in chars) should have been already calculated.
DrawAliens:
lda AlienPosYc
sta AlienCurrY
jsr CalcXpos
; Create the first sprite
lda #<(GRCHARS1+SPRITE1A*8)
sta SPRITECH
lda #>(GRCHARS1+SPRITE1A*8)
sta SPRITECH+1
jsr ClearSprite
lda AlienCode1
sta CharCode
jsr LoadSprite
lda #<(GRCHARS1+SPRITE2A*8)
sta SPRITECH
lda #>(GRCHARS1+SPRITE2A*8)
sta SPRITECH+1
jsr ClearSprite
lda AlienCode2
sta CharCode
jsr LoadSprite
lda #$ff ; Reset the min/max positions of aliens in ch.
sta AlienMinX
lda #$00
sta AlienMaxX
lda AliensR1s ; Top line of aliens
sta AliensR
lda #SPRITE1A ; AlienCode1
sta CharCode
lda #RED
sta Colour
jsr AlienLoop
inc AlienCurrY ; Second line of aliens
inc AlienCurrY
lda #SPRITE2A ; AlienCode1
sta CharCode
lda #CYAN
sta Colour
lda AliensR2s
sta AliensR
jsr AlienLoop
inc AlienCurrY ; Third line of aliens
inc AlienCurrY
lda #GREEN
sta Colour
lda #SPRITE1A ; AlienCode1
sta CharCode
lda AliensR3s
sta AliensR
jmp AlienLoop
; Wait for the wanted rasterline (NTSC only)
Waitrast:
Waitrast1: lda VICRAST ; Wait if there is a risk of flicker
lsr ; Divide by 4 as VICRAST contains bits 1:8 of
lsr ; the raster line position -> position in chars
clc
sta TmpScan
clc
adc #5
cmp AlienPosYc
bmi @noflicker
lda TmpScan
sec
sbc #10
cmp AlienPosYc
bpl @noflicker
jmp Waitrast1
@noflicker:
rts
; End of PAL-only code
; Draw a line of aliens (the sprite should have been already created)
AlienLoop: ldy AlienCurrY
ldx #0
jsr PosChar
ldx #8*2
@loop: dex ; X contains the alien index in the line
asl AliensR ; Check if the alien is still alive
bcc @ret
txa
clc
adc PixPosX ; To be added to the pos. in characters
tay ; to obtain the actual position where to draw al.
dey
cpy AlienMinX ; Update the minimum and maximum value of positions
bcs @nomin
sty AlienMinX
@nomin: cpy AlienMaxX
bcc @nomax
sty AlienMaxX
@nomax: lda CharCode
sta (POSCHARPT),Y ; Sprite char A
lda Colour
sta (POSCOLPT),Y
iny
sta (POSCOLPT),Y
clc
lda CharCode
adc #2
sta (POSCHARPT),Y ; Sprite char C
tya
adc #15
tay
lda CharCode
adc #1
sta (POSCHARPT),Y ; Sprite char B
lda Colour
sta (POSCOLPT),Y
iny
sta (POSCOLPT),Y
lda CharCode
adc #3
sta (POSCHARPT),Y ; Sprite char D
@ret: dex
bne @loop
rts
; Add the value contained in A to the current score