-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathriver-raid.ctl
More file actions
3794 lines (3794 loc) · 211 KB
/
river-raid.ctl
File metadata and controls
3794 lines (3794 loc) · 211 KB
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
> $4000 @start
> $4000
> $4000 COLOR_BLACK EQU $00
> $4000 COLOR_BLUE EQU $01
> $4000 COLOR_RED EQU $02
> $4000 COLOR_MAGENTA EQU $03
> $4000 COLOR_GREEN EQU $04
> $4000 COLOR_CYAN EQU $05
> $4000 COLOR_YELLOW EQU $06
> $4000 COLOR_WHITE EQU $07
> $4000
> $4000 ; Terrain colors
> $4000 COLOR_RIVER EQU COLOR_BLUE
> $4000 COLOR_BANK EQU COLOR_GREEN
> $4000
> $4000 ; Player colors
> $4000 COLOR_PLAYER_1 EQU COLOR_YELLOW
> $4000 COLOR_PLAYER_2 EQU COLOR_CYAN
> $4000
> $4000 ; Object colors
> $4000 COLOR_HELICOPTER EQU COLOR_YELLOW
> $4000 COLOR_SHIP EQU COLOR_CYAN
> $4000 COLOR_BALLOON EQU COLOR_YELLOW
> $4000 COLOR_FUEL EQU COLOR_MAGENTA
> $4000 COLOR_ROCK EQU COLOR_RED
> $4000 COLOR_MISSILE EQU COLOR_GREEN
> $4000 COLOR_EXPLOSION EQU COLOR_GREEN
> $4000
> $4000 ; The sprite does not impose its own color; existing screen attributes are preserved.
> $4000 ATTRIBUTES_INHERIT EQU $00
> $4000
> $4000 EXT_ATTR_INK EQU $10
> $4000 EXT_ATTR_PAPER EQU $11
> $4000 EXT_ATTR_AT EQU $16
> $4000
> $4000 ; ASCII character codes
> $4000 CHAR_ENTER EQU $0D
> $4000
> $4000 ; Z80 opcodes used in self-modifying code
> $4000 OPCODE_NOP EQU $00
> $4000 OPCODE_JP EQU $C3
> $4000 OPCODE_XOR_B EQU $A8
> $4000 OPCODE_OR_B EQU $B0
> $4000
> $4000 ; ULA port speaker control
> $4000 ULA_SPEAKER_ON EQU $10
> $4000 ULA_SPEAKER_OFF EQU $00
> $4000
> $4000 INPUT_INTERFACE_KEYBOARD EQU $00
> $4000 INPUT_INTERFACE_SINCLAIR EQU $01
> $4000 INPUT_INTERFACE_KEMPSTON EQU $02
> $4000 INPUT_INTERFACE_CURSOR EQU $03
> $4000
> $4000 OVERVIEW_MODE_OFF EQU $00
> $4000 OVERVIEW_MODE_ON EQU $01
> $4000
> $4000 GAME_MODE_BIT_TWO_PLAYERS EQU 0
> $4000
> $4000 PLAYER_1 EQU $01
> $4000 PLAYER_2 EQU $02
> $4000
> $4000 LIVES_INITIAL EQU $04
> $4000
> $4000 SPEED_STOP EQU $01
> $4000 SPEED_SLOW EQU $01
> $4000 SPEED_NORMAL EQU $02
> $4000 SPEED_FAST EQU $04
> $4000
> $4000 SOUND_BIT_FIRE EQU 0
> $4000 SOUND_BIT_SPEED_NOT_FAST EQU 1
> $4000 SOUND_BIT_SPEED_CHANGED EQU 2
> $4000 SOUND_BIT_LOW_FUEL EQU 3
> $4000 SOUND_BIT_BONUS_LIFE EQU 4
> $4000 SOUND_BIT_EXPLODING EQU 5
> $4000
> $4000 SOUND_SPEED_MASK EQU 1<<SOUND_BIT_SPEED_NOT_FAST|1<<SOUND_BIT_SPEED_CHANGED
> $4000 SOUND_SPEED_NORMAL EQU 1<<SOUND_BIT_SPEED_NOT_FAST
> $4000 SOUND_SPEED_FAST EQU 1<<SOUND_BIT_SPEED_CHANGED
> $4000 SOUND_SPEED_SLOW EQU 1<<SOUND_BIT_SPEED_NOT_FAST|1<<SOUND_BIT_SPEED_CHANGED
> $4000
> $4000 TANK_SHELL_STATE_UNITIALIZED EQU $00
> $4000 TANK_SHELL_MASK_SPEED EQU $07
> $4000 TANK_SHELL_BIT_EXPLODING EQU 5
> $4000 TANK_SHELL_BIT_FLYING EQU 7
> $4000 TANK_SHELL_TRAJECTORY_MAX_STEP EQU $08
> $4000
> $4000 HELICOPTER_MISSILE_STEP EQU $08
> $4000
> $4000 ; Tick-based timing constants for frame parity checks.
> $4000 ; state_tick bit 0 alternates every frame (even/odd).
> $4000 ; state_tick bit 2 alternates every 4 frames (fuel blink).
> $4000 TICK_MASK_FRAME_PARITY EQU $01
> $4000 TICK_PARITY_EVEN EQU $00
> $4000 TICK_PARITY_ODD EQU $01
> $4000 TICK_MASK_FUEL_BLINK EQU $04
> $4000
> $4000 FIGHTER_POSITION_LEFT_INIT EQU $E8
> $4000 FIGHTER_POSITION_LEFT_LIMIT EQU $00
> $4000 FIGHTER_POSITION_RIGHT_INIT EQU $04
> $4000 FIGHTER_POSITION_RIGHT_LIMIT EQU $E8
> $4000
> $4000 ; Object characteristics summary:
> $4000 ; +---------------+------+--------+------------+----------------------+
> $4000 ; | Object | Type | Points | Speed | Special |
> $4000 ; +---------------+------+--------+------------+----------------------+
> $4000 ; | Helicopter | $01 | 60 | 2 px/frame | Patrols, no missiles |
> $4000 ; | Ship | $02 | 30 | 2 px/frame | Patrols river |
> $4000 ; | Helicopter+ | $03 | 150 | 2 px/frame | Patrols, FIRES |
> $4000 ; | Tank | $04 | 250 | 2 px/frame | Fires parabolic |
> $4000 ; | Fighter | $05 | 100 | 4 px/frame | Fast, wraps screen |
> $4000 ; | Balloon | $06 | 60 | 2 px/frame | Floats, bounces |
> $4000 ; | Fuel Depot | $07 | 80 | 0 (static) | Refuels plane |
> $4000 ; | Bridge | -- | 500 | 0 (static) | Increases activation |
> $4000 ; +---------------+------+--------+------------+----------------------+
> $4000 ; Note: Points encoded as value/10 (e.g., POINTS_SHIP=$03 = 30 points).
> $4000 ; Helicopter+ (ADV) fires missiles at player unlike regular helicopter.
> $4000
> $4000 POINTS_SHIP EQU $03
> $4000 POINTS_HELICOPTER_REG EQU $06
> $4000 POINTS_BALLOON EQU $06
> $4000 POINTS_FUEL EQU $08
> $4000 POINTS_FIGHTER EQU $10
> $4000 POINTS_HELICOPTER_ADV EQU $15
> $4000 POINTS_TANK EQU $25
> $4000 POINTS_BRIDGE EQU $50
> $4000
> $4000 OBJECT_HELICOPTER_REG EQU $01
> $4000 OBJECT_SHIP EQU $02
> $4000 OBJECT_HELICOPTER_ADV EQU $03
> $4000 OBJECT_TANK EQU $04
> $4000 OBJECT_FIGHTER EQU $05
> $4000 OBJECT_BALLOON EQU $06
> $4000 OBJECT_FUEL EQU $07
> $4000
> $4000 ; Object definition byte bit fields (byte 2 of viewport_slots entries)
> $4000 ; +-------+--------------------------------------------------+
> $4000 ; | Bit | Meaning |
> $4000 ; +-------+--------------------------------------------------+
> $4000 ; | 0-2 | Object type (OBJECT_* constants, 0-7) |
> $4000 ; | 3 | Rock flag (1=rock decoration, 0=interactive) |
> $4000 ; | 4 | Alt shell init (set on direction reversal, triggers alternate tank shell init) |
> $4000 ; | 5 | Tank location (1=river bank, 0=bridge) |
> $4000 ; | 6 | Orientation (1=right-facing, 0=left-facing) |
> $4000 ; | 7 | Activation (1=active/interactive, 0=spawning) |
> $4000 ; +-------+--------------------------------------------------+
> $4000 SLOT_BIT_ROCK EQU $03
> $4000 SLOT_BIT_ALT_SHELL_INIT EQU $04
> $4000 SLOT_BIT_TANK_ON_BANK EQU $05
> $4000 SLOT_BIT_ORIENTATION EQU $06
> $4000 SLOT_BIT_ACTIVATION EQU $07
> $4000 SLOT_MASK_OBJECT_TYPE EQU $07
> $4000
> $4000 SIZE_LEVEL_SLOTS EQU $0100
> $4000
> $4000 SET_MARKER_EMPTY_SLOT EQU $00
> $4000 SET_MARKER_END_OF_SET EQU $FF
> $4000
> $4000 SPRITE_PLANE_WIDTH_TILES EQU $02
> $4000 SPRITE_PLANE_HEIGHT_PIXELS EQU $08
> $4000 SPRITE_PLANE_FRAME_SIZE EQU SPRITE_PLANE_WIDTH_TILES * SPRITE_PLANE_HEIGHT_PIXELS
> $4000 SPRITE_PLANE_ATTRIBUTES EQU COLOR_RIVER<<3|COLOR_PLAYER_1
> $4000
> $4000 SPRITE_3BY1_ENEMY_WIDTH_TILES EQU $03
> $4000 SPRITE_3BY1_ENEMY_HEIGHT_PIXELS EQU $08
> $4000 SPRITE_3BY1_ENEMY_FRAME_SIZE EQU SPRITE_3BY1_ENEMY_WIDTH_TILES * SPRITE_3BY1_ENEMY_HEIGHT_PIXELS
> $4000 SPRITE_3BY1_ENEMY_ATTRIBUTES EQU COLOR_RIVER<<3|COLOR_HELICOPTER
> $4000
> $4000 SPRITE_SHIP_ATTRIBUTES EQU COLOR_RIVER<<3|COLOR_SHIP
> $4000 SPRITE_TANK_ON_ROAD_ATTRIBUTES EQU ATTRIBUTES_INHERIT
> $4000 SPRITE_TANK_ON_BANK_ATTRIBUTES EQU COLOR_BLACK<<3|COLOR_BANK
> $4000 SPRITE_FIGHTER_ATTRIBUTES EQU ATTRIBUTES_INHERIT
> $4000
> $4000 SPRITE_ROTOR_WIDTH_TILES EQU $02
> $4000 SPRITE_ROTOR_HEIGHT_PIXELS EQU $02
> $4000 SPRITE_ROTOR_FRAME_SIZE EQU SPRITE_ROTOR_WIDTH_TILES * SPRITE_ROTOR_HEIGHT_PIXELS
> $4000 SPRITE_ROTOR_ATTRIBUTES EQU COLOR_RIVER<<3|COLOR_HELICOPTER
> $4000
> $4000 SPRITE_BALLOON_WIDTH_TILES EQU $02
> $4000 SPRITE_BALLOON_HEIGHT_PIXELS EQU $10
> $4000 SPRITE_BALLOON_FRAME_SIZE EQU SPRITE_BALLOON_WIDTH_TILES * SPRITE_BALLOON_HEIGHT_PIXELS
> $4000 SPRITE_BALLOON_ATTRIBUTES EQU COLOR_RIVER<<3|COLOR_BALLOON
> $4000
> $4000 ; There is only one frame, so it's size is irrelevant.
> $4000 SPRITE_FUEL_STATION_WIDTH_TILES EQU $02
> $4000 SPRITE_FUEL_STATION_HEIGHT_PIXELS EQU $19
> $4000 SPRITE_FUEL_STATION_FRAME_SIZE EQU $0000
> $4000 SPRITE_FUEL_STATION_ATTRIBUTES EQU COLOR_RIVER<<3|COLOR_FUEL
> $4000
> $4000 SPRITE_ROCK_WIDTH_TILES EQU $03
> $4000 SPRITE_ROCK_HEIGHT_PIXELS EQU $10
> $4000 SPRITE_ROCK_FRAME_SIZE EQU SPRITE_ROCK_WIDTH_TILES * SPRITE_ROCK_HEIGHT_PIXELS
> $4000 SPRITE_ROCK_ATTRIBUTES EQU COLOR_ROCK<<3|COLOR_BANK
> $4000
> $4000 SPRITE_MISSILE_WIDTH_TILES EQU $01
> $4000 SPRITE_MISSILE_HEIGHT_PIXELS EQU $08
> $4000 SPRITE_MISSILE_FRAME_SIZE_BYTES EQU SPRITE_MISSILE_WIDTH_TILES * SPRITE_MISSILE_HEIGHT_PIXELS
> $4000 SPRITE_MISSILE_ATTRIBUTES EQU COLOR_RIVER<<3|COLOR_MISSILE
> $4000
> $4000 SPRITE_HELICOPTER_MISSILE_WIDTH_TILES EQU $01
> $4000 SPRITE_HELICOPTER_MISSILE_ATTRIBUTES EQU ATTRIBUTES_INHERIT
> $4000
> $4000 ; Shell sprite is a truncated missile sprite, so the frame size should be calculated
> $4000 ; based on the original height.
> $4000 SPRITE_SHELL_WIDTH_TILES EQU $01
> $4000 SPRITE_SHELL_HEIGHT_PIXELS EQU $01
> $4000 SPRITE_SHELL_FRAME_SIZE_BYTES EQU SPRITE_SHELL_WIDTH_TILES * SPRITE_MISSILE_HEIGHT_PIXELS
> $4000 SPRITE_SHELL_ATTRIBUTES EQU $00
> $4000
> $4000 SPRITE_SHELL_EXPLOSION_WIDTH_TILES EQU $02
> $4000 SPRITE_SHELL_EXPLOSION_HEIGHT_PIXELS EQU $10
> $4000 SPRITE_SHELL_EXPLOSION_FRAME_SIZE_BYTES EQU $0000
> $4000 SPRITE_SHELL_EXPLOSION_ATTRIBUTES EQU COLOR_RIVER<<3|COLOR_EXPLOSION
> $4000
> $4000 PLANE_COORDINATE_Y EQU $80
> $4000
> $4000 FUEL_CHECK_INTERVAL EQU $03
> $4000 FUEL_INTAKE_AMOUNT EQU $04
> $4000 FUEL_LEVEL_EMPTY EQU $00
> $4000 FUEL_LEVEL_LOW EQU $C0
> $4000 FUEL_LEVEL_ALMOST_FULL EQU $FC
> $4000 FUEL_LEVEL_FULL EQU $FF
> $4000
> $4000
> $4000 ; Player is actively playing: plane is rendered, objects can be interacted with.
> $4000 GAMEPLAY_MODE_NORMAL EQU $00
> $4000 ; Initial level scroll-in at start of play: plane is not rendered, objects
> $4000 ; don't activate. Runs for $28 iterations with SPEED_FAST before switching
> $4000 ; to GAMEPLAY_MODE_NORMAL.
> $4000 GAMEPLAY_MODE_SCROLL_IN EQU $01
> $4000 ; Overview mode: plane is not rendered, objects don't activate, auto-scrolls
> $4000 ; through levels. Set by init_state and used by the overview routine.
> $4000 GAMEPLAY_MODE_OVERVIEW EQU $02
> $4000 ; Player is refueling at a fuel station.
> $4000 GAMEPLAY_MODE_REFUEL EQU $06
> $4000
> $4000 COLLISION_MODE_NONE EQU $00
> $4000 COLLISION_MODE_FUEL_DEPOT EQU $01
> $4000 COLLISION_MODE_MISSILE EQU $02
> $4000 COLLISION_MODE_FIGHTER EQU $03
> $4000 COLLISION_MODE_HELICOPTER_MISSILE EQU $04
> $4000
> $4000 TANK_SHELL_INACTIVE EQU $00
> $4000 TANK_SHELL_ACTIVE EQU $01
> $4000
> $4000 VIEWPORT_HEIGHT EQU $88
> $4000
> $4000 ACTIVATION_INTERVAL_NORMAL EQU $1F
> $4000 ACTIVATION_INTERVAL_FAST EQU $0F
> $4000
> $4000 SCROLL_IN_ITERATIONS EQU $28
> $4000
> $4000 TERRAIN_PROFILE_SIZE EQU $10
> $4000
> $4000 EXPLOSION_ANIM_FRAMES EQU $10
> $4000 EXPLOSION_SOUND_TICKS EQU $18
> $4000
> $4000 BONUS_LIFE_SOUND_TICKS EQU $40
> $4000
> $4000 SCORE_DIGIT_COUNT EQU $06
> $4000
> $4000 IM1_VECTOR_TABLE_HI EQU $3F
> $4000
> $4000 ; STRUCTURES
> $4000 ; ----------
> $4000 ;
> $4000 ; OBJECT_DEFINITION ::
> $4000 ;
> $4000 ; Bits 0..2 represent an interactive object type.
> $4000 ; OBJECT_DEFINITION_MASK_INTERACTIVE_TYPE = $07,
> $4000 ;
> $4000 ; Bits 0..1 represent a rock type type.
> $4000 ; OBJECT_DEFINITION_MASK_ROCK_TYPE = $03,
> $4000 ;
> $4000 ; Bit 3 defines whether the slot contains an interactive object (reset)
> $4000 ; or a rock (set).
> $4000 ; OBJECT_DEFINITION_BIT_ROCK = 3,
> $4000 ;
> $4000 ; Bit 4 is set when a tank reverses direction (via #R$7525). On the next
> $4000 ; processing tick, it causes the tank to use the alternate shell
> $4000 ; initialization path at #R$735E (check_shell_init_condition) instead of
> $4000 ; the normal path at #R$7343 (init_tank_shell_state).
> $4000 ; OBJECT_DEFINITION_BIT_ALT_SHELL_INIT = 4,
> $4000 ;
> $4000 ; Bit 5 defines a tank location: bridge (unset) or river bank (set).
> $4000 ; OBJECT_DEFINITION_BIT_TANK_LOCATION = 5,
> $4000 ;
> $4000 ; Bit 6 defines object orientation: left (unset) or right (set).
> $4000 ; OBJECT_DEFINITION_BIT_TANK_ORIENTATION = 6,
> $4000 ;
> $4000 ; Bit 7 is an activation flag used by operate_viewport_slots to track
> $4000 ; whether an object has been activated on its first frame.
@ $4000 org
@ $4000 equ=KEYBOARD=$02BF
@ $4000 equ=BEEPER=$03B5
@ $4000 equ=CHAN_OPEN=$1601
@ $4000 equ=OUT_NUM_1=$1A1B
@ $4000 equ=PR_STRING=$203C
@ $4000 equ=LAST_K=$5C08
@ $4000 equ=UDG=$5C7B
@ $4000 equ=INT_VECTOR_TABLE_HI=$FC
@ $4000 equ=INT_VECTOR_ENTRY=$FE
@ $4000 label=screen_pixels
b $4000 Screen pixels.
D $4000 #UDGTABLE { #SCR(loading) } TABLE#
$4000,6144,16
@ $5800 label=screen_attributes
b $5800 Screen attributes.
$5800,32,16
@ $5820 label=screen_attributes_row_1
b $5820 Screen attributes row 1.
$5820,512,16
@ $5A20 label=screen_attributes_row_16
b $5A20 Screen attributes row 16 (boundary for visible sprite area).
$5A20,32,16
@ $5A40 label=screen_attributes_row_17
b $5A40 Screen attributes row 17 (start of lower screen area).
$5A40,192,16
@ $5B00 label=screen_row_table
b $5B00 Screen row address lookup table (64 entries × 2 bytes = 128 bytes).
D $5B00 Pre-computed table mapping row indices (0-63) to screen memory addresses. Used by #R$683B for fast vertical scrolling. This table provides O(1) lookup: screen_addr = screen_row_table[row_index × 2].
W $5B00,128,2
@ $5B80 label=data_unused_5B80
u $5B80 Unused area.
@ $5C78 label=int_counter
g $5C78 Interrupt counter. Incremented by the interrupt handler.
@ $5C79 label=data_unused_5C79
u $5C79
@ $5CD2 label=init
c $5CD2 Game initialization and interrupt setup
N $5CD2 This is the main entry point invoked by the BASIC loader. It performs one-time initialization of the game engine, including setting up the custom interrupt handler (IM 2 mode) and initializing global pointers.
$5CD2 Initialize #R$9283 to point to #R$6BB0.
@ $5CD8 nowarn
$5CD8 Initialize #R$8B08 to point to #R$6136
@ $5CDE isub=LD A,OPCODE_JP
$5CDE Write the JP opcode to the address that all vector table entries will point to.
@ $5CE0 isub=LD (INT_VECTOR_ENTRY<<8|INT_VECTOR_ENTRY),A
@ $5CE3 nowarn
$5CE3 Write the interrupt handler address, completing the JP #R$6BDB instruction.
@ $5CE6 isub=LD (INT_VECTOR_ENTRY<<8|INT_VECTOR_ENTRY+1),HL
@ $5CE9 isub=LD HL,INT_VECTOR_TABLE_HI<<8
$5CE9 Point #REGhl to the start of interrupt vector table.
$5CEC Prepare to iterate 256 times.
@ $5CEE label=int_vector_table_write_loop
@ $5CEE isub=LD (HL),INT_VECTOR_ENTRY
$5CEE Write INT_VECTOR_ENTRY to the current vector table entry.
$5CF0
$5CF1
@ $5CF3 isub=LD (HL),INT_VECTOR_ENTRY
$5CF3 Write INT_VECTOR_ENTRY to the final vector table entry.
@ $5CF5 isub=LD A,INT_VECTOR_TABLE_HI
$5CF5 Set the I register to the high byte of the interrupt vector table address.
$5CF9
$5CFD,2 Set interrupt mode 2 (vectored interrupts).
$5D00 Store the address of #R$8182 in #R$5F7E.
@ $5D06 label=return_to_control_selection
@ $5D06 isub=LD A,IM1_VECTOR_TABLE_HI
c $5D06 Return to control selection dialog
N $5D06 This entry point is used when returning to the control selection dialog from the game (via #R$6BD2) or from the overview mode. It switches back to IM 1, then calls clear_and_setup to display the control selection dialog.
N $5D06 .
N $5D06 After the user selects controls and game mode, execution continues at #R$5D10.
$5D06 Restore standard IM 1 interrupt vector table.
$5D08 Set the I register to $3F for IM 1 mode.
$5D0A Set interrupt mode 1.
$5D0C Enable interrupts.
$5D0D Display the control selection dialog.
@ $5D10 label=start_gameplay_or_overview
@ $5D10 isub=LD A,INT_VECTOR_TABLE_HI
c $5D10 Start gameplay or overview mode based on user selection
N $5D10 This routine is called after the user selects controls and game mode from the control selection dialog.
$5D10,6 Restore IM 2 (vectored interrupts) and enable interrupts.
$5D17 Copy selected control type to state_input_interface.
@ $5D20 isub=CP OVERVIEW_MODE_ON
$5D1D If overview mode was selected, start overview; otherwise initialize and start gameplay.
@ $5D2B label=start_overview
c $5D2B Start overview mode
N $5D2B This routine is called when overview mode is selected (either from the control selection dialog or after game over).
@ $5D35 label=start_gameplay
c $5D35 Start gameplay mode
N $5D35 This routine is called when the player presses Enter during gameplay to restart.
@ $5D3F label=starting_bridges
b $5D3F Array of possible starting bridge values.
R $5D3F Index of list element is specified by the second and third bits of the #R$923A.
R $5D3F The values correspond to the dialog rendered as #R$792A.
@ $5D43 label=state_overview_start_scroll
g $5D43
@ $5D44 label=init_state
c $5D44 Initialize game state for overview/demo mode.
N $5D44 This routine sets up the initial game state used by the overview (attract mode) routine. It initializes player positions, scores, lives, terrain state, and sets the gameplay mode to GAMEPLAY_MODE_OVERVIEW.
$5D44 Initialize plane X position to $78. Why isn't it $80?
$5D49 Set starting bridges for both players based on game mode.
$5D4C Initialize viewport objects list (set pointer, mark as empty).
@ $5D52 isub=LD (HL),SET_MARKER_END_OF_SET
@ $5D54 isub=LD A,ACTIVATION_INTERVAL_NORMAL
$5D54 Set normal activation timing (every 32 frames).
@ $5D56 isub=LD (state_activation_interval),A
$5D59 Set border to black, silence speaker, clear tank shell state and sound flags.
@ $5D5D isub=LD (state_tank_shell),A
@ $5D63 ignoreua=$4C83
$5D63 Initialize terrain element (C=$83: river center position, B=$4C: row offset).
$5D6A Initialize terrain profile, gameplay mode, and speed to $02 (GAMEPLAY_MODE_OVERVIEW / SPEED_NORMAL).
@ $5D75 isub=LD (state_bridge_destroyed),A
$5D75 Store $02 to #R$5F6D (unclear why $02 is used here).
@ $5D78 isub=LD HL,"00"
M $5D78,18 Initialize all player scores to zero.
C $5D78,c3
C $5D7B,h
$5D8D Set level fragment number and terrain position to 1.
@ $5D95 isub=LD HL,LIVES_INITIAL<<8|LIVES_INITIAL
$5D95 Set both players' lives to 4.
$5D9B,3 Set current player to PLAYER_1.
@ $5D9F label=decrease_lives_player_2
c $5D9F Decrease player 2 lives
@ $5DA6 label=play
c $5DA6 Initialize and start gameplay mode
D $5DA6 This routine prepares the game for play and is called when starting a new game or after losing a life.
$5DA6 Initialize island line index (starting line for island rendering).
@ $5DAB isub=LD A,ACTIVATION_INTERVAL_NORMAL
$5DAB Initialize activation interval (every 32 frames).
$5DB0
@ $5DB4 isub=LD D,COLOR_RIVER<<3|COLOR_BANK
$5DB4 Clear screen with PAPER RIVER, INK BANK.
$5DB9
@ $5DBF isub=LD BC,status_line_2 - status_line_1
$5DBC Print status line 1.
$5DC5 Initialize tick counter.
$5DCA Open channel 1 (upper screen).
@ $5DD0 isub=LD BC,status_line_3 - status_line_2
$5DCD Print status line 2.
$5DD6 Open channel 2 (lower screen).
$5DDB
$5DDE Initialize terrain fragment counter.
$5DE3 Clear state variables.
@ $5DF1 isub=LD A,FUEL_LEVEL_FULL
$5DF1 Initialize fuel level.
$5DF6 Initialize scroll offset.
$5DFD
$5E00 Initialize X position (horizontal center).
$5E05 Initialize viewport_slots list.
@ $5E0B isub=LD (HL),SET_MARKER_END_OF_SET
$5E0B Mark viewport objects list as empty.
$5E0D Initialize exploding_fragments list.
$5E13 Mark exploding fragments list as empty.
$5E15
$5E1B Open channel 1 for score display.
@ $5E20 isub=LD A,EXT_ATTR_AT
$5E20 AT 1,5
@ $5E29 isub=LD A,EXT_ATTR_INK
@ $5E2C isub=LD A,COLOR_PLAYER_1
$5E29 INK PLAYER_1
@ $5E32 isub=LD BC,state_score_player_2_low - state_score_player_1_low
$5E2F Print player 1 score.
$5E38 Open channel 2.
@ $5E40 isub=LD BC,status_line_4_end - status_line_4
$5E3D Print status line 4.
@ $5E49 isub=ADD A,"1"
$5E46 Print game mode digit (1 or 2 player).
$5E4C Open channel 1.
$5E51 Set terrain position to $FF (forces terrain regeneration).
$5E56 Initialize terrain profile number.
$5E5B Open channel 2.
$5E5E,8 Initialize level fragment number, gameplay mode, and bridge destroyed flag.
@ $5E69 isub=LD A,"h"
$5E69 Set LAST_K to 'h' to start in paused state.
$5E6E Clear sound flags and tank shell state.
@ $5E76 isub=LD A,SPEED_FAST
$5E76 Set speed for scroll-in animation.
@ $5E7B ignoreua=$4C83
$5E7B Initialize terrain element (C=$83: river center position, B=$4C: row offset).
$5E82
$5E85
@ $5E88 isub=LD B,SCROLL_IN_ITERATIONS
$5E88 Set loop counter for scroll-in (40 iterations).
@ $5E8A label=scroll_in_loop
$5E8A Save loop counter.
$5E8B Increment tick counter.
$5E8F
$5E92
$5E95
@ $5E98 isub=LD A,SPEED_FAST
$5E98 Maintain speed during scroll-in.
$5E9D Restore loop counter.
$5E9E Loop until scroll-in complete.
$5EA0 Clear sound flags.
$5EA5 Set gameplay mode to normal (ready for player control).
$5EA8
@ $5EAB isub=LD A,CHAR_ENTER
$5EAB Set last key to Enter (wait for start input).
@ $5EB3 isub=CP PLAYER_2
$5EB0 Decrement current player's lives.
@ $5EBC label=after_life_lost
$5EBC
@ $5EBF label=wait_for_start_input
$5EBF
$5EC2 Enable interrupts.
@ $5EC6 isub=CP CHAR_ENTER
$5EC3 If any key except Enter pressed, start game.
@ $5ECD isub=CP INPUT_INTERFACE_KEMPSTON
$5ECA If not Kempston joystick, keep waiting.
$5ED2 Read Kempston joystick; if centered, keep waiting.
@ $5EDB label=start_game
$5EDB Clear bridge destroyed flag.
@ $5EEE label=state_terrain_fragment_counter
g $5EEE Terrain fragment render counter (0-7). Tracks which of the 8 terrain fragments to render next. Incremented after each fragment, wraps at 8.
@ $5EEF label=state_tick
g $5EEF Frame tick (0-255). Incremented each game frame for animation timing. Bit 0 alternates every frame, bits 0-1 cycle every 4 frames, etc.
@ $5EF0 label=state_bridge_index
g $5EF0 Current bridge number (0-47).
@ $5EF1 label=state_input_readings
g $5EF1 Raw input port value from last keyboard/joystick read. Format depends on input interface type.
@ $5EF2 label=state_tank_shell
g $5EF2 Road tank center flag ($00 = inactive, $01 = road tank is at center X=$80).
@ $5EF3 label=state_striker_y
g $5EF3 Y coordinate of the active striker (missile or plane) in pixels (0-255). $00 when no missile is active.
@ $5EF4 label=state_striker_x
g $5EF4 X coordinate of the active striker (missile or plane) in pixels (0-255).
@ $5EF5 label=state_collision_mode
g $5EF5 Collision detection mode for current render pass. Determines which collision handler to invoke on pixel overlap.
@ $5EF6 label=state_collision_y
g $5EF6 Y coordinate where collision was detected, in pixels.
@ $5EF7 label=ptr_plane_sprite
g $5EF7 Pointer to current plane sprite data. Points into #R$82C5 sprite bank, offset by speed for animation frame selection.
W $5EF7
@ $5EF9 label=state_island_render_idx
g $5EF9 Island rendering line counter. Tracks current scanline during island terrain rendering (0-23).
@ $5EFA label=state_island_profile_idx
g $5EFA Island terrain profile index (0-7). Selects which of 8 island shapes from #R$8063 to render.
@ $5EFB label=state_island_byte_2
g $5EFB Island definition byte 2. Second byte from island data at #R$C600, controls island width/shape.
@ $5EFC label=state_island_byte_3
g $5EFC Island definition byte 3. Third byte from island data at #R$C600, controls island rendering parameters.
@ $5EFD label=state_island_line_idx
g $5EFD Island starting line index. Screen line (0-23) where island rendering begins. Initialized to $10 (16).
@ $5EFE label=data_unused_5EFE
u $5EFE
@ $5F00 label=viewport_slots
g $5F00 Viewport slots array (15 slots for active game objects).
D $5F00 Each slot is a 3-byte entry that can hold one game object (enemy, fuel depot, etc.). Iterated by #R$708E (operate) and #R$62E8 (collision). New objects added via #R$6EAB. See #LINK(DataReference)(Data reference) for terminology.
D $5F00 #TABLE(default) { =h Byte | =h Contents } { 0 | X position (0-255 pixels), or slot marker } { 1 | Y position (0-255 pixels, increases as object scrolls down) } { 2 | Object definition byte (type, orientation, activation) } TABLE#
D $5F00 #TABLE(default) { =h Marker | =h Value | =h Meaning } { SET_MARKER_EMPTY_SLOT | $00 | Unused slot (skip during iteration) } { SET_MARKER_END_OF_SET | $FF | End of active slots (reset iterator) } TABLE#
B $5F00,46,3
@ $5F2E label=exploding_fragments
g $5F2E Explosion animation fragments array.
D $5F2E Array of up to 16 explosion fragments. Each entry is 3 bytes. Animated by #R$6EC8. New explosions added via #R$6EAB.
D $5F2E #TABLE(default) { =h Byte | =h Contents } { 0 | X position (0-255 pixels) } { 1 | Y offset (relative position, adjusted during scroll) } { 2 | Frame counter: bits 0-6 = frame (1-6), bit 7 = erase flag } TABLE#
D $5F2E Explosion frames: 1,5=#R$8471 (small), 2,4=#R$8481 (medium), 3=#R$8491 (large), 6=#R$82F5 (erase).
B $5F2E,49,3
@ $5F5F label=state_activation_interval
g $5F5F Object activation interval bitmask
D $5F5F Controls when newly spawned objects become active (start moving/shooting). Objects spawn inactive (bit 7 clear in slot definition) and activate when (interrupt_counter AND mask) == 0. Checked in #R$708E.
D $5F5F #TABLE(default) { =h Value | =h Meaning | =h When Set } { $1F | Every 32 frames | Normal gameplay } { $0F | Every 16 frames | After bridge destruction } TABLE#
@ $5F60 label=current_slot_ptr
g $5F60 Current slot pointer. Iterator for traversing #R$5F00 during object processing.
W $5F60
@ $5F62 label=exploding_fragments_ptr
g $5F62 Pointer to a slot from #R$5F2E
W $5F62
@ $5F64 label=state_speed
g $5F64 Current scroll speed in pixels per frame.
D $5F64 #TABLE(default) { =h Speed | =h Value | =h Terrain scroll } { SLOW | $01 | 1 pixel/frame } { NORMAL | $02 | 2 pixels/frame } { FAST | $04 | 4 pixels/frame } TABLE#
D $5F64 Also determines sprite animation frame and number of terrain fragments rendered per frame. Horizontal movement is fixed at 2 pixels per input regardless of scroll speed.
@ $5F65 label=low_fuel_sound_period
g $5F65 Low fuel warning sound period (0-127). Used as delay loop iteration count for speaker ON/OFF phases. Lower values = higher pitch. Decremented each frame, creating a rising-then-resetting warble effect.
@ $5F66 label=state_fuel
g $5F66 Fuel level (0-255). $FF=full tank, $00=empty.
@ $5F67 label=state_input_interface
g $5F67 Input interface type ($00=Keyboard, $01=Sinclair, $02=Kempston, $03=Cursor).
@ $5F68 label=state_gameplay_mode
@ $5F68 isub=DEFB GAMEPLAY_MODE_NORMAL
g $5F68 Current gameplay mode
D $5F68 Controls what actions are allowed and how rendering/collision works.
D $5F68 #TABLE(default) { =h Mode | =h Value | =h Description } { NORMAL | $00 | Full player control, collision active } { SCROLL_IN | $01 | Terrain preview at game start, no input } { OVERVIEW | $02 | Attract mode demo } { REFUEL | $06 | Over fuel depot, adding fuel } TABLE#
D $5F68 During SCROLL_IN and OVERVIEW, player input is ignored and no collision occurs. During REFUEL, fuel is added each frame and collisions use plane position instead of missile position.
@ $5F69 label=state_plane_sprite_bank
g $5F69 Plane sprite bank offset. $00=centered, $04=banked left/right. Reset to $00 at the start of each frame, then set to $04 by #R$6630 after the banked sprite is rendered. Read by #R$6E40, but always $00 at that call site — the check is dead code.
@ $5F6A label=state_bridge_player_1
g $5F6A Player 1's current bridge number (0-47). Saved when switching players in 2-player mode.
@ $5F6B label=state_bridge_player_2
g $5F6B Player 2's current bridge number (0-47). Saved when switching players in 2-player mode.
@ $5F6C label=state_bridge_section
g $5F6C Bridge section indicator. $00=normal terrain, $02=bridge terrain section. Set when plane enters bridge area.
@ $5F6D label=state_bridge_destroyed
g $5F6D Bridge destruction flag ($00 = intact, $01 = destroyed).
@ $5F6E label=state_bridge_y_position
g $5F6E Y-position of destroyed bridge in pixels. Updated during scroll to track bridge debris position on screen.
@ $5F6F label=state_unused_5F6F
g $5F6F Unused.
@ $5F70 label=state_scroll_offset
g $5F70 Vertical scroll offset (16-bit, little-endian). Accumulated distance traveled in pixels. Used to calculate level slot index and bridge position.
@ $5F72 label=state_plane_x
g $5F72 Player plane X coordinate in pixels (0-255). $78 (120) is horizontal center. Changes by 2 pixels per left/right input.
@ $5F73 label=helicopter_missile_coordinates_ptr
g $5F73 Pointer to helicopter missile coordinates in viewport_slots array. Updated when advanced helicopter fires.
@ $5F75 label=helicopter_missile_state
g $5F75 Helicopter missile state. Bit 7 set when missile active. Lower bits track missile animation/position.
@ $5F76 label=state_level_fragment_number
g $5F76 Current level terrain array index (1-255). Points to current element in level definition at #R$9500. Incremented as terrain scrolls.
@ $5F77 label=state_terrain_profile_number
b $5F77 Current terrain profile number (0-7). Index into terrain sprite table at #R$8063. Loaded from first byte of level element at #R$9500.
@ $5F78 label=state_terrain_element_23
g $5F78 Cached terrain element data (2 bytes). Stores bytes 2-3 of current level element for object spawning.
@ $5F7A label=state_terrain_extras
g $5F7A Terrain extras byte. Additional terrain rendering flags from level data.
@ $5F7B label=screen_ptr
g $5F7B Current screen memory pointer. Points to pixel address being rendered during terrain drawing.
W $5F7B
@ $5F7D label=state_terrain_position
g $5F7D Terrain sub-position (0-3). Index within current terrain element. Incremented every 4 scroll units, triggers new terrain load at 0.
@ $5F7E label=ptr_scroller
g $5F7E Pointer to the text to be displayed in the scroller.
W $5F7E
@ $5F80 label=data_unused_5F80
u $5F80
@ $5F81 label=state_overview_frame
g $5F81 Overview mode frame counter. Counts scroll iterations during attract mode. Game starts after 5 increments or key press.
@ $5F82 label=data_unused_5F82
u $5F82
@ $5F83 label=saved_stack_pointer
g $5F83 Saved stack pointer. Captured at init, restored when starting new life to unwind any nested calls.
W $5F83
@ $5F85 label=collision_saved_hl
g $5F85 Saved #REGhl register during collision detection. Preserved across collision handler calls.
W $5F85
@ $5F87 label=collision_saved_de
g $5F87 Saved #REGde register during collision detection. Preserved across collision handler calls.
W $5F87
@ $5F89 label=collision_saved_bc
g $5F89 Saved #REGbc register during collision detection. Preserved across collision handler calls.
W $5F89
@ $5F8B label=collision_coordinates
g $5F8B Collision coordinates (Y in high byte, X in low byte). Set by collision detection when overlap found.
W $5F8B
@ $5F8D label=state_saved_object_coords
g $5F8D Saved object coordinates during rendering. Backup of object position for multi-pass rendering.
W $5F8D
@ $5F8F label=state_plane_missile_coords_backup
g $5F8F Missile coordinates preserved during plane rendering.
W $5F8F
@ $5F91 label=main_loop
c $5F91 Main gameplay loop
D $5F91 Central game loop that runs continuously during active gameplay. Orchestrates all game subsystems in a fixed sequence each frame.
D $5F91 #LIST { Input: Check Enter key (Caps+Enter → restart, Symbol+Enter → control select) } { Timing: Increment tick counter (#R$5EEF) } { Render: Explosions, plane/terrain, viewport objects } { Missiles: Two-pass player missile (erase then draw) } { Projectiles: Tank shells, helicopter missiles } { Scroll: Advance game world (#R$66D0) } { Fuel: Consume fuel (#R$6DFF) } { Controls: Dispatch to input handler based on #R$5F67 } LIST#
N $5F91 The loop is infinite. It terminates only via: game over (fuel empty, collision) or player death (jumps to death handler). Pressing Enter calls #R$6BBF (Caps+Enter → #R$5D35 to restart gameplay; Symbol+Enter → #R$6BD2 to return to control selection). The H key pause is handled entirely by the interrupt handler (#R$6BDB) and does not exit the loop.
C $5F91,9 Check Enter key for mode transitions.
$5F9A,4 Increment tick counter at #R$5EEF.
$5FA7 Player missile pass 1: erase at old position.
$5FAF,8 Player missile pass 2: draw at new position.
$5FC3 Reset plane sprite bank.
$5FC8 Dispatch to input handler.
@ $5FCB isub=CP INPUT_INTERFACE_KEMPSTON
@ $5FD0 isub=CP INPUT_INTERFACE_SINCLAIR
@ $5FD5 isub=CP INPUT_INTERFACE_KEYBOARD
@ $5FDA label=scan_cursor
c $5FDA Scan cursor keys
D $5FDA Reads keyboard half-rows via port $FE. Cursor keys use active-low bits (Z = pressed). Each detected press calls the corresponding handler.
C $5FDA Scan "8" (RIGHT)
C $5FE6 Scan "5" (LEFT)
C $5FEF Scan "0" (FIRE)
C $5FF7 Scan "7" (UP)
C $5FFF Scan "6" (DOWN)
@ $600A label=scan_kempston
c $600A Scan Kempston joystick
D $600A Reads Kempston joystick port $1F. Unlike keyboard, Kempston uses active-high bits (NZ = pressed).
C $600A Scan RIGHT
C $6016 Scan LEFT
C $601E Scan DOWN
C $6026 Scan UP
C $602E Scan FIRE
@ $6039 label=scan_sinclair
c $6039 Scan Sinclair joystick
D $6039 Reads half-row $EF (keys 6-0) via port $FE. Maps Sinclair Interface 2 joystick 1 to game actions.
C $6039 Scan "0" (FIRE)
C $6045 Scan "9" (UP)
C $604D Scan "8" (DOWN)
C $6055 Scan "7" (RIGHT)
C $605D Scan "6" (LEFT)
@ $6068 label=scan_keyboard
c $6068 Scan keyboard
D $6068 Reads multiple half-rows via port $FE. Keys: P=right, O=left, 2=up, W=down, either bottom row=fire.
C $6068 Scan "P" (RIGHT)
C $6071 Scan "O" (LEFT)
C $607A Scan "2" (UP)
C $6083 Scan "W" (DOWN)
C $608C Scan lower row right (FIRE)
C $6097 Scan lower row left (FIRE)
@ $60A5 label=render_plane_and_terrain
c $60A5 Render player plane and terrain fragments
D $60A5 Top-level render routine called from main loop (#R$5F91). Orchestrates visual updates in a specific order to prevent flicker.
N $60A5 Render sequence:
N $60A5 .
N $60A5 #LIST { Player plane sprite (GAMEPLAY_MODE_NORMAL only) } { Screen scroll system } { Terrain fragments (count = current speed) } { Attribute scroll (every 8 fragments) } LIST#
N $60A5 .
N $60A5 Speed affects the number of terrain fragments rendered per frame and the sprite animation frame selection.
$60A5 Skip plane rendering if not GAMEPLAY_MODE_NORMAL.
@ $60A8 isub=CP GAMEPLAY_MODE_NORMAL
@ $60AD isub=LD A,COLLISION_MODE_NONE
$60AD Set collision mode to NONE for plane (no collision during render).
$60B2 Calculate plane sprite frame: offset = (8 - speed) * 2.
$60C2 Apply offset to sprite pointer.
$60C9 Compute look-ahead Y: Y = 136 - speed (135 slow, 134 normal, 132 fast), placing the plane higher on screen at faster speeds to reveal more upcoming terrain. Dead code: movement handlers (#R$65F3, #R$6642, #R$6682) always overwrite this with the fixed PLANE_COORDINATE_Y.
$60D0 Set plane coordinates for rendering.
$60D8 Render plane: width=2, size=$10, attrs=$00.
@ $60E5 label=render_terrain_fragments
$60E5 Render islands.
$60E8 Calculate starting screen row based on speed.
@ $60F4 label=calculate_screen_row_loop
$60F4 #REGhl = screen_start + (speed * $100).
$60F8 Store screen pointer to #R$5F7B.
$60FB Set up terrain fragment loop (count = speed).
@ $6103 label=render_terrain_loop
$6103 Increment fragment counter.
$6106 Every 8 fragments, scroll attributes.
$610D Render terrain fragment.
$6110 Move screen pointer up one row ($100 bytes).
$611D Loop until all fragments rendered.
$611F Store updated fragment counter.
@ $6124 label=calculate_fuel_gauge_offset
c $6124 Calculate fuel gauge sprite offset
D $6124 Computes a sprite data offset for the fuel gauge display using the formula: offset = (7 - B) * 8 + D. The result is patched into the operand of the following RLC instruction via self-modifying code.
R $6124 I:B Fuel gauge row (0-7, top to bottom)
R $6124 I:D Sub-row pixel offset within the gauge row
R $6124 O:B Rotated fuel gauge row (used by caller for next iteration)
$6124 Calculate offset = (7 - B) * 8 + D and patch into next instruction.
$6133,2 Rotate B left through carry.
@ $6136 label=collision_dispatcher
c $6136 Collision detection dispatcher
D $6136 Central collision handler called during sprite rendering (via #R$8C45) when pixel overlap is detected. Saves registers, reads collision mode from #R$5EF5, and dispatches to the appropriate handler.
D $6136 #TABLE(default) { =h Mode | =h Handler | =h Description } { COLLISION_MODE_NONE ($00) | #R$8C3B | Rendering only } { COLLISION_MODE_FUEL_DEPOT ($01) | #R$6256 | Fuel depot refuel } { COLLISION_MODE_MISSILE ($02) | #R$61BB | Missile hit } { COLLISION_MODE_FIGHTER ($03) | #R$615E | Fighter hit } { COLLISION_MODE_HELICOPTER_MISSILE ($04) | #R$7415 | Enemy missile } TABLE#
$6136 Save registers: return address to #R$5F85, #REGde to #R$5F87, #REGbc to #R$5F89.
$6142 Load collision mode and dispatch to handler (see table above).
@ $6145 isub=CP COLLISION_MODE_NONE
@ $614A isub=CP COLLISION_MODE_FUEL_DEPOT
@ $614F isub=CP COLLISION_MODE_MISSILE
@ $6154 isub=CP COLLISION_MODE_FIGHTER
@ $6159 isub=CP COLLISION_MODE_HELICOPTER_MISSILE
@ $615E label=handle_collision_mode_fighter
c $615E Handle COLLISION_MODE_FIGHTER collision detection
N $615E Checks if the player's missile has collided with a fighter aircraft. Uses bounding box collision detection by comparing the missile coordinates (from #R$5EF3) with the fighter's coordinates (from #R$8B0C).
N $615E .
N $615E The collision box is 6 pixels wide (horizontally) and uses vertical bounds of +10 and +1 pixels.
N $615E .
N $615E If no collision is detected (any boundary check fails), jumps to #R$6401 to reset collision mode and return.
N $615E .
N $615E On collision: removes the fighter from the viewport, triggers two explosion fragments, awards POINTS_FIGHTER, and continues to post-collision processing at #R$6794.
$615E Load missile coordinates from #R$5EF3 and fighter coordinates from #R$8B0C.
N $6166 Y-axis collision check.
$6166 Check if missile_Y + 6 >= fighter_Y; exit to #R$6401 if not.
$616D Check if fighter_Y + 6 >= missile_Y; exit to #R$6401 if not.
N $6174 X-axis collision check.
$6174 Check if fighter_X + 10 >= missile_X; exit to #R$6401 if not.
$6182 Check if missile_X + 1 >= fighter_X; exit to #R$6401 if not.
N $6194 Collision detected - process hit.
$6194 Clean up stack (3x POP #REGde).
$6197 Store fighter Y coordinate to #R$5EF6.
$619B Get object coordinates from viewport and mark slot as empty.
@ $61A3 isub=LD (HL),SET_MARKER_EMPTY_SLOT
$61A5 Spawn two explosion fragments (second offset by X-6).
@ $61B3 isub=LD A,POINTS_FIGHTER
$61B3 Award POINTS_FIGHTER to player.
$61B8 Deactivate missile.
@ $61BB label=check_collision
c $61BB Check collision against bridges and viewport objects
N $61BB Checks if the entity (missile or plane) has collided with a bridge or any viewport object. The coordinates to check are read from #R$5EF3.
N $61BB .
N $61BB Called in two contexts: (1) when COLLISION_MODE_MISSILE is set, checks missile collision; (2) when called from #R$6256, checks plane collision with fuel depots.
N $61BB .
N $61BB First checks if a bridge exists (#R$5F6E != 0) and whether the Y coordinate is within the bridge's vertical bounds (bridge_y - $16 to bridge_y). If the bridge is hit, awards POINTS_BRIDGE, triggers 6 explosion fragments in a 2x3 grid pattern, increments the player's bridge counter, and clears the bridge.
N $61BB .
N $61BB If no bridge collision, falls through to #R$62E8 to check collision against viewport objects.
$61BB Load entity coordinates from #R$5EF3 and bridge Y from #R$5F6E.
$61C2 If no bridge exists, jump to #R$62E8 to check viewport objects.
$61C7 Check if entity_Y <= bridge_Y; if not, check objects.
$61CC Check if entity_Y >= bridge_Y - $16; if not, check objects.
@ $61D3 isub=LD A,POINTS_BRIDGE
N $61D3 Bridge hit - award points and spawn explosions.
$61D3 Award POINTS_BRIDGE to player.
@ $61D8 isub=LD A,ACTIVATION_INTERVAL_FAST
$61D8 Increase activation rate (every 16 frames after bridge destruction).
$61DD Clean up stack and store hit Y to #R$5EF6.
$61E5 Set up bottom explosion row: Y = bridge_Y - 4, X = $70, D = 0.
$61EF Spawn explosion 1 (bottom-left).
$61F2 Move to X = $80 and spawn explosion 2 (bottom-right).
$61F7 Move Y - 8 and spawn explosion 3 (middle-right).
$61FE Move to X = $70 and spawn explosion 4 (middle-left).
$6203 Move Y - 8 and spawn explosion 5 (top-left).
$620A Move to X = $80 and spawn explosion 6 (top-right).
$620F If bridge section is $02, call #R$6BA4 with screen #R$4000.
$6217 (continued from above).
@ $621A ignoreua=$4100
$621A If bridge section is $02, call #R$6BA4 with screen $4100.
@ $621F keep
@ $621F ignoreua=$4100
$6222 (continued from above).
$6225 Clear bridge Y position (#R$5F6E).
$622A Set bridge destroyed flag (#R$5F6D).
$622F Restore entity coordinates from #R$5F8D to #R$5EF3.
$6237 Check if player 2; if so, jump to #R$6249.
@ $623A isub=CP PLAYER_2
$623C (continued from above).
@ $623F label=next_bridge_player_1
$623F Increment player 1 bridge counter at #R$5F6A and print.
$6246 Deactivate missile.
@ $6249 label=next_bridge_player_2
c $6249 Increment player 2's bridge counter
$6249 Increment player 2 bridge counter at #R$5F6B and print.
$6250,3 Deactivate missile.
@ $6253 label=data_unused_6253
u $6253
@ $6256 label=handle_collision_mode_fuel_depot
@ $6256 isub=LD A,GAMEPLAY_MODE_REFUEL
c $6256 Handle COLLISION_MODE_FUEL_DEPOT (initiate refueling)
N $6256 Called from the collision dispatcher when COLLISION_MODE_FUEL_DEPOT is detected. Sets gameplay mode to GAMEPLAY_MODE_REFUEL and uses the plane's coordinates (instead of missile coordinates) for collision detection.
N $6256 .
N $6256 This allows the plane itself to interact with fuel depots via #R$61BB.
$6256 Set gameplay mode to GAMEPLAY_MODE_REFUEL in #R$5F68.
$625B Load plane coordinates (Y=$80, X from #R$5F72) into #R$5EF3.
$6265 Check for collision with fuel depot.
@ $6268 label=check_fragment_collision
c $6268 Check collision with explosion fragments
N $6268 Iterates through #R$5F2E (exploding_fragments) to check if the entity collides with any active debris. Each fragment entry is 3 bytes: X, Y, and type/state.
N $6268 .
N $6268 Collision uses 8x8 bounding box for the entity and 16x8 for fragments. Result stored in #R$5F8B: $02 = collision detected, $00 = no collision (end of list reached).
$6268 Load fragment coordinates (C=X, B=Y) from list pointer #R$5F62 and advance by 3.
$6270 Store updated pointer; copy X to A for marker check.
@ $6274 isub=CP SET_MARKER_EMPTY_SLOT
$6274 If empty slot, skip to next fragment.
@ $6279 isub=CP SET_MARKER_END_OF_SET
$6279 If end-of-set marker, jump to #R$62CE (no collision).
$627E Adjust Y coordinate for scrolling.
N $6281 Y-axis collision check (8-pixel height for both entity and fragment).
$6281 Check if entity_Y + 8 >= fragment_Y; if not, next fragment.
$6291 (continued).
$6294 Check if fragment_Y + 8 >= entity_Y; if not, next fragment.
N $62A3 X-axis collision check (8-pixel width for entity, 16-pixel for fragment).
$62A3 Check if entity_X + 8 >= fragment_X; if not, next fragment.
$62B3 (continued).
$62B6 Check if fragment_X + 16 >= entity_X; if not, next fragment.
N $62C8 Collision detected with fragment.
$62C8 Store $02 (collision) to #R$5F8B and return.
@ $62CE label=check_fragment_collision_end
c $62CE End of fragment list - no collision found
$62CE Store $00 (no collision) to #R$5F8B and return.
@ $62D4 label=get_offset_balloon
c $62D4 Get Y offset for balloon collision
$62D4 Return $09 in E (balloon Y offset).
@ $62D7 label=get_offset_fuel
c $62D7 Get Y offset for fuel depot collision
$62D7 Return $11 in E (fuel depot Y offset).
@ $62DA label=advance_object
c $62DA Increase vertical coordinate of the object by the value of #R$5F64.
D $62DA Adds the current scroll delta to an object's Y coordinate. Used during refueling mode to compensate for screen scrolling when checking collisions.
R $62DA I:B Current Y coordinate
R $62DA O:B New Y coordinate (B + scroll_delta)
$62DA,5 Load scroll delta from #R$5F64 and add to B.
@ $62E0 label=retract_object
c $62E0 Decrease vertical coordinate of the object by the value of #R$5F64.
D $62E0 Subtracts the current scroll delta from an object's Y coordinate. Used to reverse the adjustment made by #R$62DA after collision checking.
R $62E0 I:B Current Y coordinate
R $62E0 O:B New Y coordinate (B - scroll_delta)
$62E0,7 Load scroll delta from #R$5F64 and subtract from B.
@ $62E8 label=check_missile_vs_objects
c $62E8 Check missile collision against viewport objects
N $62E8 Iterates through #R$5F00 (viewport_slots) checking if the player's missile collides with any object. Each slot is 3 bytes: X, Y, and definition.
N $62E8 .
N $62E8 Collision detection uses type-specific bounding boxes. Most objects are 10x8 pixels, but balloons and fuel depots are taller (17-25 pixels), and ships are wider (19 pixels).
N $62E8 .
N $62E8 On hit, dispatches to type-specific handlers: #R$6414 (helicopter), #R$6423 (ship), #R$6444 (advanced helicopter), #R$6453 (fighter), #R$6462 (balloon), #R$6478 (fuel). If no known type matches, checks #R$6268 for fragment collision.
N $62E8 .
N $62E8 During GAMEPLAY_MODE_REFUEL, object Y coordinates are adjusted for scrolling before and after collision checks.
$62E8 Load object coordinates (C=X, B=Y) from list pointer #R$5F60 and advance by 3.
$62F0 Store updated pointer; copy X to A for marker check.
@ $62F4 isub=CP SET_MARKER_EMPTY_SLOT
$62F4 If empty slot, skip to next object.
@ $62F9 isub=CP SET_MARKER_END_OF_SET
$62F9 If end-of-set marker, jump to #R$63B9 for fragment collision check.
$62FE If GAMEPLAY_MODE_REFUEL, call #R$62DA to adjust object Y for scrolling.
@ $6301 isub=CP GAMEPLAY_MODE_REFUEL
N $6306 Y-axis collision check (entity height: 9 pixels).
$6306 Check if entity_Y + 9 >= object_Y; if not, next object.
$6316 (continued).
$6319 Set up D = object_Y + 8, E = 0 (height offset).
$631F Load object type from viewport pointer.
@ $6324 isub=AND SLOT_MASK_OBJECT_TYPE
$6324 If balloon, call #R$62D4 (E = 9, taller).
@ $6326 isub=CP OBJECT_BALLOON
@ $632B isub=CP OBJECT_FUEL
$632B If fuel depot, call #R$62D7 (E = 17, tallest).
$6330 Check if object_Y + 8 + offset >= entity_Y; if not, next object.
$633F (continued).
N $6342 X-axis collision check (entity width: 8 pixels).
$6342 Check if entity_X + 8 >= object_X; if not, next object.
$6353 (continued).
$6356 Set up D = object_X + 10, E = 0 (width offset).
$635C Load object type from viewport pointer.
@ $6361 isub=AND SLOT_MASK_OBJECT_TYPE
$6361 If ship, call #R$62D4 (E = 9, wider).
@ $6363 isub=CP OBJECT_SHIP
$6368 Store object coordinates to #R$5F8B for hit handlers.
$636E (continued).
$6372 Check if object_X + 10 + offset >= entity_X; if not, next object.
$637A (continued).
N $637D Collision detected - prepare for type dispatch.
$637D If GAMEPLAY_MODE_REFUEL, call #R$62E0 to undo Y adjustment.
@ $6380 isub=CP GAMEPLAY_MODE_REFUEL
$6385 Store object coordinates to #R$5F8B.
$6389 Load object type from viewport pointer.
@ $638E isub=AND SLOT_MASK_OBJECT_TYPE
$638E If type is 0, jump to #R$63DD to process collision.
$6395 Mark object slot as empty.
$6399 Clear D for type dispatch.
@ $639B isub=CP OBJECT_HELICOPTER_REG
$639B If helicopter, jump to #R$6414.
@ $63A0 isub=CP OBJECT_SHIP
$63A0 If ship, jump to #R$6423.
@ $63A5 isub=CP OBJECT_HELICOPTER_ADV
$63A5 If advanced helicopter, jump to #R$6444.
@ $63AA isub=CP OBJECT_FIGHTER
$63AA If fighter, jump to #R$6453.
@ $63AF isub=CP OBJECT_BALLOON
$63AF If balloon, jump to #R$6462.
@ $63B4 isub=CP OBJECT_FUEL
$63B4 If fuel depot, jump to #R$6478.
@ $63B9 label=check_missile_vs_objects_end
N $63B9 End of object list - check fragment collision and tank shell.
$63B9 Reset #R$5F60/#R$5F62 to list starts.
$63C8 If fragment collision detected ($02), jump to #R$63FC.
$63D0,10 Compare missile Y with tank shell Y; if equal, jump to #R$63FC.
@ $63DD label=process_collision_hit
N $63DD Entry point for collision hit processing. Cleans up stack and deactivates missile.
$63DD Pop four words from stack (unwind call frames).
$63E1 Store hit Y coordinate to #R$5EF6.
$63E5 Reset #R$5F62 and #R$5F60 to list starts.
$63F1 Load saved coordinates from #R$5F8D.
$63F5 Store to #R$5EF3.
$63F9 Deactivate missile.
@ $63FC label=reset_gameplay_mode
c $63FC Reset gameplay mode to normal and exit collision system
$63FC Reset gameplay mode to GAMEPLAY_MODE_NORMAL.
@ $6401 label=no_collision_exit
@ $6401 isub=LD A,COLLISION_MODE_NONE
N $6401 Exit collision system with no collision detected.
$6401 Clear collision mode, restore saved registers, and continue rendering.
@ $6414 label=hit_helicopter_reg
@ $6414 isub=LD A,POINTS_HELICOPTER_REG
c $6414 Handle missile hit on regular helicopter
N $6414 Awards POINTS_HELICOPTER_REG and spawns 1 explosion fragment at the object coordinates.
$6414 Award POINTS_HELICOPTER_REG to player.
$6419 Spawn explosion at object coordinates from #R$5F8B.
$6420 Deactivate missile.
@ $6423 label=hit_ship
@ $6423 isub=LD A,POINTS_SHIP
c $6423 Handle missile hit on ship
N $6423 Awards POINTS_SHIP and spawns 3 explosion fragments in a triangular pattern to cover the ship's width.
$6423 Award POINTS_SHIP to player.
$6428 Load coordinates from #R$5F8B and spawn explosion 1 (left).
$642F Move X+8 and spawn explosion 2 (right).
$6436 Move X-8, Y+4 and spawn explosion 3 (center-bottom).
$6441 Deactivate missile.
@ $6444 label=hit_helicopter_adv
@ $6444 isub=LD A,POINTS_HELICOPTER_ADV
c $6444 Handle missile hit on advanced helicopter
N $6444 Awards POINTS_HELICOPTER_ADV and spawns 1 explosion fragment.
$6444 Award POINTS_HELICOPTER_ADV to player.
$6449 Spawn explosion at object coordinates from #R$5F8B.
$6450 Deactivate missile.
@ $6453 label=hit_fighter
@ $6453 isub=LD A,POINTS_FIGHTER
c $6453 Handle missile hit on fighter
N $6453 Awards POINTS_FIGHTER and spawns 1 explosion fragment.
$6453 Award POINTS_FIGHTER to player.
$6458 Spawn explosion at object coordinates from #R$5F8B.
$645F Deactivate missile.
@ $6462 label=hit_balloon
@ $6462 isub=LD A,POINTS_BALLOON
c $6462 Handle missile hit on balloon
N $6462 Awards POINTS_BALLOON and spawns 2 explosion fragments vertically (balloon is taller than other objects).
$6462 Award POINTS_BALLOON to player.
$6467 Load coordinates from #R$5F8B and spawn explosion 1 (top).
$646E Move Y+8 and spawn explosion 2 (bottom).
$6475 Deactivate missile.
@ $6478 label=hit_fuel
c $6478 Handle collision with fuel depot
N $6478 Handles both missile hits and plane refueling at fuel depots.
N $6478 .
N $6478 If in GAMEPLAY_MODE_REFUEL: marks the depot slot as empty and calls #R$6E40 to add fuel to the player's tank.
N $6478 .
N $6478 Otherwise (missile hit): awards POINTS_FUEL and spawns 4 explosion fragments vertically (fuel depot is the tallest object).
$6478 Check if in REFUEL mode; if so, jump to #R$64A1.
@ $647B isub=CP GAMEPLAY_MODE_REFUEL
@ $6480 isub=LD A,POINTS_FUEL
N $6480 Missile hit on fuel depot - destroy it.
$6480 Award POINTS_FUEL to player.
$6485 Load coordinates from #R$5F8B and spawn explosion 1.
$648C Move Y+8 and spawn explosion 2.
$6493 Move Y+8 and spawn explosion 3.
$649A Move Y+1 and spawn explosion 4.
@ $649E label=hit_fuel_done
c $649E Jump to collision finalization
$649E Deactivate missile.
@ $64A1 label=refuel_from_depot
c $64A1 Handle refueling from fuel depot