-
Notifications
You must be signed in to change notification settings - Fork 0
/
prg.asm
3029 lines (2606 loc) · 108 KB
/
prg.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
; NES Game Genie - PRG ROM (ASM6)
; TODO: some subs starting from check_button_b need tidying up.
; --- Constants ---------------------------------------------------------------
; Notes:
; - Each variable takes one byte unless otherwise mentioned.
; - Signed integers are two's complement unless otherwise noted.
; CPU addresses - RAM
ram_clear_ptr equ $00 ; RAM clear pointer (2 bytes, overlaps)
ppu_ctrl_mirror equ $00
ppu_mask_mirror equ $01
temp1 equ $04 ; a temporary variable, many uses
vram_addr_hi equ $05 ; VRAM address high
joypad1_status equ $07
joypad2_status equ $08
skip_nmi equ $0c
graphic_width equ $0d ; width of graphic in nybbles/tiles
graphic_height equ $0e ; height of graphic in nybbles/tiles
ram_prog_target equ $10 ; RAM program target
graphic_x_offs equ $15 ; graphic X offset
graphic_y_offs equ $16 ; graphic Y offset
graphic_x equ $17
graphic_y equ $18
graphic_nybble equ $19
attr_fill_byte equ $1a ; attribute fill byte
vram_block_x equ $1e
vram_block_y equ $1f
nybble_vram_lo equ $20 ; low
nybble_vram_hi equ $21 ; high
multiply_temp equ $27 ; temporary for multiplication
vram_block_cost equ $28
vram_buf_rd_pos equ $29 ; next read position in VRAM buffer
vram_buf_wr_pos equ $2a ; next write position in VRAM buffer
vram_buf_free equ $2b ; number of free bytes in VRAM buffer
vram_budget equ $2c
metasprite_x equ $2d
always_zero1 equ $2e ; always 0
metasprite_y equ $2f
always_zero2 equ $30 ; always 0
graphic_id equ $31
unused1 equ $33
metaspr_width equ $34 ; metasprite width in nybbles/tiles
metaspr_height equ $35 ; metasprite height in nybbles/tiles
always_00011100 equ $36 ; always %00011100
grafic_dataleft equ $37 ; graphic data left
metaspr_index equ $38 ; metasprite index
graphics_ptr equ $39 ; graphics pointer (2 bytes)
nybbles_left_x equ $3b
sprite_y equ $3c ; 2 bytes
sprite_x equ $3e ; 2 bytes
nybble_offs equ $40 ; nybble offset
always_one equ $41 ; 1
always_zero3 equ $42 ; 0
odd_frame_flag1 equ $43
scroll_x_mirror equ $45
scroll_y_mirror equ $46
nmi_done equ $49
unused2 equ $4a
first_free_byte equ $4b
keybd_graphic equ $4c ; keyboard graphic
keybd_graphic_x equ $4d ; keyboard graphic X position
keybd_graphic_y equ $4e ; keyboard graphic Y position
hand_metasprite equ $4f ; hand cursor; metasprite number (0)
hand_x_pixel equ $50 ; 14-238
hand_y_pixel equ $51 ; 60-204
hand_x_letr_trg equ $52 ; hand cursor - X target in letters (0-7)
hand_y_letr_trg equ $53 ; hand cursor - Y target in letters (0-4)
revolv_metaspr equ $54 ; revolving cursor - metasprite number (1)
revolv_x equ $55 ; revolving cursor - X pos (10-234)
revolv_y equ $56 ; revolving cursor - Y pos (152-216)
hand_x_spd_ptr equ $57 ; hand cursor - X speed pointer (2 bytes)
hand_y_spd_ptr equ $59 ; hand cursor - Y speed pointer (2 bytes)
hand_y_spd_offs equ $5b ; hand cursor - Y speed offset
hand_x_spd_offs equ $5c ; hand cursor - X speed offset
last_x_inp_acc equ $5d ; last X input accepted (pad_left/pad_right)
last_y_inp_acc equ $5e ; last Y input accepted (pad_up/pad_down)
odd_frame_flag2 equ $5f
hand_x_keyboard equ $60 ; hand cursor - last X pos on virtual keyboard (0-7)
hand_y_keyboard equ $61 ; hand cursor - last Y pos on virtual keyboard (0-1)
hand_x_letr equ $62 ; hand cursor - X position in letters (0-7)
hand_y_letr equ $63 ; hand cursor - Y position in letters (0-4)
revolv_x_letr1 equ $64 ; revolving cursor - X position in letters (0-7)
revolv_y_letr1 equ $65 ; revolving cursor - Y position in letters (0-2)
prev_btn_a_stat equ $66 ; previous status of button A
prev_btn_b_stat equ $67 ; previous status of button B
revolv_x_target equ $68 ; revolving cursor - X target (10-234)
revolv_y_target equ $69 ; revolving cursor - Y target (152-216, step 32)
revolv_trg_spd equ $6a ; revolving cursor - target speed (signed, excess-128)
revolv_spd_x equ $6b ; revolving cursor - X speed (signed)
revolv_spd_y equ $6c ; revolving cursor - Y speed (signed)
revolv_phase equ $6d ; revolving cursor - phase of animation (0-15)
revolv_pos equ $6e ; revolving cursor - X/Y pos (arg for sub)
revolv_target equ $6f ; revolving cursor - X/Y target (arg for sub)
parti_start_x equ $70 ; particles - start X position
parti_start_y equ $71 ; particles - start Y position
parti_set_flag equ $72 ; particles - which set to spawn
parti_set1timer equ $73 ; particles - timer of set 1 (counts up!)
parti_set2timer equ $74 ; particles - timer of set 2 (counts up!)
particle_timer equ $75 ; particles - timer (counts down!)
flying_x equ $76 ; flying letter - X position
flying_y equ $77 ; flying letter - Y position
flying_x_spd equ $78 ; flying letter - X speed (signed, -14...+14)
flying_y_spd equ $79 ; flying letter - Y speed (3-9)
flying_timelft1 equ $7a ; flying letter - time left 1
flying_metaspr equ $7b ; flying letter - metasprite number (2)
flying_timelft2 equ $7c ; flying letter - time left 2
entered_letr equ $7d ; entered letter
rows_left equ $7e ; only in fill_atrblkrows
anim_colr_phase equ $7f ; phase of animated color (0-7)
code_ptr equ $80 ; code pointer (2 bytes, overlaps)
anim_colr_delay equ $80 ; delay of animated color (0-4)
revolv_x_letr2 equ $81 ; revolving cursor - X position in letters (0-7?)
decoded_cod_ptr equ $82 ; pointer to decoded codes (2 bytes, overlaps)
revolv_y_letr2 equ $82 ; revolving cursor - Y position in letters (2-4)
revolv_y_ltr2pr equ $83 ; revolving cursor - previous Y pos in letters (2-4)
temp2 equ $84 ; temporary, many uses
code_length equ $85
code_enab_mask equ $86 ; code enable bitmask
comp_enab_mask equ $87 ; compare value enable bitmask
codes_left equ $88 ; number of codes left to decode
genie_ctrl_val equ $89 ; Game Genie hardware control value
decoded_code equ $8a ; 4 bytes; see "arrays" below
decoded_codes equ $90 ; 16 bytes; see "arrays" below
sprite_data equ $0200 ; interleaved sprite data; 256 bytes; copied to OAM
vram_buffer equ $0300 ; 256 bytes; several blocks to be copied to VRAM
attr_data_copy equ $0400 ; 64 bytes; copy of attribute table data
vram_block equ $0440 ; 35 bytes; see "arrays" below
sprite_data_atr equ $0463 ; 64 bytes; see "arrays" below
sprite_data_x equ $04a3 ; 64 bytes; see "arrays" below
sprite_data_y equ $04e3 ; 64 bytes; see "arrays" below
sprite_data_til equ $0523 ; 64 bytes; see "arrays" below
metaspr_indexes equ $0563 ; 50 bytes; indexes to metasprites
metasprites equ $0595 ; 46 bytes; see "arrays" below
parti_spds_x equ $060b ; 64 bytes; see "arrays" below
parti_spds_y equ $062b ; 64 bytes; see "arrays" below
entered_letrs equ $066b ; 24 bytes; see "arrays" below
; Arrays:
;
; decoded_code:
; current decoded Game Genie code
; bytes: address high, address low, replace value, compare value
; decoded_codes:
; all decoded codes; 12 bytes actually used; bytes for each code:
; address high, address low, compare value, replace value
; (note: order differs from decoded_code)
; vram_block:
; a block of bytes to be copied to VRAM
; 3 bytes: payload size (1-32), address high, address low
; 1-32 bytes: payload
; sprite_data_atr, sprite_data_x, sprite_data_y, sprite_data_til:
; attributes, X positions, Y positions and tiles of sprites
; 0-19: hand cursor (5*4 sprites)
; 20-23: revolving cursor (2*2 sprites)
; 24-39: flying letter (4*4 sprites)
; 32-47: 1st particle set (16 sprites; overlaps with flying letter)
; 48-63: 2nd particle set (16 sprites)
; metasprites:
; objects consisting of several hardware sprites
; 2 + 5*4 bytes for hand cursor
; 2 + 2*2 bytes for revolving cursor
; 2 + 4*4 bytes for flying letter
; for each one:
; 1 byte: width in hardware sprites
; 1 byte: height in hardware sprites
; width * height bytes: indexes to planar sprite data
; parti_spds_x, parti_spds_y:
; horizontal/vertical speeds of flying particles; signed; only indexes
; 32-63 used in each
; entered_letrs
; letters entered by user (0 = none, 3-18 = AEPOZXLUGKISTVYN)
; CPU addresses - NES memory-mapped registers; see:
; - https://www.nesdev.org/wiki/PPU_registers
; - https://www.nesdev.org/wiki/APU_registers
; - https://www.nesdev.org/wiki/Controller_reading_code
ppu_ctrl equ $2000
ppu_mask equ $2001
ppu_status equ $2002
oam_addr equ $2003
ppu_scroll equ $2005
ppu_addr equ $2006
ppu_data equ $2007
oam_dma equ $4014
pulse1_regs equ $4000 ; $4000-$4003
noise_regs equ $400c ; $400c, $400e-$400f
sound_ctrl equ $4015
joypad1 equ $4016
joypad2 equ $4017
; CPU addresses - Game Genie memory-mapped registers;
; see: https://www.nesdev.org/wiki/Game_Genie
; - genie_codes: address high/low, compare, replace for 3 codes
genie_ctrl equ $8000 ; master control
genie_codes equ $8001 ; $8001-$800c (12 bytes, see above)
genie_unknown1 equ $fff0
genie_unknown2 equ $fff1
; PPU addresses
vram_nt0 equ $2000 ; name table 0
vram_at0 equ $23c0 ; attribute table 0
vram_palette equ $3f00 ; palette
; joypad bitmasks
pad_a equ %10000000 ; A button
pad_b equ %01000000 ; B button
pad_st_sel equ %00110000 ; start or select button
pad_up equ %00001000 ; d-pad up
pad_down equ %00000100 ; d-pad down
pad_left equ %00000010 ; d-pad left
pad_right equ %00000001 ; d-pad right
; --- Macros ------------------------------------------------------------------
; These macros prevent ASM6 from automatically optimizing 16-bit addressing to
; 8-bit when the address is $00-$ff.
macro deca _word ; DEC absolute
hex ce
dw _word
endm
macro ldaa _word ; LDA absolute
hex ad
dw _word
endm
macro ldxa _word ; LDX absolute
hex ae
dw _word
endm
macro staa _word ; STA absolute
hex 8d
dw _word
endm
macro add _operand ; add without carry
clc
adc _operand
endm
macro sub _operand ; subtract with carry
sec
sbc _operand
endm
; -----------------------------------------------------------------------------
base $f000 ; last 4 KiB of CPU address space
init1 ; Part 1/3 of initialization. Called by reset vector.
sei ; ignore IRQs
cld ; disable decimal mode
lda #%00000000
sta ppu_ctrl ; disable NMI
ldx #$ff
txs ; initialize stack pointer
; do something to unknown Game Genie registers
lda #$00
sta genie_unknown1
jsr delay
sta genie_unknown2
jsr delay
sta genie_unknown1
jmp init2 ; continue initialization
delay ldx #96 ; wait; called by init1
ldy #8
- dex
bne -
dey
bne -
rts
init2 ; Part 2/3 of initialization. Called by init1.
ldx #10 ; wait for VBlank 10 times
- lda ppu_status
bpl -
dex
bne -
ldx #$ff ; reinitialize stack pointer (why?)
txs
lda #$07 ; clear RAM (fill $0000-$07ff with $00)
sta ram_clear_ptr+1
lda #$00
sta ram_clear_ptr+0
tay
- sta (ram_clear_ptr),y
iny
bne -
dec ram_clear_ptr+1
bpl -
lda #%00000110 ; hide sprites and background
sta ppu_mask_mirror
sta ppu_mask
; enable NMI; use 8*8-pixel sprites, pattern table 0, name
; table 0 and 1-byte VRAM address autoincrement
lda #%10000000
sta ppu_ctrl_mirror
sta ppu_ctrl
deca vram_buf_free ; set to 255
jmp init3 ; continue initialization
; -----------------------------------------------------------------------------
read_joypads ; Read joypads. Out: joypad1_status, joypad2_status.
; Bits: A, B, select, start, up, down, left, right.
; Called by do_every_frame.
lda #%00000001 ; initialize joypads
sta joypad1
lda #%00000000
sta joypad1
; read joypad 1 (for each button, copy least significant bit
; of joypad1 to joypad1_status via carry)
ldy #8
- lda joypad1
ror a
rol joypad1_status
dey
bne -
ldy #8 ; read joypad 2 in similar fashion
- lda joypad2
ror a
rol joypad2_status
dey
bne -
rts
; -----------------------------------------------------------------------------
; The non-maskable interrupt routine.
; In: skip_nmi (if set, skip PPU stuff).
; Out: nmi_done (set when exiting).
; Called by NMI vector.
nmi pha ; push A, X, Y
txa
pha
tya
pha
lda skip_nmi ; if flag set, skip PPU stuff
bne +
jsr sprite_dma ; do sprite DMA
jsr flush_vram_buf ; flush VRAM buffer
ldaa scroll_x_mirror ; update PPU registers from mirrors
sta ppu_scroll
ldaa scroll_y_mirror
sta ppu_scroll
lda ppu_ctrl_mirror
sta ppu_ctrl
+ lda #1 ; set flag; pull Y, X, A
staa nmi_done
pla
tay
pla
tax
pla
rti
; -----------------------------------------------------------------------------
draw_bg_graphic ; Copy a graphic (e.g. the Game Genie logo) to name table.
; In: A = id (see graphix_offsets), X/Y = X/Y position in
; tiles.
; Called by init_background, letter_input, check_button_b.
stx graphic_x
sty graphic_y
jsr set_graphix_ptr ; A = id
ldy #0 ; get width and height in nybbles/tiles
lda (graphics_ptr),y
sta graphic_width
iny
lda (graphics_ptr),y
sta graphic_height
ldaa graphics_ptr+0 ; advance pointer to start of data
add #2
staa graphics_ptr+0
ldaa graphics_ptr+1
adc #0
staa graphics_ptr+1
lda #0 ; copy nybbles to VRAM buffer
sta graphic_y_offs
-- lda #0 ; start Y loop
sta graphic_x_offs
- jsr tile_to_vrambuf ; start X loop
inc graphic_x_offs
lda graphic_x_offs
cmp graphic_width
bne -
inc graphic_y_offs
lda graphic_y_offs
cmp graphic_height
bne --
rts
tile_to_vrambuf ; Copy one nybble/tile of a graphic to VRAM buffer.
; In:
; graphic_x/graphic_y:
; top left position in tiles
; graphic_x_offs/graphic_y_offs:
; position inside graphic in tiles
; vram_addr_hi:
; high byte of VRAM address
; Called by draw_bg_graphic.
lda graphic_y_offs ; offset to nybble -> nybble_offs
ldx graphic_width
jsr multiply ; X * A -> A
add graphic_x_offs
staa nybble_offs
lsr a ; graphic data byte -> temp1
tay
lda (graphics_ptr),y
sta temp1
; nybble from byte -> graphic_nybble;
; if even offset, upper nybble; if odd, lower
ldaa nybble_offs
and #%00000001
beq +
lda temp1
and #%00001111
jmp ++
+ lda temp1
lsr a
lsr a
lsr a
lsr a
++ sta graphic_nybble
; vram_addr_hi * 256
; + (graphic_y + graphic_y_offs) * 32
; + graphic_x + graphic_x_offs - 4
; -> word(nybble_vram_hi, nybble_vram_lo)
; graphic_x + graphic_x_offs - 4 -> vram_block_x
lda graphic_x
add graphic_x_offs
sub #4
sta vram_block_x
; (graphic_y + graphic_y_offs) * 32 + vram_addr_hi * 256
; -> word(nybble_vram_hi, nybble_vram_lo)
lda graphic_y
add graphic_y_offs
sta vram_block_y
lda vram_block_y
asl a
asl a
asl a
sta nybble_vram_lo
lda #0 ; start computing nybble_vram_hi
asl nybble_vram_lo ; 4th shift
rol a ; save overflowed bit
asl nybble_vram_lo ; 5th shift
rol a ; save overflowed bit
add vram_addr_hi
sta nybble_vram_hi
; word(nybble_vram_hi, nybble_vram_lo) += vram_block_x
lda nybble_vram_lo
add vram_block_x
sta nybble_vram_lo
lda nybble_vram_hi
adc #0
sta nybble_vram_hi
; set up a VRAM block with data size 1 and copy it to VRAM
; buffer
lda nybble_vram_hi
sta vram_block+1
lda nybble_vram_lo
sta vram_block+2
lda graphic_nybble
sta vram_block+3
lda #1
sta vram_block+0
jsr vram_blk_to_buf
rts
; -----------------------------------------------------------------------------
multiply ; Multiply (X * A -> A).
; Called by tile_to_vrambuf, draw_metaspr_gr.
sta multiply_temp
lda #0
cpx #0
beq +
clc
- adc multiply_temp
dex
bne -
+ rts
; -----------------------------------------------------------------------------
sprite_dma ; Copy interleaved sprite data to OAM.
; Called by nmi, init3.
lda #$00
sta oam_addr
lda #>sprite_data
sta oam_dma
rts
; -----------------------------------------------------------------------------
flush_vram_buf ; Move as many blocks as possible from vram_buffer to VRAM.
; Each block in vram_buffer: data size, address high, address
; low, data.
; Called by nmi, vram_blk_to_buf.
; maximum sum of (totalDataSize + blocks * 5) to copy
lda #100
sta vram_budget
ldy vram_buf_rd_pos
-- cpy vram_buf_wr_pos ; exit if all blocks copied
beq +
lda vram_buffer,y ; get data size; compute cost of copying
tax ; it (dataSize + 5)
add #5
sta vram_block_cost
lda vram_budget ; remove cost from budget
sub vram_block_cost
bcc + ; exit if out of budget
sta vram_budget
lda vram_buf_free ; add total data size to number of free
clc ; bytes
adc vram_buffer,y
adc #3
sta vram_buf_free
iny ; get and set VRAM address
lda vram_buffer,y
sta ppu_addr
iny
lda vram_buffer,y
sta ppu_addr
iny ; copy block data to VRAM
- lda vram_buffer,y
sta ppu_data
iny
dex
bne -
jmp -- ; next block
+ sty vram_buf_rd_pos ; all blocks copied or out of budget
rts
; -----------------------------------------------------------------------------
vram_blk_to_buf ; Copy current VRAM block to VRAM buffer.
; Called by tile_to_vrambuf, update_attr_blk, init3,
; animate_color, hilite_inp_row.
; total block size -> temp1; does block fit in VRAM buffer?
lda vram_block+0
add #3
sta temp1
lda vram_buf_free
cmp temp1
bcs +
; does not fit; if NMI is being skipped, flush VRAM buffer and
; restart sub, otherwise just restart sub
lda skip_nmi
beq vram_blk_to_buf
jsr flush_vram_buf
jmp vram_blk_to_buf
+ ; fits; subtract total block size from free space, copy VRAM
; block to VRAM buffer (X/Y = source/destination index)
sub temp1
sta vram_buf_free
ldx #0
ldy vram_buf_wr_pos
- lda vram_block,x
sta vram_buffer,y
inx
iny
cpx temp1
bne -
sty vram_buf_wr_pos
rts
; -----------------------------------------------------------------------------
draw_metaspr_gr ; Draw a graphic (e.g. the hand cursor) as a metasprite.
; In: A = graphic id (see graphix_offsets)
; Out: A = index to metaspr_indexes
; Called by init3.
sta graphic_id ; set pointer to address of graphic
jsr set_graphix_ptr
ldy #0 ; get width and height
lda (graphics_ptr),y
sta metaspr_width
tax
iny
lda (graphics_ptr),y
sta metaspr_height
; width * height (A * X) -> A, grafic_dataleft
jsr multiply
sta grafic_dataleft
add #2 ; total size of graphic -> A
; find first free byte in metaspr_indexes;
; save to metaspr_index and X;
; add total size of graphic to all following bytes in
; metaspr_indexes
jsr findfreemetaspr ; A = value to add
sta metaspr_index
tax
lda metaspr_indexes,x ; index to metasprites -> X
tax
; save width and height of graphic to metasprite data
lda metaspr_width
sta metasprites,x
lda metaspr_height
sta metasprites+1,x
inx ; indexes to individual sprites
inx
; assign grafic_dataleft free sprites to metasprite, starting
; from first free sprite
ldy #255
- iny
lda sprite_data_atr,y
bne -
tya
sta metasprites,x
inx
dec grafic_dataleft
bne -
; set up tiles and attributes for individual sprites
ldx metaspr_index
lda graphic_id
jsr init_metasprite ; A = id, X = index to metaspr_indexes
; set up positions for individual sprites
ldx metaspr_index
jsr update_metaspr ; X = metasprite index
lda metaspr_index ; return metasprite index
rts
; -----------------------------------------------------------------------------
set_graphix_ptr ; Set graphics pointer.
; In: A = graphic id (see graphix_offsets).
; Out: graphics_ptr = address of graphic.
; Called by draw_bg_graphic, draw_metaspr_gr, init_metasprite.
sta temp1 ; id
lda #<graphix_offsets ; set pointer to start of offsets
sta graphics_ptr+0
lda #>graphix_offsets
sta graphics_ptr+1
lda temp1 ; word(graphics_ptr) += graphic_id >> 7
bpl +
inc graphics_ptr+1 ; never accessed
+ asl a ; offset to offset
tay
lda (graphics_ptr),y ; offset to graphic
pha
iny
lda (graphics_ptr),y
add #<graphix_offsets ; address of graphic
sta graphics_ptr+0
pla
adc #>graphix_offsets
sta graphics_ptr+1
rts
; -----------------------------------------------------------------------------
update_metaspr ; Update metasprite's position to its individual sprites'
; positions.
; In:
; X: metasprite index (0 = hand cursor, 1 = revolving cursor,
; 2 = flying letter)
; metasprite_x, metasprite_y: position of metasprite
; Called by draw_metaspr_gr, do_every_frame, update_revolv,
; input_effects, move_flying_ltr.
lda metaspr_indexes,x ; index to metasprite info
tax
lda metasprites,x ; get size of metasprite
sta metaspr_width
lda metasprites+1,x
sta metaspr_height
inx ; start of target sprite indexes
inx
lda metasprite_y ; init Y position of target sprites
sta sprite_y+0
lda always_zero2
sta sprite_y+1
-- ; outer loop; hide sprite row if beyond bottom edge of screen;
; init inner loop counter and X position of target sprites
lda sprite_y+1
bne hide_sprite_row
lda metaspr_width
sta nybbles_left_x
lda metasprite_x
sta sprite_x+0
lda always_zero1
sta sprite_x+1
;
- ; inner loop; hide sprite if beyond right edge of screen
lda sprite_x+1
bne +
lda metasprites,x ; index to target sprite
tay
lda sprite_data_til,y ; hide if target tile = 0
beq +
lda sprite_x+0 ; set target sprite position
sta sprite_data_x,y
lda sprite_y+0
sta sprite_data_y,y
inx
jmp ++
;
+ lda metasprites,x ; hide sprite
tay
lda #255
sta sprite_data_y,y
inx
;
++ lda sprite_x+0 ; next sprite
add #8
sta sprite_x+0
lda sprite_x+1
adc #0
sta sprite_x+1
dec nybbles_left_x
bne -
;
next_row lda sprite_y+0 ; next row of sprites
add #8
sta sprite_y+0
lda sprite_y+1
adc #0
sta sprite_y+1
dec metaspr_height
bne --
rts
hide_sprite_row lda metaspr_width ; hide row of sprites
sta nybbles_left_x
- lda metasprites,x
tay
lda #255
sta sprite_data_y,y
inx
dec nybbles_left_x
bne -
jmp next_row
; -----------------------------------------------------------------------------
init_metasprite ; Set up tiles and attributes for individual sprites of a
; metasprite.
; In: A = graphic id (see graphix_offsets),
; X = index to metaspr_indexes.
; Called by draw_metaspr_gr, input_effects.
sta graphic_id
lda metaspr_indexes,x ; index to metasprites
add #2
pha
lda graphic_id ; start reading graphic (A = id)
jsr set_graphix_ptr
ldy #0 ; get dimensions of graphic
lda (graphics_ptr),y
sta metaspr_width
iny
lda (graphics_ptr),y
sta metaspr_height
lda #0 ; 2 -> nybble_offs
ora #%00011100
ldx unused1
sta always_00011100
lda #2
ldy #1
sty always_one
sta nybble_offs
lda #0
sta always_zero3
pla ; index to metasprites -> X
tax
init_loop_y ; copy all rows of graphics data to target sprites
lda metaspr_width ; inner loop counter
sta nybbles_left_x
init_loop_x ; copy one row of graphics data to target sprites
lda nybble_offs ; graphics data byte -> temp1
lsr a
add #1
tay
lda (graphics_ptr),y
sta temp1
lda nybble_offs ; if even offset, push high nybble,
and #%00000001 ; else low
beq +
lda temp1
and #%00001111
jmp ++
+ lda temp1
lsr a
lsr a
lsr a
lsr a
++ pha
lda metasprites,x ; nybble -> target sprite tile
tay
pla
sta sprite_data_til,y
lda always_00011100 ; %00011100 -> target sprite attribute
sta sprite_data_atr,y
inx ; next target sprite
lda nybble_offs ; increment
add always_one
sta nybble_offs
dec nybbles_left_x
bne init_loop_x
lda nybble_offs ; do nothing
add always_zero3
sta nybble_offs
dec metaspr_height
bne init_loop_y
rts
; -----------------------------------------------------------------------------
convert_sprites ; Convert planar sprite data to interleaved.
; Sprites with attribute byte $00 will be hidden.
; Called by do_every_frame.
; ascending order every 2nd frame, descending every 2nd frame
lda odd_frame_flag1
eor #%00000001
sta odd_frame_flag1
bne ++
; descending order: planar sprites 61-0 -> interleaved sprites
; 2-63
ldy #(2*4) ; destination offset
ldx #61 ; source offset
- lda sprite_data_atr,x
beq + ; hide if attributes are $00
;
; A = attribute byte, X = src index, Y = dst index
jsr convert_sprite
dex
bpl -
rts
+ jsr hide_sprite_sub ; Y = index
dex
bpl -
rts ; never accessed
++ ; ascending order: planar sprites 0-61 -> interleaved sprites
; 2-63
ldy #(2*4) ; destination offset
ldx #0 ; source offset
- lda sprite_data_atr,x
beq + ; hide if attributes are $00
;
; A = attribute byte, X = src index, Y = dst index
jsr convert_sprite
inx
cpx #62
bne -
rts
+ jsr hide_sprite_sub ; Y = index
inx
cpx #62
bne -
rts
convert_sprite ; Convert one non-hidden sprite from planar to interleaved.
; In: A = attribute byte, X/Y = source/target index.
; Out: Y += 4.
; Called by convert_sprites.
sta sprite_data+2,y
lda sprite_data_y,x
sta sprite_data+0,y
lda sprite_data_til,x
sta sprite_data+1,y
lda sprite_data_x,x
sta sprite_data+3,y
iny
iny
iny
iny
rts
hide_sprite_sub ; Hide a sprite in sprite_data.
; In: Y = index, out: Y += 4.
; Called by convert_sprites.
lda #255
sta sprite_data,y
iny
iny
iny
iny
rts
; -----------------------------------------------------------------------------
update_attr_blk ; Change the value of an attribute block (2 bits) within one
; attribute byte, preserving the other bits.
; In:
; vram_block_x: X position of attribute block (0-15)
; vram_block_y: Y position of attribute block (0-14)
; attr_fill_byte: value of new block (which 2 bits are read
; depends on vram_block_x and vram_block_y)
; Called by update_atr_byte, fill_atrblkrows.
; position of block within byte (0-3) -> Y
lda vram_block_y
and #%00000001
asl a
sta temp1
lda vram_block_x
and #%00000001
ora temp1
tay
; position of byte within attribute table (0-63) -> A, X
lda vram_block_y
asl a
asl a
and #%11111000
sta temp1
lda vram_block_x
lsr a
add temp1
tax
; vram_at0 + A -> VRAM block target address
ora #<vram_at0
sta vram_block+2
lda #>vram_at0
sta vram_block+1
lda #1 ; size of data to copy: one byte
sta vram_block+0
; combine old and new bits of attribute byte:
; (newAttributeByte & bitmaskDeterminedByY)