forked from RobertBaruch/lode_runner_reveng
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.tex
executable file
·10708 lines (9122 loc) · 744 KB
/
main.tex
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
\documentclass[10pt]{report}% ===> this file was generated automatically by noweave --- better not edit it
\usepackage {noweb}
\usepackage {graphicx}
\graphicspath { {./images/} }
\usepackage[table,dvipsnames]{xcolor}
\definecolor {apple_white}{rgb}{0.9,0.9,0.9}
\def \bk0 {\cellcolor{black}}
\def \bl0 {\cellcolor{Cerulean}}
\def \bw0 {\cellcolor{apple_white}}
\def \bo0 {\cellcolor{orange}}
\usepackage {booktabs}
\usepackage {tikz}
\usetikzlibrary {positioning, shapes.geometric, svg.path, arrows.meta, quotes, calc}
\tikzstyle{startstop} = [rectangle, rounded corners, font=\tiny, minimum width=1cm, minimum height=0.3cm,text centered, draw=black, fill=blue!30]
\tikzstyle{process} = [rectangle, text width=1cm, minimum width=1cm, font=\tiny, minimum height=0.3cm, text centered, draw=black, fill=orange!30]
\tikzstyle{decision} = [diamond, aspect=2, text width=1.2cm, minimum width=1cm, font=\tiny, minimum height=0.3cm, text centered, draw=black, fill=green!30]
\noweboptions {smallcode,longchunks}
% Generate assembly file with:
% notangle -Rpreamble main.nw > main.asm
%
% Generate tex file with:
% noweave -delay -index main.nw > main.tex
% pdflatex main.tex (run twice for two passes)
% Indents in code only appears in the PDF output
% under TeX Live 2019.
%
% See also: https://www.cs.tufts.edu/~nr/noweb/johnson-lj.pdf
\begin{document}
\pagestyle{noweb}
\nwfilename{main.nw}\nwbegindocs{1}\chapter{Lode Runner}
\section{Introduction}
Lode Runner was a game originally written in 1982 by Douglas E. Smith (1960--2014) for
the Apple II series of computers, and published by Broderbund.
\begin{center}
\includegraphics[width=\columnwidth]{title-screen}
\end{center}
You control the movement of your character, moving left and right along brick
and bedrock platforms, climbing ladders,
and "monkey-traversing" ropes strung across gaps. The object is to collect all the
gold boxes while avoiding being touched by the guards. You can dig holes in
brick parts of the floor which can allow you to reach otherwise unreachable caverns,
and the holes can also trap the guards for a short while. Holes fill themselves in
after a short time period, and if you're in a hole when that happens, you lose
a life. However, if a guard is in the hole and the hole fills, the guard disappears and
reappears somewhere along the top of the screen.
You get points for collecting boxes and forcing guards to respawn. Once you collect
all the boxes, a ladder will appear leading out of the top of the screen. This
gets you to the next level, and play continues.
\begin{center}
\includegraphics[width=\columnwidth]{screen}
\end{center}
Lode Runner included 150 levels and also a level editor.
\section{About this document}
This is a literate programming document. This means the explanatory text is
interspersed with source code. The source code can be extracted from the document
and compiled.
The goal is to provide all the source code necessary to reproduce a binary
identical to the one found on the Internet Archive's {\Tt{}Lode{\_}Runner{\_}1983{\_}Broderbund{\_}cr{\_}Reset{\_}Vector.do\nwendquote}
disk image.
The assembly code is assembled using {\Tt{}dasm\nwendquote}.
This document doesn't explain every last detail. It's assumed that the reader can
find enough details on the 6502 processor and the Apple II series of computers
to fill in the gaps.
\chapter{Programming techniques}
\section{Zero page temporaries}
Zero-page consists essentially of global variables. Sometimes we need local
temporaries, and Lode Runner mostly doesn't use the stack for those. Rather,
some "global" variables are reserved for temporaries. You might see multiple
symbols equated to a single zero-page location. The names of such symbols are
used to make sense within their context.
\section{Tail calls}
Rather than a {\Tt{}JSR\nwendquote} immediately followed by an {\Tt{}RTS\nwendquote}, instead a {\Tt{}JMP\nwendquote}
can be used to save stack space, code space, and time. This is known as a
tail call, because it is a call that happens at the tail of a function.
\section{Unconditional branches}
The 6502 doesn't have an unconditional short jump. However, if you can find
a condition that is always true, this can serve as an unconditional short
jump, which saves space and time.
\section{Stretchy branches}
6502 branches have a limit to how far they can jump. If they really need to
jump farther than that, you have to put a {\Tt{}JMP\nwendquote} or an unconditional branch
within reach.
\section{Shared code}
To save space, sometimes code at the end of one function is also useful to
the next function, as long as it is within reach. This can save space, at
the expense of functions being completely independent.
\section{DOS}
Since programs generally come on disk, and such disks are genrally bootable,
the only thing the disk card does is load the data of the disk on track 0
sector 0 to location {\Tt{}0800\nwendquote} and then jump to it. Thus, any additional
services need to be supplied by the disk as a disk operating system. The most
popular ones were DOS 3.3 and ProDOS.
Lode Runner contains just the parts of DOS 3.3 it needs. See the section on
Disk routines for more information.
\section{Temporaries and scratch space}
\nwenddocs{}\nwbegincode{2}\sublabel{NW1Xx3lK-10jlgu-1}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-1}}}\moddef{defines~{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-1}}}\endmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{\relax}{NW1Xx3lK-10jlgu-2}\nwenddeflinemarkup
\nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1} EQU $0A ; 2 bytes
\nwlinkedidentc{TMP}{NW1Xx3lK-10jlgu-1} EQU $1A
\nwlinkedidentc{SCRATCH_5C}{NW1Xx3lK-10jlgu-1} EQU $5C
\nwlinkedidentc{MATH_TMPL}{NW1Xx3lK-10jlgu-1} EQU $6F
\nwlinkedidentc{MATH_TMPH}{NW1Xx3lK-10jlgu-1} EQU $70
\nwlinkedidentc{TMP_LOOP_CTR}{NW1Xx3lK-10jlgu-1} EQU $88
\nwlinkedidentc{SCRATCH_A1}{NW1Xx3lK-10jlgu-1} EQU $A1
\nwindexdefn{\nwixident{TMP{\_}PTR}}{TMP:unPTR}{NW1Xx3lK-10jlgu-1}\nwindexdefn{\nwixident{TMP}}{TMP}{NW1Xx3lK-10jlgu-1}\nwindexdefn{\nwixident{SCRATCH{\_}5C}}{SCRATCH:un5C}{NW1Xx3lK-10jlgu-1}\nwindexdefn{\nwixident{TMP{\_}LOOP{\_}CTR}}{TMP:unLOOP:unCTR}{NW1Xx3lK-10jlgu-1}\nwindexdefn{\nwixident{SCRATCH{\_}A1}}{SCRATCH:unA1}{NW1Xx3lK-10jlgu-1}\nwindexdefn{\nwixident{MATH{\_}TMPL}}{MATH:unTMPL}{NW1Xx3lK-10jlgu-1}\nwindexdefn{\nwixident{MATH{\_}TMPH}}{MATH:unTMPH}{NW1Xx3lK-10jlgu-1}\eatline
\nwalsodefined{\\{NW1Xx3lK-10jlgu-2}\\{NW1Xx3lK-10jlgu-3}\\{NW1Xx3lK-10jlgu-4}\\{NW1Xx3lK-10jlgu-5}\\{NW1Xx3lK-10jlgu-6}\\{NW1Xx3lK-10jlgu-7}\\{NW1Xx3lK-10jlgu-8}\\{NW1Xx3lK-10jlgu-9}\\{NW1Xx3lK-10jlgu-A}\\{NW1Xx3lK-10jlgu-B}\\{NW1Xx3lK-10jlgu-C}\\{NW1Xx3lK-10jlgu-D}\\{NW1Xx3lK-10jlgu-E}\\{NW1Xx3lK-10jlgu-F}\\{NW1Xx3lK-10jlgu-G}\\{NW1Xx3lK-10jlgu-H}\\{NW1Xx3lK-10jlgu-I}\\{NW1Xx3lK-10jlgu-J}\\{NW1Xx3lK-10jlgu-K}\\{NW1Xx3lK-10jlgu-L}\\{NW1Xx3lK-10jlgu-M}\\{NW1Xx3lK-10jlgu-N}\\{NW1Xx3lK-10jlgu-O}\\{NW1Xx3lK-10jlgu-P}\\{NW1Xx3lK-10jlgu-Q}\\{NW1Xx3lK-10jlgu-R}\\{NW1Xx3lK-10jlgu-S}\\{NW1Xx3lK-10jlgu-T}\\{NW1Xx3lK-10jlgu-U}\\{NW1Xx3lK-10jlgu-V}\\{NW1Xx3lK-10jlgu-W}\\{NW1Xx3lK-10jlgu-X}\\{NW1Xx3lK-10jlgu-Y}\\{NW1Xx3lK-10jlgu-Z}\\{NW1Xx3lK-10jlgu-a}\\{NW1Xx3lK-10jlgu-b}\\{NW1Xx3lK-10jlgu-c}\\{NW1Xx3lK-10jlgu-d}\\{NW1Xx3lK-10jlgu-e}\\{NW1Xx3lK-10jlgu-f}\\{NW1Xx3lK-10jlgu-g}\\{NW1Xx3lK-10jlgu-h}\\{NW1Xx3lK-10jlgu-i}\\{NW1Xx3lK-10jlgu-j}\\{NW1Xx3lK-10jlgu-k}\\{NW1Xx3lK-10jlgu-l}\\{NW1Xx3lK-10jlgu-m}\\{NW1Xx3lK-10jlgu-n}\\{NW1Xx3lK-10jlgu-o}\\{NW1Xx3lK-10jlgu-p}}\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{MATH{\_}TMPH}}{MATH:unTMPH}}\\{{\nwixident{MATH{\_}TMPL}}{MATH:unTMPL}}\\{{\nwixident{SCRATCH{\_}5C}}{SCRATCH:un5C}}\\{{\nwixident{SCRATCH{\_}A1}}{SCRATCH:unA1}}\\{{\nwixident{TMP}}{TMP}}\\{{\nwixident{TMP{\_}LOOP{\_}CTR}}{TMP:unLOOP:unCTR}}\\{{\nwixident{TMP{\_}PTR}}{TMP:unPTR}}}\nwendcode{}\nwbegindocs{3}\nwdocspar
\chapter{Apple II Graphics}
Hi-res graphics on the Apple II is odd. Graphics are memory-mapped, not exactly
consecutively, and bits don't always correspond to pixels. Color especially is
odd, compared to today's luxurious 32-bit per pixel RGBA.
The Apple II has two hi-res graphics pages, and maps the area from {\Tt{}{\$}2000-{\$}3FFF\nwendquote} to
high-res graphics page 1 (HGR1), and {\Tt{}{\$}4000-{\$}5FFF\nwendquote} to page 2 (HGR2).
We have routines to clear these screens.
\nwenddocs{}\nwbegincode{4}\sublabel{NW1Xx3lK-8jv1b-1}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\moddef{routines~{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\endmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{\relax}{NW1Xx3lK-8jv1b-2}\nwenddeflinemarkup
ORG $7A51
\nwlinkedidentc{CLEAR_HGR1}{NW1Xx3lK-8jv1b-1}:
SUBROUTINE
LDA #$20 ; Start at $2000
LDX #$40 ; End at $4000 (but not including)
BNE CLEAR_PAGE ; Unconditional jump
\nwlinkedidentc{CLEAR_HGR2}{NW1Xx3lK-8jv1b-1}:
SUBROUTINE
LDA #$40 ; Start at $4000
LDX #$60 ; End at $6000 (but not including)
; fallthrough
CLEAR_PAGE:
STA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}+1 ; Start with the page in A.
LDA #$00
STA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}
TAY
LDA #$80 ; fill byte = 0x80
.loop:
STA (\nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}),Y
INY
BNE .loop
INC \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}+1
CPX \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}+1
BNE .loop ; while \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1} != X * 0x100
RTS
\nwindexdefn{\nwixident{CLEAR{\_}HGR1}}{CLEAR:unHGR1}{NW1Xx3lK-8jv1b-1}\nwindexdefn{\nwixident{CLEAR{\_}HGR2}}{CLEAR:unHGR2}{NW1Xx3lK-8jv1b-1}\eatline
\nwalsodefined{\\{NW1Xx3lK-8jv1b-2}\\{NW1Xx3lK-8jv1b-3}\\{NW1Xx3lK-8jv1b-4}\\{NW1Xx3lK-8jv1b-5}\\{NW1Xx3lK-8jv1b-6}\\{NW1Xx3lK-8jv1b-7}\\{NW1Xx3lK-8jv1b-8}\\{NW1Xx3lK-8jv1b-9}\\{NW1Xx3lK-8jv1b-A}\\{NW1Xx3lK-8jv1b-B}\\{NW1Xx3lK-8jv1b-C}}\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{CLEAR{\_}HGR1}}{CLEAR:unHGR1}}\\{{\nwixident{CLEAR{\_}HGR2}}{CLEAR:unHGR2}}}\nwidentuses{\\{{\nwixident{TMP{\_}PTR}}{TMP:unPTR}}}\nwindexuse{\nwixident{TMP{\_}PTR}}{TMP:unPTR}{NW1Xx3lK-8jv1b-1}\nwendcode{}\nwbegindocs{5}\nwdocspar
\section{Pixels and their color}
First we'll talk about pixels. Nominally, the resolution of the hi-res graphics screen
is 280 pixels wide by 192 pixels tall. In the memory map, each row is represented
by 40 bytes. The high bit of each byte is not used for pixel data, but is used to
control color.
Here are some rules for how these bytes are turned into pixels:
\begin{itemize}
\item Pixels are drawn to the screen from byte data least significant bit first.
This means that for the first byte bit 0 is column 0, bit 1 is column 1,
and so on.
\item A pattern of {\Tt{}11\nwendquote} results in two white pixels at the {\Tt{}1\nwendquote} positions.
\item A pattern of {\Tt{}010\nwendquote} results at least in a colored pixel at the {\Tt{}1\nwendquote} position.
\item A pattern of {\Tt{}101\nwendquote} results at least in a colored pixel at the {\Tt{}0\nwendquote} position.
\item So, a pattern of {\Tt{}01010\nwendquote} results in at least three consecutive colored
pixels starting from the first {\Tt{}1\nwendquote} to the last {\Tt{}1\nwendquote}. The last {\Tt{}0\nwendquote} bit
would also be colored if followed by a {\Tt{}1\nwendquote}.
\item Likewise, a pattern of {\Tt{}11011\nwendquote} results in two white pixels, a colored pixel,
and then two more white pixels.
\item The color of a {\Tt{}010\nwendquote} pixel depends on the column that the {\Tt{}1\nwendquote} falls on, and
also whether the high bit of its byte was set or not.
\item The color of a {\Tt{}11011\nwendquote} pixel depends on the column that the {\Tt{}0\nwendquote} falls on, and
also whether the high bit of its byte was set or not.
\begin{center}
\begin{tabular}{@{}rcc@{}} \toprule
& Odd & Even \\ \cmidrule(r){2-3}
High bit clear & Green & Violet \\
High bit set & Orange & Blue \\ \bottomrule
\end{tabular}
\end{center}
The implication is that you can only select one pair of colors per byte.
\end{itemize}
An example would probably be good here. We will take one of the sprites from the game.
\begin{center}
\begin{tabular}{@{}rcc@{}} \toprule
Bytes & Bits & Pixel Data \\ \cmidrule{1-3}
{\Tt{}00\ 00\nwendquote} & {\Tt{}0000000\ 0000000\nwendquote} & {\Tt{}00000000000000\nwendquote} \\
{\Tt{}00\ 00\nwendquote} & {\Tt{}0000000\ 0000000\nwendquote} & {\Tt{}00000000000000\nwendquote} \\
{\Tt{}00\ 00\nwendquote} & {\Tt{}0000000\ 0000000\nwendquote} & {\Tt{}00000000000000\nwendquote} \\
{\Tt{}55\ 00\nwendquote} & {\Tt{}1010101\ 0000000\nwendquote} & {\Tt{}10101010000000\nwendquote} \\
{\Tt{}41\ 00\nwendquote} & {\Tt{}1000001\ 0000000\nwendquote} & {\Tt{}10000010000000\nwendquote} \\
{\Tt{}01\ 00\nwendquote} & {\Tt{}0000001\ 0000000\nwendquote} & {\Tt{}10000000000000\nwendquote} \\
{\Tt{}55\ 00\nwendquote} & {\Tt{}1010101\ 0000000\nwendquote} & {\Tt{}10101010000000\nwendquote} \\
{\Tt{}50\ 00\nwendquote} & {\Tt{}1010000\ 0000000\nwendquote} & {\Tt{}00001010000000\nwendquote} \\
{\Tt{}50\ 00\nwendquote} & {\Tt{}1010000\ 0000000\nwendquote} & {\Tt{}00001010000000\nwendquote} \\
{\Tt{}51\ 00\nwendquote} & {\Tt{}1010001\ 0000000\nwendquote} & {\Tt{}10001010000000\nwendquote} \\
{\Tt{}55\ 00\nwendquote} & {\Tt{}1010101\ 0000000\nwendquote} & {\Tt{}10101010000000\nwendquote} \\ \bottomrule
\end{tabular}
\end{center}
The game automatically sets the high bit of each byte, so we know we're going to see
orange and blue. Assuming that the following bits are all zero, and we place the
sprite starting at column 0, we should see this:
\begin{center}
\begin{tabular}{@{}rcccccccccccccc@{}}
0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
1 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
2 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
3 & \bl0 & \bl0 & \bl0 & \bl0 & \bl0 & \bl0 & \bl0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
4 & \bl0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bl0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
5 & \bl0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
6 & \bl0 & \bl0 & \bl0 & \bl0 & \bl0 & \bl0 & \bl0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
7 & \bk0 & \bk0 & \bk0 & \bk0 & \bl0 & \bl0 & \bl0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
8 & \bk0 & \bk0 & \bk0 & \bk0 & \bl0 & \bl0 & \bl0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
9 & \bl0 & \bk0 & \bk0 & \bk0 & \bl0 & \bl0 & \bl0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
10 & \bl0 & \bl0 & \bl0 & \bl0 & \bl0 & \bl0 & \bl0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
\end{tabular}
\end{center}
Here is a more complex sprite:
\begin{center}
\begin{tabular}{@{}rcc@{}} \toprule
Bytes & Bits & Pixel Data \\ \cmidrule{1-3}
{\Tt{}40\ 00\nwendquote} & {\Tt{}1000000\ 0000000\nwendquote} & {\Tt{}00000010000000\nwendquote} \\
{\Tt{}60\ 01\nwendquote} & {\Tt{}1100000\ 0000001\nwendquote} & {\Tt{}00000111000000\nwendquote} \\
{\Tt{}60\ 01\nwendquote} & {\Tt{}1100000\ 0000001\nwendquote} & {\Tt{}00000111000000\nwendquote} \\
{\Tt{}70\ 00\nwendquote} & {\Tt{}1110000\ 0000000\nwendquote} & {\Tt{}00001110000000\nwendquote} \\
{\Tt{}6C\ 01\nwendquote} & {\Tt{}1101100\ 0000001\nwendquote} & {\Tt{}00110111000000\nwendquote} \\
{\Tt{}36\ 06\nwendquote} & {\Tt{}0110110\ 0000110\nwendquote} & {\Tt{}01101100110000\nwendquote} \\
{\Tt{}30\ 00\nwendquote} & {\Tt{}0110000\ 0000000\nwendquote} & {\Tt{}00001100000000\nwendquote} \\
{\Tt{}70\ 00\nwendquote} & {\Tt{}1110000\ 0000000\nwendquote} & {\Tt{}00001110000000\nwendquote} \\
{\Tt{}5E\ 01\nwendquote} & {\Tt{}1011110\ 0000001\nwendquote} & {\Tt{}01111011000000\nwendquote} \\
{\Tt{}40\ 01\nwendquote} & {\Tt{}1000000\ 0000001\nwendquote} & {\Tt{}00000011000000\nwendquote} \\
{\Tt{}40\ 01\nwendquote} & {\Tt{}1000000\ 0000001\nwendquote} & {\Tt{}00000011000000\nwendquote} \\ \bottomrule
\end{tabular}
\end{center}
\begin{center}
\begin{tabular}{@{}rcccccccccccccc@{}}
0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bl0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
1 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bw0 & \bw0 & \bw0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
2 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bw0 & \bw0 & \bw0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
3 & \bk0 & \bk0 & \bk0 & \bk0 & \bw0 & \bw0 & \bw0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
4 & \bk0 & \bk0 & \bw0 & \bw0 & \bo0 & \bw0 & \bw0 & \bw0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
5 & \bk0 & \bw0 & \bw0 & \bl0 & \bw0 & \bw0 & \bk0 & \bk0 & \bw0 & \bw0 & \bk0 & \bk0 & \bk0 & \bk0 \\
6 & \bk0 & \bk0 & \bk0 & \bk0 & \bw0 & \bw0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
7 & \bk0 & \bk0 & \bk0 & \bk0 & \bw0 & \bw0 & \bw0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
8 & \bk0 & \bw0 & \bw0 & \bw0 & \bw0 & \bl0 & \bw0 & \bw0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
9 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bw0 & \bw0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
10 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bw0 & \bw0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 & \bk0 \\
\end{tabular}
\end{center}
Take note of the orange and blue pixels. All the patterns noted in the rules above are used.
\section{The sprites}
Lode Runner defines 104 sprites, each being 11 rows, with two bytes per row. The first bytes of
all 104 sprites are in the table first, then the second bytes, then the third bytes, and so on.
Later we will see that only the leftmost 10 pixels out of the 14-pixel description is used.
\nwenddocs{}\nwbegincode{6}\sublabel{NW1Xx3lK-1W8AJS-1}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-1}}}\moddef{tables~{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-1}}}\endmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{\relax}{NW1Xx3lK-1W8AJS-2}\nwenddeflinemarkup
ORG $AD00
\nwlinkedidentc{SPRITE_DATA}{NW1Xx3lK-1W8AJS-1}:
INCLUDE "sprite_data.asm"
\nwindexdefn{\nwixident{SPRITE{\_}DATA}}{SPRITE:unDATA}{NW1Xx3lK-1W8AJS-1}\eatline
\nwalsodefined{\\{NW1Xx3lK-1W8AJS-2}\\{NW1Xx3lK-1W8AJS-3}\\{NW1Xx3lK-1W8AJS-4}\\{NW1Xx3lK-1W8AJS-5}\\{NW1Xx3lK-1W8AJS-6}\\{NW1Xx3lK-1W8AJS-7}\\{NW1Xx3lK-1W8AJS-8}\\{NW1Xx3lK-1W8AJS-9}\\{NW1Xx3lK-1W8AJS-A}\\{NW1Xx3lK-1W8AJS-B}\\{NW1Xx3lK-1W8AJS-C}\\{NW1Xx3lK-1W8AJS-D}\\{NW1Xx3lK-1W8AJS-E}\\{NW1Xx3lK-1W8AJS-F}\\{NW1Xx3lK-1W8AJS-G}\\{NW1Xx3lK-1W8AJS-H}\\{NW1Xx3lK-1W8AJS-I}\\{NW1Xx3lK-1W8AJS-J}\\{NW1Xx3lK-1W8AJS-K}\\{NW1Xx3lK-1W8AJS-L}\\{NW1Xx3lK-1W8AJS-M}\\{NW1Xx3lK-1W8AJS-N}\\{NW1Xx3lK-1W8AJS-O}\\{NW1Xx3lK-1W8AJS-P}\\{NW1Xx3lK-1W8AJS-Q}\\{NW1Xx3lK-1W8AJS-R}\\{NW1Xx3lK-1W8AJS-S}\\{NW1Xx3lK-1W8AJS-T}\\{NW1Xx3lK-1W8AJS-U}\\{NW1Xx3lK-1W8AJS-V}\\{NW1Xx3lK-1W8AJS-W}\\{NW1Xx3lK-1W8AJS-X}}\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{SPRITE{\_}DATA}}{SPRITE:unDATA}}}\nwendcode{}\nwbegindocs{7}\nwdocspar
\input{sprite_tables.tex}
\nwenddocs{}\nwbegincode{8}\sublabel{NW1Xx3lK-10jlgu-2}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-2}}}\moddef{defines~{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-10jlgu-1}{NW1Xx3lK-10jlgu-3}\nwenddeflinemarkup
SPRITE_EMPTY EQU #$00
SPRITE_BRICK EQU #$01
SPRITE_STONE EQU #$02
SPRITE_LADDER EQU #$03
SPRITE_ROPE EQU #$04
SPRITE_TRAP EQU #$05
SPRITE_INVISIBLE_LADDER EQU #$06
SPRITE_GOLD EQU #$07
SPRITE_GUARD EQU #$08
SPRITE_PLAYER EQU #$09
SPRITE_ALLWHITE EQU #$0A
SPRITE_BRICK_FILL0 EQU #$37
SPRITE_BRICK_FILL1 EQU #$38
SPRITE_GUARD_EGG0 EQU #$39
SPRITE_GUARD_EGG1 EQU #$3A
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwendcode{}\nwbegindocs{9}\nwdocspar
\section{Shifting sprites}
This is all very good if we're going to draw sprites exactly on 7-pixel
boundaries, but what if we want to draw them starting at other columns?
In general, such a shifted sprite would straddle three bytes, and Lode
Runner sets aside an area of memory at the end of zero page for 11 rows
of three bytes that we'll write to when we want to compute the data for
a shifted sprite.
\nwenddocs{}\nwbegincode{10}\sublabel{NW1Xx3lK-10jlgu-3}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-3}}}\moddef{defines~{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-10jlgu-2}{NW1Xx3lK-10jlgu-4}\nwenddeflinemarkup
\nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3} EQU $DF ; 33 bytes
\nwindexdefn{\nwixident{BLOCK{\_}DATA}}{BLOCK:unDATA}{NW1Xx3lK-10jlgu-3}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{BLOCK{\_}DATA}}{BLOCK:unDATA}}}\nwendcode{}\nwbegindocs{11}\nwdocspar
Lode Runner also contains tables which show how to shift any arbitrary
7-pixel pattern right by any amount from zero to six pixels.
For example, suppose we start with a pixel pattern of {\Tt{}0110001\nwendquote}, and we want to
shift that right by three bits. The 14-bit result would be {\Tt{}0000110\ 0010000\nwendquote}.
However, we have to break that up into bytes, reverse the bits (remember that
each byte's bits are output as pixels least significant bit first), and set
their high bits, so we end up with {\Tt{}10110000\ 10000100\nwendquote}.
Now, given a shift amount and a pixel pattern, we should be able to find the
two-byte shifted pattern. Lode Runner accomplishes this with table lookups as follows:
\vspace{1em}
\begin{tikzpicture}
[basicbox/.style={draw,rectangle,inner sep=0pt,minimum width=1.5cm,minimum height=1.5cm,fill=blue!10},
pageoffsets/.style={basicbox,minimum height=1cm,text height=1.5ex,text depth=.25ex},
multilinebox/.style={basicbox,text width=1cm,align=center}]
\node (pixelshiftpages) at (0,0) [multilinebox] {pixel shift pages};
\node (start) at (-3,0) {};
\draw [->] (start) -- (pixelshiftpages) node [above,text width=1cm,align=center,midway] {shift amount};
\node (offsets0) [pageoffsets,anchor=north,below right=0 and 2 of pixelshiftpages.north east] {offsets};
\node (pages0) [pageoffsets,below=0 of offsets0.south] {pages};
\node (offsets1) [pageoffsets,below=0 of pages0.south,fill=blue!30] {offsets};
\node (pages1) [pageoffsets,below=0 of offsets1.south,fill=blue!30] {pages};
\node (offsets2) [pageoffsets,below=0 of pages1.south] {offsets};
\node (pages2) [pageoffsets,below=0 of offsets2.south] {pages};
\draw [->] (pixelshiftpages) -- (offsets1.north west) {};
\node (pixelpattern) [above left=1 and 0 of offsets0.north west] {pixel pattern};
\draw [->] (pixelpattern.south) |- (offsets1.west) {};
\draw [->] (pixelpattern.south) |- (pages1.west) {};
\node (patterntable) [multilinebox,minimum height=4cm,text width=1.2cm,anchor=north west,below right=0 and 2 of offsets0.north east] {pixel pattern table};
\node (join) [inner sep=0pt,below right=0 and 1 of offsets1.south east] {};
\draw (offsets1.east) -- (join);
\draw (pages1.east) -- (join);
\draw [->] (join) -- ([yshift=5mm]patterntable.west);
\end{tikzpicture}
\vspace{1em}
The pixel pattern table is a table of every possible pattern of 7 consecutive pixels
spread out over two bytes. This table is 512 entries, each entry being two bytes.
A naive table would have redundancy. For example the pattern {\Tt{}0000100\nwendquote} starting
at column 0 is exactly the same as the pattern {\Tt{}0001000\nwendquote} starting at column 1.
This table eliminates that redundancy.
\nwenddocs{}\nwbegincode{12}\sublabel{NW1Xx3lK-1W8AJS-2}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-2}}}\moddef{tables~{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-1W8AJS-1}{NW1Xx3lK-1W8AJS-3}\nwenddeflinemarkup
ORG $A900
\nwlinkedidentc{PIXEL_PATTERN_TABLE}{NW1Xx3lK-1W8AJS-2}:
INCLUDE "pixel_pattern_table.asm"
\nwindexdefn{\nwixident{PIXEL{\_}PATTERN{\_}TABLE}}{PIXEL:unPATTERN:unTABLE}{NW1Xx3lK-1W8AJS-2}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{PIXEL{\_}PATTERN{\_}TABLE}}{PIXEL:unPATTERN:unTABLE}}}\nwendcode{}\nwbegindocs{13}\nwdocspar
Now we just need tables which index into {\Tt{}\nwlinkedidentq{PIXEL{\_}PATTERN{\_}TABLE}{NW1Xx3lK-1W8AJS-2}\nwendquote} for every
7-pixel pattern and shift value. This table works by having the page number
for the shifted pixel pattern at index {\Tt{}shift\ *\ 0x100\ +\ 0x80\ +\ pattern\nwendquote}
and the offset at index {\Tt{}shift\ *\ 0x100\ +\ pattern\nwendquote}.
\nwenddocs{}\nwbegincode{14}\sublabel{NW1Xx3lK-1W8AJS-3}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-3}}}\moddef{tables~{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-1W8AJS-2}{NW1Xx3lK-1W8AJS-4}\nwenddeflinemarkup
ORG $A200
\nwlinkedidentc{PIXEL_SHIFT_TABLE}{NW1Xx3lK-1W8AJS-3}:
INCLUDE "pixel_shift_table.asm"
\nwindexdefn{\nwixident{PIXEL{\_}SHIFT{\_}TABLE}}{PIXEL:unSHIFT:unTABLE}{NW1Xx3lK-1W8AJS-3}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{PIXEL{\_}SHIFT{\_}TABLE}}{PIXEL:unSHIFT:unTABLE}}}\nwendcode{}\nwbegindocs{15}\nwdocspar
Rather than multiplying the shift value by {\Tt{}0x100\nwendquote}, we instead define
another table which holds the page numbers for the shift tables for each
shift value.
\nwenddocs{}\nwbegincode{16}\sublabel{NW1Xx3lK-1W8AJS-4}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-4}}}\moddef{tables~{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-1W8AJS-3}{NW1Xx3lK-1W8AJS-5}\nwenddeflinemarkup
ORG $84C1
\nwlinkedidentc{PIXEL_SHIFT_PAGES}{NW1Xx3lK-1W8AJS-4}:
HEX A2 A3 A4 A5 A6 A7 A8
\nwindexdefn{\nwixident{PIXEL{\_}SHIFT{\_}PAGES}}{PIXEL:unSHIFT:unPAGES}{NW1Xx3lK-1W8AJS-4}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{PIXEL{\_}SHIFT{\_}PAGES}}{PIXEL:unSHIFT:unPAGES}}}\nwendcode{}\nwbegindocs{17}\nwdocspar
So we can get shifted pixels by indexing into all these tables.
Now we can define a routine that will take a sprite number and a pixel shift
amount, and write the shifted pixel data into the {\Tt{}\nwlinkedidentq{BLOCK{\_}DATA}{NW1Xx3lK-10jlgu-3}\nwendquote} area. The
routine first shifts the first byte of the sprite into a two-byte area. Then
it shifts the second byte of the sprite, and combines that two-byte result
with the first. Thus, we shift two bytes of sprite data into a three-byte
result.
\begin{center}
\begin{tikzpicture}
[basicbox/.style={draw,rectangle,inner sep=0pt,minimum width=1.5cm,minimum height=0.5cm,fill=blue!10}]
\node (spriterowbyte0) at (0,0) [basicbox] {};
\node (spriterowbyte1) [basicbox,right=0 of spriterowbyte0.east] {};
\node (spriterowlabel) [left=0.1 of spriterowbyte0.west] {sprite row};
\node (shifted0byte0) [basicbox,below left=1 and 0 of spriterowbyte0.south west] {};
\node (shifted0byte1) [basicbox,right=0 of shifted0byte0.east] {};
\node (shifted1byte0) [basicbox,below right=2 and 0 of spriterowbyte0.south west] {};
\node (shifted1byte1) [basicbox,right=0 of shifted1byte0.east] {};
\node (orlabel) [below=0 of shifted0byte1] {OR};
\draw [->] (spriterowbyte0.south) -- (shifted0byte0.north east)
node [left,text width=1cm,align=center,midway] {shift};
\draw [->] (spriterowbyte1.south) to [auto, bend left=45] node {shift} (shifted1byte0.north east);
\node (result0) [basicbox,below left=0.5 and 0 of shifted1byte0.south west] {};
\node (result1) [basicbox,right=0 of result0.east] {};
\node (result2) [basicbox,right=0 of result1.east] {};
\draw [->] (shifted0byte0) -- (result0) {};
\draw [->] (shifted1byte0) -- (result1) {};
\draw [->] (shifted1byte1) -- (result2) {};
\node (blocklabel) [right=0.1 of result2.east] {block data};
\end{tikzpicture}
\end{center}
Rather than load addresses from the tables and store them, the routine
modifies its own instructions with those addresses.
\nwenddocs{}\nwbegincode{18}\sublabel{NW1Xx3lK-10jlgu-4}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-4}}}\moddef{defines~{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-10jlgu-3}{NW1Xx3lK-10jlgu-5}\nwenddeflinemarkup
\nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4} EQU $1D
\nwlinkedidentc{SPRITE_NUM}{NW1Xx3lK-10jlgu-4} EQU $1E
\nwindexdefn{\nwixident{ROW{\_}COUNT}}{ROW:unCOUNT}{NW1Xx3lK-10jlgu-4}\nwindexdefn{\nwixident{SPRITE{\_}NUM}}{SPRITE:unNUM}{NW1Xx3lK-10jlgu-4}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{ROW{\_}COUNT}}{ROW:unCOUNT}}\\{{\nwixident{SPRITE{\_}NUM}}{SPRITE:unNUM}}}\nwendcode{}\nwbegindocs{19}\nwdocspar
\nwenddocs{}\nwbegincode{20}\sublabel{NW1Xx3lK-8jv1b-2}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-2}}}\moddef{routines~{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-8jv1b-1}{NW1Xx3lK-8jv1b-3}\nwenddeflinemarkup
ORG $8438
\nwlinkedidentc{COMPUTE_SHIFTED_SPRITE}{NW1Xx3lK-8jv1b-2}:
SUBROUTINE
; Enter routine with X set to pixel shift amount and
; \nwlinkedidentc{SPRITE_NUM}{NW1Xx3lK-10jlgu-4} containing the sprite number to read.
.offset_table EQU $A000 ; Target addresses in read
.page_table EQU $A080 ; instructions. The only truly
.shift_ptr_byte0 EQU $A000 ; necessary value here is the
.shift_ptr_byte1 EQU $A000 ; 0x80 in .shift_ptr_byte0.
LDA #$0B ; 11 rows
STA \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4}
LDA #<\nwlinkedidentc{SPRITE_DATA}{NW1Xx3lK-1W8AJS-1}
STA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}
LDA #>\nwlinkedidentc{SPRITE_DATA}{NW1Xx3lK-1W8AJS-1}
STA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}+1 ; \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1} = \nwlinkedidentc{SPRITE_DATA}{NW1Xx3lK-1W8AJS-1}
LDA \nwlinkedidentc{PIXEL_SHIFT_PAGES}{NW1Xx3lK-1W8AJS-4},X
STA .rd_offset_table + 2
STA .rd_page_table + 2
STA .rd_offset_table2 + 2
STA .rd_page_table2 + 2 ; Fix up pages in lookup instructions
; based on shift amount (X).
LDX #$00 ; X is the offset into \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}.
.loop: ; === LOOP === (over all 11 rows)
LDY \nwlinkedidentc{SPRITE_NUM}{NW1Xx3lK-10jlgu-4}
LDA (\nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}),Y
TAY ; Get sprite pixel data.
.rd_offset_table:
LDA .offset_table,Y ; Load offset for shift amount.
STA .rd_shift_ptr_byte0 + 1
CLC
ADC #$01
STA .rd_shift_ptr_byte1 + 1 ; Fix up instruction offsets with it.
.rd_page_table:
LDA .page_table,Y ; Load page for shift amount.
STA .rd_shift_ptr_byte0 + 2
STA .rd_shift_ptr_byte1 + 2 ; Fix up instruction page with it.
.rd_shift_ptr_byte0:
LDA .shift_ptr_byte0 ; Read shifted pixel data byte 0
STA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X ; and store in block data byte 0.
.rd_shift_ptr_byte1:
LDA .shift_ptr_byte1 ; Read shifted pixel data byte 1
STA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}+1,X ; and store in block data byte 1.
LDA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}
CLC
ADC #$68
STA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}
LDA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}+1
ADC #$00
STA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}+1 ; \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}++
; Now basically do the same thing with the second sprite byte
LDY \nwlinkedidentc{SPRITE_NUM}{NW1Xx3lK-10jlgu-4}
LDA (\nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}),Y
TAY ; Get sprite pixel data.
.rd_offset_table2:
LDA .offset_table,Y ; Load offset for shift amount.
STA .rd_shift_ptr2_byte0 + 1
CLC
ADC #$01
STA .rd_shift_ptr2_byte1 + 1 ; Fix up instruction offsets with it.
.rd_page_table2:
LDA .page_table,Y ; Load page for shift amount.
STA .rd_shift_ptr2_byte0 + 2
STA .rd_shift_ptr2_byte1 + 2 ; Fix up instruction page with it.
.rd_shift_ptr2_byte0:
LDA .shift_ptr_byte0 ; Read shifted pixel data byte 0
ORA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}+1,X ; OR with previous block data byte 1
STA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}+1,X ; and store in block data byte 1.
.rd_shift_ptr2_byte1:
LDA .shift_ptr_byte1 ; Read shifted pixel data byte 1
STA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}+2,X ; and store in block data byte 2.
LDA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}
CLC
ADC #$68
STA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}
LDA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}+1
ADC #$00
STA \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}+1 ; \nwlinkedidentc{TMP_PTR}{NW1Xx3lK-10jlgu-1}++
INX
INX
INX ; X += 3
DEC \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4} ; \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4}--
BNE .loop ; loop while \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4} > 0
RTS
\nwindexdefn{\nwixident{COMPUTE{\_}SHIFTED{\_}SPRITE}}{COMPUTE:unSHIFTED:unSPRITE}{NW1Xx3lK-8jv1b-2}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{COMPUTE{\_}SHIFTED{\_}SPRITE}}{COMPUTE:unSHIFTED:unSPRITE}}}\nwidentuses{\\{{\nwixident{BLOCK{\_}DATA}}{BLOCK:unDATA}}\\{{\nwixident{PIXEL{\_}SHIFT{\_}PAGES}}{PIXEL:unSHIFT:unPAGES}}\\{{\nwixident{ROW{\_}COUNT}}{ROW:unCOUNT}}\\{{\nwixident{SPRITE{\_}DATA}}{SPRITE:unDATA}}\\{{\nwixident{SPRITE{\_}NUM}}{SPRITE:unNUM}}\\{{\nwixident{TMP{\_}PTR}}{TMP:unPTR}}}\nwindexuse{\nwixident{BLOCK{\_}DATA}}{BLOCK:unDATA}{NW1Xx3lK-8jv1b-2}\nwindexuse{\nwixident{PIXEL{\_}SHIFT{\_}PAGES}}{PIXEL:unSHIFT:unPAGES}{NW1Xx3lK-8jv1b-2}\nwindexuse{\nwixident{ROW{\_}COUNT}}{ROW:unCOUNT}{NW1Xx3lK-8jv1b-2}\nwindexuse{\nwixident{SPRITE{\_}DATA}}{SPRITE:unDATA}{NW1Xx3lK-8jv1b-2}\nwindexuse{\nwixident{SPRITE{\_}NUM}}{SPRITE:unNUM}{NW1Xx3lK-8jv1b-2}\nwindexuse{\nwixident{TMP{\_}PTR}}{TMP:unPTR}{NW1Xx3lK-8jv1b-2}\nwendcode{}\nwbegindocs{21}\nwdocspar
\section{Memory mapped graphics}
Within a screen row, consecutive bytes map to consecutive pixels. However, rows
themselves are not consecutive in memory.
To make it easy to convert a row number from 0 to 191 to a base address, Lode Runner has
a table and a routine to use that table.
\nwenddocs{}\nwbegincode{22}\sublabel{NW1Xx3lK-1W8AJS-5}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-5}}}\moddef{tables~{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-1W8AJS-4}{NW1Xx3lK-1W8AJS-6}\nwenddeflinemarkup
ORG $1A85
\nwlinkedidentc{ROW_TO_OFFSET_LO}{NW1Xx3lK-1W8AJS-5}:
INCLUDE "row_to_offset_lo_table.asm"
\nwlinkedidentc{ROW_TO_OFFSET_HI}{NW1Xx3lK-1W8AJS-5}:
INCLUDE "row_to_offset_hi_table.asm"
\nwindexdefn{\nwixident{ROW{\_}TO{\_}OFFSET{\_}LO}}{ROW:unTO:unOFFSET:unLO}{NW1Xx3lK-1W8AJS-5}\nwindexdefn{\nwixident{ROW{\_}TO{\_}OFFSET{\_}HI}}{ROW:unTO:unOFFSET:unHI}{NW1Xx3lK-1W8AJS-5}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{ROW{\_}TO{\_}OFFSET{\_}HI}}{ROW:unTO:unOFFSET:unHI}}\\{{\nwixident{ROW{\_}TO{\_}OFFSET{\_}LO}}{ROW:unTO:unOFFSET:unLO}}}\nwendcode{}\nwbegindocs{23}\nwdocspar
\nwenddocs{}\nwbegincode{24}\sublabel{NW1Xx3lK-10jlgu-5}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-5}}}\moddef{defines~{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-10jlgu-4}{NW1Xx3lK-10jlgu-6}\nwenddeflinemarkup
\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5} EQU $0C ; 2 bytes
\nwlinkedidentc{ROW_ADDR2}{NW1Xx3lK-10jlgu-5} EQU $0E ; 2 bytes
\nwlinkedidentc{HGR_PAGE}{NW1Xx3lK-10jlgu-5} EQU $1F ; 0x20 for HGR1, 0x40 for HGR2
\nwindexdefn{\nwixident{ROW{\_}ADDR}}{ROW:unADDR}{NW1Xx3lK-10jlgu-5}\nwindexdefn{\nwixident{ROW{\_}ADDR2}}{ROW:unADDR2}{NW1Xx3lK-10jlgu-5}\nwindexdefn{\nwixident{HGR{\_}PAGE}}{HGR:unPAGE}{NW1Xx3lK-10jlgu-5}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{HGR{\_}PAGE}}{HGR:unPAGE}}\\{{\nwixident{ROW{\_}ADDR}}{ROW:unADDR}}\\{{\nwixident{ROW{\_}ADDR2}}{ROW:unADDR2}}}\nwendcode{}\nwbegindocs{25}\nwdocspar
\nwenddocs{}\nwbegincode{26}\sublabel{NW1Xx3lK-8jv1b-3}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-3}}}\moddef{routines~{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-8jv1b-2}{NW1Xx3lK-8jv1b-4}\nwenddeflinemarkup
ORG $7A31
\nwlinkedidentc{ROW_TO_ADDR}{NW1Xx3lK-8jv1b-3}:
SUBROUTINE
; Enter routine with Y set to row. Base address
; (for column 0) will be placed in \nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}.
LDA \nwlinkedidentc{ROW_TO_OFFSET_LO}{NW1Xx3lK-1W8AJS-5},Y
STA \nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}
LDA \nwlinkedidentc{ROW_TO_OFFSET_HI}{NW1Xx3lK-1W8AJS-5},Y
ORA \nwlinkedidentc{HGR_PAGE}{NW1Xx3lK-10jlgu-5}
STA \nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}+1
RTS
\nwindexdefn{\nwixident{ROW{\_}TO{\_}ADDR}}{ROW:unTO:unADDR}{NW1Xx3lK-8jv1b-3}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{ROW{\_}TO{\_}ADDR}}{ROW:unTO:unADDR}}}\nwidentuses{\\{{\nwixident{HGR{\_}PAGE}}{HGR:unPAGE}}\\{{\nwixident{ROW{\_}ADDR}}{ROW:unADDR}}\\{{\nwixident{ROW{\_}TO{\_}OFFSET{\_}HI}}{ROW:unTO:unOFFSET:unHI}}\\{{\nwixident{ROW{\_}TO{\_}OFFSET{\_}LO}}{ROW:unTO:unOFFSET:unLO}}}\nwindexuse{\nwixident{HGR{\_}PAGE}}{HGR:unPAGE}{NW1Xx3lK-8jv1b-3}\nwindexuse{\nwixident{ROW{\_}ADDR}}{ROW:unADDR}{NW1Xx3lK-8jv1b-3}\nwindexuse{\nwixident{ROW{\_}TO{\_}OFFSET{\_}HI}}{ROW:unTO:unOFFSET:unHI}{NW1Xx3lK-8jv1b-3}\nwindexuse{\nwixident{ROW{\_}TO{\_}OFFSET{\_}LO}}{ROW:unTO:unOFFSET:unLO}{NW1Xx3lK-8jv1b-3}\nwendcode{}\nwbegindocs{27}\nwdocspar
There's also a routine to load the address for both page 1 and page 2.
\nwenddocs{}\nwbegincode{28}\sublabel{NW1Xx3lK-8jv1b-4}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-4}}}\moddef{routines~{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-8jv1b-3}{NW1Xx3lK-8jv1b-5}\nwenddeflinemarkup
ORG $7A3E
\nwlinkedidentc{ROW_TO_ADDR_FOR_BOTH_PAGES}{NW1Xx3lK-8jv1b-4}:
SUBROUTINE
; Enter routine with Y set to row. Base address
; (for column 0) will be placed in \nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5} (for page 1)
; and \nwlinkedidentc{ROW_ADDR2}{NW1Xx3lK-10jlgu-5} (for page 2).
LDA \nwlinkedidentc{ROW_TO_OFFSET_LO}{NW1Xx3lK-1W8AJS-5},Y
STA \nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}
STA \nwlinkedidentc{ROW_ADDR2}{NW1Xx3lK-10jlgu-5}
LDA \nwlinkedidentc{ROW_TO_OFFSET_HI}{NW1Xx3lK-1W8AJS-5},Y
ORA #$20
STA \nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}+1
EOR #$60
STA \nwlinkedidentc{ROW_ADDR2}{NW1Xx3lK-10jlgu-5}+1
RTS
\nwindexdefn{\nwixident{ROW{\_}TO{\_}ADDR{\_}FOR{\_}BOTH{\_}PAGES}}{ROW:unTO:unADDR:unFOR:unBOTH:unPAGES}{NW1Xx3lK-8jv1b-4}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{ROW{\_}TO{\_}ADDR{\_}FOR{\_}BOTH{\_}PAGES}}{ROW:unTO:unADDR:unFOR:unBOTH:unPAGES}}}\nwidentuses{\\{{\nwixident{ROW{\_}ADDR}}{ROW:unADDR}}\\{{\nwixident{ROW{\_}ADDR2}}{ROW:unADDR2}}\\{{\nwixident{ROW{\_}TO{\_}OFFSET{\_}HI}}{ROW:unTO:unOFFSET:unHI}}\\{{\nwixident{ROW{\_}TO{\_}OFFSET{\_}LO}}{ROW:unTO:unOFFSET:unLO}}}\nwindexuse{\nwixident{ROW{\_}ADDR}}{ROW:unADDR}{NW1Xx3lK-8jv1b-4}\nwindexuse{\nwixident{ROW{\_}ADDR2}}{ROW:unADDR2}{NW1Xx3lK-8jv1b-4}\nwindexuse{\nwixident{ROW{\_}TO{\_}OFFSET{\_}HI}}{ROW:unTO:unOFFSET:unHI}{NW1Xx3lK-8jv1b-4}\nwindexuse{\nwixident{ROW{\_}TO{\_}OFFSET{\_}LO}}{ROW:unTO:unOFFSET:unLO}{NW1Xx3lK-8jv1b-4}\nwendcode{}\nwbegindocs{29}\nwdocspar
Lode Runner's screens are organized into 28 sprites across by 17 sprites
down. To convert between sprite coordinates and screen coordinates and vice-versa, we
use tables and lookup routines. Each sprite is 10 pixels across by 11 pixels down.
Note that the last row is used for the status, so actually the game screen is 16 sprites vertically.
\nwenddocs{}\nwbegincode{30}\sublabel{NW1Xx3lK-10jlgu-6}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-6}}}\moddef{defines~{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-10jlgu-5}{NW1Xx3lK-10jlgu-7}\nwenddeflinemarkup
MAX_GAME_COL EQU #27 ; 0x1B
MAX_GAME_ROW EQU #15 ; 0x0F
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwendcode{}\nwbegindocs{31}\nwdocspar
\nwenddocs{}\nwbegincode{32}\sublabel{NW1Xx3lK-1W8AJS-6}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-6}}}\moddef{tables~{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-1W8AJS-5}{NW1Xx3lK-1W8AJS-7}\nwenddeflinemarkup
ORG $1C35
\nwlinkedidentc{HALF_SCREEN_COL_TABLE}{NW1Xx3lK-1W8AJS-6}:
; 28 cols of 5 double-pixels each
HEX 00 05 0a 0f 14 19 1e 23 28 2d 32 37 3c 41 46 4b
HEX 50 55 5a 5f 64 69 6e 73 78 7d 82 87
\nwlinkedidentc{SCREEN_ROW_TABLE}{NW1Xx3lK-1W8AJS-6}:
; 17 rows of 11 pixels each
HEX 00 0B 16 21 2C 37 42 4D 58 63 6E 79 84 8F 9A A5
HEX B5
\nwlinkedidentc{COL_BYTE_TABLE}{NW1Xx3lK-1W8AJS-6}:
; Byte number
HEX 00 01 02 04 05 07 08 0A 0B 0C 0E 0F 11 12 14 15
HEX 16 18 19 1B 1C 1E 1F 20 22 23 25 26
\nwlinkedidentc{COL_SHIFT_TABLE}{NW1Xx3lK-1W8AJS-6}:
; Right shift amount
HEX 00 03 06 02 05 01 04 00 03 06 02 05 01 04 00 03
HEX 06 02 05 01 04 00 03 06 02 05 01 04
\nwlinkedidentc{HALF_SCREEN_COL_BYTE_TABLE}{NW1Xx3lK-1W8AJS-6}:
HEX 00 00 00 00 01 01 01 02 02 02 02 03 03 03 04 04
HEX 04 04 05 05 05 06 06 06 06 07 07 07 08 08 08 08
HEX 09 09 09 0A 0A 0A 0A 0B 0B 0B 0C 0C 0C 0C 0D 0D
HEX 0D 0E 0E 0E 0E 0F 0F 0F 10 10 10 10 11 11 11 12
HEX 12 12 12 13 13 13 14 14 14 14 15 15 15 16 16 16
HEX 16 17 17 17 18 18 18 18 19 19 19 1A 1A 1A 1A 1B
HEX 1B 1B 1C 1C 1C 1C 1D 1D 1D 1E 1E 1E 1E 1F 1F 1F
HEX 20 20 20 20 21 21 21 22 22 22 22 23 23 23 24 24
HEX 24 24 25 25 25 26 26 26 26 27 27 27
\nwlinkedidentc{HALF_SCREEN_COL_SHIFT_TABLE}{NW1Xx3lK-1W8AJS-6}:
HEX 00 02 04 06 01 03 05 00 02 04 06 01 03 05 00 02
HEX 04 06 01 03 05 00 02 04 06 01 03 05 00 02 04 06
HEX 01 03 05 00 02 04 06 01 03 05 00 02 04 06 01 03
HEX 05 00 02 04 06 01 03 05 00 02 04 06 01 03 05 00
HEX 02 04 06 01 03 05 00 02 04 06 01 03 05 00 02 04
HEX 06 01 03 05 00 02 04 06 01 03 05 00 02 04 06 01
HEX 03 05 00 02 04 06 01 03 05 00 02 04 06 01 03 05
HEX 00 02 04 06 01 03 05 00 02 04 06 01 03 05 00 02
HEX 04 06 01 03 05 00 02 04 06 01 03 05
\nwindexdefn{\nwixident{SCREEN{\_}ROW{\_}TABLE}}{SCREEN:unROW:unTABLE}{NW1Xx3lK-1W8AJS-6}\nwindexdefn{\nwixident{COL{\_}BYTE{\_}TABLE}}{COL:unBYTE:unTABLE}{NW1Xx3lK-1W8AJS-6}\nwindexdefn{\nwixident{HALF{\_}SCREEN{\_}COL{\_}TABLE}}{HALF:unSCREEN:unCOL:unTABLE}{NW1Xx3lK-1W8AJS-6}\nwindexdefn{\nwixident{COL{\_}SHIFT{\_}TABLE}}{COL:unSHIFT:unTABLE}{NW1Xx3lK-1W8AJS-6}\nwindexdefn{\nwixident{HALF{\_}SCREEN{\_}COL{\_}BYTE{\_}TABLE}}{HALF:unSCREEN:unCOL:unBYTE:unTABLE}{NW1Xx3lK-1W8AJS-6}\nwindexdefn{\nwixident{HALF{\_}SCREEN{\_}COL{\_}SHIFT{\_}TABLE}}{HALF:unSCREEN:unCOL:unSHIFT:unTABLE}{NW1Xx3lK-1W8AJS-6}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{COL{\_}BYTE{\_}TABLE}}{COL:unBYTE:unTABLE}}\\{{\nwixident{COL{\_}SHIFT{\_}TABLE}}{COL:unSHIFT:unTABLE}}\\{{\nwixident{HALF{\_}SCREEN{\_}COL{\_}BYTE{\_}TABLE}}{HALF:unSCREEN:unCOL:unBYTE:unTABLE}}\\{{\nwixident{HALF{\_}SCREEN{\_}COL{\_}SHIFT{\_}TABLE}}{HALF:unSCREEN:unCOL:unSHIFT:unTABLE}}\\{{\nwixident{HALF{\_}SCREEN{\_}COL{\_}TABLE}}{HALF:unSCREEN:unCOL:unTABLE}}\\{{\nwixident{SCREEN{\_}ROW{\_}TABLE}}{SCREEN:unROW:unTABLE}}}\nwendcode{}\nwbegindocs{33}\nwdocspar
Here is the routine to return the screen coordinates for the given sprite coordinates.
The reason that {\Tt{}\nwlinkedidentq{GET{\_}SCREEN{\_}COORDS{\_}FOR}{NW1Xx3lK-8jv1b-5}\nwendquote} returns half the screen column coordinate
is that otherwise the screen column coordinate wouldn't fit in a register.
\nwenddocs{}\nwbegincode{34}\sublabel{NW1Xx3lK-8jv1b-5}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-5}}}\moddef{routines~{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-8jv1b-4}{NW1Xx3lK-8jv1b-6}\nwenddeflinemarkup
ORG $885D
\nwlinkedidentc{GET_SCREEN_COORDS_FOR}{NW1Xx3lK-8jv1b-5}:
SUBROUTINE
; Enter routine with Y set to sprite row (0-16) and
; X set to sprite column (0-27). On return, Y will be set to
; screen row, and X is set to half screen column.
LDA \nwlinkedidentc{SCREEN_ROW_TABLE}{NW1Xx3lK-1W8AJS-6},Y
PHA
LDA \nwlinkedidentc{HALF_SCREEN_COL_TABLE}{NW1Xx3lK-1W8AJS-6},X
TAX ; X = \nwlinkedidentc{HALF_SCREEN_COL_TABLE}{NW1Xx3lK-1W8AJS-6}[X]
PLA
TAY ; Y = \nwlinkedidentc{SCREEN_ROW_TABLE}{NW1Xx3lK-1W8AJS-6}[Y]
RTS
\nwindexdefn{\nwixident{GET{\_}SCREEN{\_}COORDS{\_}FOR}}{GET:unSCREEN:unCOORDS:unFOR}{NW1Xx3lK-8jv1b-5}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{GET{\_}SCREEN{\_}COORDS{\_}FOR}}{GET:unSCREEN:unCOORDS:unFOR}}}\nwidentuses{\\{{\nwixident{HALF{\_}SCREEN{\_}COL{\_}TABLE}}{HALF:unSCREEN:unCOL:unTABLE}}\\{{\nwixident{SCREEN{\_}ROW{\_}TABLE}}{SCREEN:unROW:unTABLE}}}\nwindexuse{\nwixident{HALF{\_}SCREEN{\_}COL{\_}TABLE}}{HALF:unSCREEN:unCOL:unTABLE}{NW1Xx3lK-8jv1b-5}\nwindexuse{\nwixident{SCREEN{\_}ROW{\_}TABLE}}{SCREEN:unROW:unTABLE}{NW1Xx3lK-8jv1b-5}\nwendcode{}\nwbegindocs{35}\nwdocspar
This routine takes a sprite column and converts it to
the memory-mapped byte offset and right-shift amount.
\nwenddocs{}\nwbegincode{36}\sublabel{NW1Xx3lK-8jv1b-6}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-6}}}\moddef{routines~{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-8jv1b-5}{NW1Xx3lK-8jv1b-7}\nwenddeflinemarkup
ORG $8868
\nwlinkedidentc{GET_BYTE_AND_SHIFT_FOR_COL}{NW1Xx3lK-8jv1b-6}:
SUBROUTINE
; Enter routine with X set to sprite column. On
; return, A will be set to screen column byte number
; and X will be set to an additional right shift amount.
LDA \nwlinkedidentc{COL_BYTE_TABLE}{NW1Xx3lK-1W8AJS-6},X
PHA ; A = \nwlinkedidentc{COL_BYTE_TABLE}{NW1Xx3lK-1W8AJS-6}[X]
LDA \nwlinkedidentc{COL_SHIFT_TABLE}{NW1Xx3lK-1W8AJS-6},X
TAX ; X = \nwlinkedidentc{COL_SHIFT_TABLE}{NW1Xx3lK-1W8AJS-6}[X]
PLA
RTS
\nwindexdefn{\nwixident{GET{\_}BYTE{\_}AND{\_}SHIFT{\_}FOR{\_}COL}}{GET:unBYTE:unAND:unSHIFT:unFOR:unCOL}{NW1Xx3lK-8jv1b-6}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{GET{\_}BYTE{\_}AND{\_}SHIFT{\_}FOR{\_}COL}}{GET:unBYTE:unAND:unSHIFT:unFOR:unCOL}}}\nwidentuses{\\{{\nwixident{COL{\_}BYTE{\_}TABLE}}{COL:unBYTE:unTABLE}}\\{{\nwixident{COL{\_}SHIFT{\_}TABLE}}{COL:unSHIFT:unTABLE}}}\nwindexuse{\nwixident{COL{\_}BYTE{\_}TABLE}}{COL:unBYTE:unTABLE}{NW1Xx3lK-8jv1b-6}\nwindexuse{\nwixident{COL{\_}SHIFT{\_}TABLE}}{COL:unSHIFT:unTABLE}{NW1Xx3lK-8jv1b-6}\nwendcode{}\nwbegindocs{37}\nwdocspar
This routine takes half the screen column coordinate and converts it to
the memory-mapped byte offset and right-shift amount.
\nwenddocs{}\nwbegincode{38}\sublabel{NW1Xx3lK-8jv1b-7}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-7}}}\moddef{routines~{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-8jv1b-6}{NW1Xx3lK-8jv1b-8}\nwenddeflinemarkup
ORG $8872
\nwlinkedidentc{GET_BYTE_AND_SHIFT_FOR_HALF_SCREEN_COL}{NW1Xx3lK-8jv1b-7}:
SUBROUTINE
; Enter routine with X set to half screen column. On
; return, A will be set to screen column byte number
; and X will be set to an additional right shift amount.
LDA \nwlinkedidentc{HALF_SCREEN_COL_BYTE_TABLE}{NW1Xx3lK-1W8AJS-6},X
PHA ; A = \nwlinkedidentc{HALF_SCREEN_COL_BYTE_TABLE}{NW1Xx3lK-1W8AJS-6}[X]
LDA \nwlinkedidentc{HALF_SCREEN_COL_SHIFT_TABLE}{NW1Xx3lK-1W8AJS-6},X
TAX ; X = \nwlinkedidentc{HALF_SCREEN_COL_SHIFT_TABLE}{NW1Xx3lK-1W8AJS-6}[X]
PLA
RTS
\nwindexdefn{\nwixident{GET{\_}BYTE{\_}AND{\_}SHIFT{\_}FOR{\_}HALF{\_}SCREEN{\_}COL}}{GET:unBYTE:unAND:unSHIFT:unFOR:unHALF:unSCREEN:unCOL}{NW1Xx3lK-8jv1b-7}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{GET{\_}BYTE{\_}AND{\_}SHIFT{\_}FOR{\_}HALF{\_}SCREEN{\_}COL}}{GET:unBYTE:unAND:unSHIFT:unFOR:unHALF:unSCREEN:unCOL}}}\nwidentuses{\\{{\nwixident{HALF{\_}SCREEN{\_}COL{\_}BYTE{\_}TABLE}}{HALF:unSCREEN:unCOL:unBYTE:unTABLE}}\\{{\nwixident{HALF{\_}SCREEN{\_}COL{\_}SHIFT{\_}TABLE}}{HALF:unSCREEN:unCOL:unSHIFT:unTABLE}}}\nwindexuse{\nwixident{HALF{\_}SCREEN{\_}COL{\_}BYTE{\_}TABLE}}{HALF:unSCREEN:unCOL:unBYTE:unTABLE}{NW1Xx3lK-8jv1b-7}\nwindexuse{\nwixident{HALF{\_}SCREEN{\_}COL{\_}SHIFT{\_}TABLE}}{HALF:unSCREEN:unCOL:unSHIFT:unTABLE}{NW1Xx3lK-8jv1b-7}\nwendcode{}\nwbegindocs{39}\nwdocspar
We also have some utility routines that let us take a sprite row or column and
get its screen row or half column, but offset in either row or column by anywhere from
{\Tt{}-2\nwendquote} to {\Tt{}+2\nwendquote}.
\nwenddocs{}\nwbegincode{40}\sublabel{NW1Xx3lK-1W8AJS-7}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-7}}}\moddef{tables~{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-1W8AJS-6}{NW1Xx3lK-1W8AJS-8}\nwenddeflinemarkup
ORG $888A
\nwlinkedidentc{ROW_OFFSET_TABLE}{NW1Xx3lK-1W8AJS-7}:
HEX FB FD 00 02 04
\nwindexdefn{\nwixident{ROW{\_}OFFSET{\_}TABLE}}{ROW:unOFFSET:unTABLE}{NW1Xx3lK-1W8AJS-7}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{ROW{\_}OFFSET{\_}TABLE}}{ROW:unOFFSET:unTABLE}}}\nwendcode{}\nwbegindocs{41}\nwdocspar
\nwenddocs{}\nwbegincode{42}\sublabel{NW1Xx3lK-8jv1b-8}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-8}}}\moddef{routines~{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-8jv1b-7}{NW1Xx3lK-8jv1b-9}\nwenddeflinemarkup
ORG $887C
\nwlinkedidentc{GET_SCREEN_ROW_OFFSET_IN_X_FOR}{NW1Xx3lK-8jv1b-8}:
SUBROUTINE
; Enter routine with X set to offset+2 (in double-pixels) and
; Y set to sprite row. On return, X will retain its value and
; Y will be set to the screen row.
TXA
PHA
JSR \nwlinkedidentc{GET_SCREEN_COORDS_FOR}{NW1Xx3lK-8jv1b-5}
PLA
TAX ; Restore X
TYA
CLC
ADC \nwlinkedidentc{ROW_OFFSET_TABLE}{NW1Xx3lK-1W8AJS-7},X
TAY
RTS
\nwindexdefn{\nwixident{GET{\_}SCREEN{\_}ROW{\_}OFFSET{\_}IN{\_}X{\_}FOR}}{GET:unSCREEN:unROW:unOFFSET:unIN:unX:unFOR}{NW1Xx3lK-8jv1b-8}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{GET{\_}SCREEN{\_}ROW{\_}OFFSET{\_}IN{\_}X{\_}FOR}}{GET:unSCREEN:unROW:unOFFSET:unIN:unX:unFOR}}}\nwidentuses{\\{{\nwixident{GET{\_}SCREEN{\_}COORDS{\_}FOR}}{GET:unSCREEN:unCOORDS:unFOR}}\\{{\nwixident{ROW{\_}OFFSET{\_}TABLE}}{ROW:unOFFSET:unTABLE}}}\nwindexuse{\nwixident{GET{\_}SCREEN{\_}COORDS{\_}FOR}}{GET:unSCREEN:unCOORDS:unFOR}{NW1Xx3lK-8jv1b-8}\nwindexuse{\nwixident{ROW{\_}OFFSET{\_}TABLE}}{ROW:unOFFSET:unTABLE}{NW1Xx3lK-8jv1b-8}\nwendcode{}\nwbegindocs{43}\nwdocspar
\nwenddocs{}\nwbegincode{44}\sublabel{NW1Xx3lK-1W8AJS-8}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-8}}}\moddef{tables~{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-1W8AJS-7}{NW1Xx3lK-1W8AJS-9}\nwenddeflinemarkup
ORG $889D
\nwlinkedidentc{COL_OFFSET_TABLE}{NW1Xx3lK-1W8AJS-8}:
HEX FE FF 00 01 02
\nwindexdefn{\nwixident{COL{\_}OFFSET{\_}TABLE}}{COL:unOFFSET:unTABLE}{NW1Xx3lK-1W8AJS-8}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{COL{\_}OFFSET{\_}TABLE}}{COL:unOFFSET:unTABLE}}}\nwendcode{}\nwbegindocs{45}\nwdocspar
\nwenddocs{}\nwbegincode{46}\sublabel{NW1Xx3lK-8jv1b-9}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-9}}}\moddef{routines~{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-8jv1b-8}{NW1Xx3lK-8jv1b-A}\nwenddeflinemarkup
ORG $888F
\nwlinkedidentc{GET_HALF_SCREEN_COL_OFFSET_IN_Y_FOR}{NW1Xx3lK-8jv1b-9}:
SUBROUTINE
; Enter routine with Y set to offset+2 (in double-pixels) and
; X set to sprite column. On return, Y will retain its value and
; X will be set to the half screen column.
TYA
PHA
JSR \nwlinkedidentc{GET_SCREEN_COORDS_FOR}{NW1Xx3lK-8jv1b-5}
PLA
TAY ; Restore Y
TXA
CLC
ADC \nwlinkedidentc{COL_OFFSET_TABLE}{NW1Xx3lK-1W8AJS-8},Y
TAX
RTS
\nwindexdefn{\nwixident{GET{\_}HALF{\_}SCREEN{\_}COL{\_}OFFSET{\_}IN{\_}Y{\_}FOR}}{GET:unHALF:unSCREEN:unCOL:unOFFSET:unIN:unY:unFOR}{NW1Xx3lK-8jv1b-9}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{GET{\_}HALF{\_}SCREEN{\_}COL{\_}OFFSET{\_}IN{\_}Y{\_}FOR}}{GET:unHALF:unSCREEN:unCOL:unOFFSET:unIN:unY:unFOR}}}\nwidentuses{\\{{\nwixident{COL{\_}OFFSET{\_}TABLE}}{COL:unOFFSET:unTABLE}}\\{{\nwixident{GET{\_}SCREEN{\_}COORDS{\_}FOR}}{GET:unSCREEN:unCOORDS:unFOR}}}\nwindexuse{\nwixident{COL{\_}OFFSET{\_}TABLE}}{COL:unOFFSET:unTABLE}{NW1Xx3lK-8jv1b-9}\nwindexuse{\nwixident{GET{\_}SCREEN{\_}COORDS{\_}FOR}}{GET:unSCREEN:unCOORDS:unFOR}{NW1Xx3lK-8jv1b-9}\nwendcode{}\nwbegindocs{47}\nwdocspar
Now we can finally write the routines that draw a sprite on the screen. We have one
routine that draws a sprite at a given game row and game column.
There are two entry points, one to draw on HGR1, and one for HGR2.
\nwenddocs{}\nwbegincode{48}\sublabel{NW1Xx3lK-10jlgu-7}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-7}}}\moddef{defines~{\nwtagstyle{}\subpageref{NW1Xx3lK-10jlgu-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-10jlgu-6}{NW1Xx3lK-10jlgu-8}\nwenddeflinemarkup
\nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7} EQU $1B
\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7} EQU $1C
\nwlinkedidentc{MASK0}{NW1Xx3lK-10jlgu-7} EQU $50
\nwlinkedidentc{MASK1}{NW1Xx3lK-10jlgu-7} EQU $51
\nwlinkedidentc{COL_SHIFT_AMT}{NW1Xx3lK-10jlgu-7} EQU $71
\nwlinkedidentc{GAME_COLNUM}{NW1Xx3lK-10jlgu-7} EQU $85
\nwlinkedidentc{GAME_ROWNUM}{NW1Xx3lK-10jlgu-7} EQU $86
\nwindexdefn{\nwixident{ROWNUM}}{ROWNUM}{NW1Xx3lK-10jlgu-7}\nwindexdefn{\nwixident{COLNUM}}{COLNUM}{NW1Xx3lK-10jlgu-7}\nwindexdefn{\nwixident{MASK0}}{MASK0}{NW1Xx3lK-10jlgu-7}\nwindexdefn{\nwixident{MASK1}}{MASK1}{NW1Xx3lK-10jlgu-7}\nwindexdefn{\nwixident{COL{\_}SHIFT{\_}AMT}}{COL:unSHIFT:unAMT}{NW1Xx3lK-10jlgu-7}\nwindexdefn{\nwixident{GAME{\_}COLNUM}}{GAME:unCOLNUM}{NW1Xx3lK-10jlgu-7}\nwindexdefn{\nwixident{GAME{\_}ROWNUM}}{GAME:unROWNUM}{NW1Xx3lK-10jlgu-7}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{COL{\_}SHIFT{\_}AMT}}{COL:unSHIFT:unAMT}}\\{{\nwixident{COLNUM}}{COLNUM}}\\{{\nwixident{GAME{\_}COLNUM}}{GAME:unCOLNUM}}\\{{\nwixident{GAME{\_}ROWNUM}}{GAME:unROWNUM}}\\{{\nwixident{MASK0}}{MASK0}}\\{{\nwixident{MASK1}}{MASK1}}\\{{\nwixident{ROWNUM}}{ROWNUM}}}\nwendcode{}\nwbegindocs{49}\nwdocspar
\nwenddocs{}\nwbegincode{50}\sublabel{NW1Xx3lK-1W8AJS-9}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-9}}}\moddef{tables~{\nwtagstyle{}\subpageref{NW1Xx3lK-1W8AJS-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-1W8AJS-8}{NW1Xx3lK-1W8AJS-A}\nwenddeflinemarkup
ORG $8328
\nwlinkedidentc{PIXEL_MASK0}{NW1Xx3lK-1W8AJS-9}:
BYTE %00000000
BYTE %00000001
BYTE %00000011
BYTE %00000111
BYTE %00001111
BYTE %00011111
BYTE %00111111
\nwlinkedidentc{PIXEL_MASK1}{NW1Xx3lK-1W8AJS-9}:
BYTE %11111000
BYTE %11110000
BYTE %11100000
BYTE %11000000
BYTE %10000000
BYTE %11111110
BYTE %11111100
\nwindexdefn{\nwixident{PIXEL{\_}MASK0}}{PIXEL:unMASK0}{NW1Xx3lK-1W8AJS-9}\nwindexdefn{\nwixident{PIXEL{\_}MASK1}}{PIXEL:unMASK1}{NW1Xx3lK-1W8AJS-9}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{PIXEL{\_}MASK0}}{PIXEL:unMASK0}}\\{{\nwixident{PIXEL{\_}MASK1}}{PIXEL:unMASK1}}}\nwendcode{}\nwbegindocs{51}\nwdocspar
\nwenddocs{}\nwbegincode{52}\sublabel{NW1Xx3lK-8jv1b-A}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-A}}}\moddef{routines~{\nwtagstyle{}\subpageref{NW1Xx3lK-8jv1b-1}}}\plusendmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-1p0Y9w-1}}\nwprevnextdefs{NW1Xx3lK-8jv1b-9}{NW1Xx3lK-8jv1b-B}\nwenddeflinemarkup
ORG $82AA
\nwlinkedidentc{DRAW_SPRITE_PAGE1}{NW1Xx3lK-8jv1b-A}:
SUBROUTINE
; Enter routine with A set to sprite number to draw,
; \nwlinkedidentc{GAME_ROWNUM}{NW1Xx3lK-10jlgu-7} set to the row to draw it at, and \nwlinkedidentc{GAME_COLNUM}{NW1Xx3lK-10jlgu-7}
; set to the column to draw it at.
STA \nwlinkedidentc{SPRITE_NUM}{NW1Xx3lK-10jlgu-4}
LDA #$20 ; Page number for HGR1
BNE DRAW_SPRITE ; Actually unconditional jump
\nwlinkedidentc{DRAW_SPRITE_PAGE2}{NW1Xx3lK-8jv1b-A}:
SUBROUTINE
; Enter routine with A set to sprite number to draw,
; \nwlinkedidentc{GAME_ROWNUM}{NW1Xx3lK-10jlgu-7} set to the row to draw it at, and \nwlinkedidentc{GAME_COLNUM}{NW1Xx3lK-10jlgu-7}
; set to the column to draw it at.
STA \nwlinkedidentc{SPRITE_NUM}{NW1Xx3lK-10jlgu-4}
LDA #$40 ; Page number for HGR2
; fallthrough
DRAW_SPRITE:
STA \nwlinkedidentc{HGR_PAGE}{NW1Xx3lK-10jlgu-5}
LDY \nwlinkedidentc{GAME_ROWNUM}{NW1Xx3lK-10jlgu-7}
JSR \nwlinkedidentc{GET_SCREEN_COORDS_FOR}{NW1Xx3lK-8jv1b-5}
STY \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7} ; \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7} = \nwlinkedidentc{SCREEN_ROW_TABLE}{NW1Xx3lK-1W8AJS-6}[\nwlinkedidentc{GAME_ROWNUM}{NW1Xx3lK-10jlgu-7}]
LDX \nwlinkedidentc{GAME_COLNUM}{NW1Xx3lK-10jlgu-7}
JSR \nwlinkedidentc{GET_BYTE_AND_SHIFT_FOR_COL}{NW1Xx3lK-8jv1b-6}
STA \nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7} ; \nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7} = \nwlinkedidentc{COL_BYTE_TABLE}{NW1Xx3lK-1W8AJS-6}[\nwlinkedidentc{GAME_COLNUM}{NW1Xx3lK-10jlgu-7}]
STX \nwlinkedidentc{COL_SHIFT_AMT}{NW1Xx3lK-10jlgu-7} ; \nwlinkedidentc{COL_SHIFT_AMT}{NW1Xx3lK-10jlgu-7} = \nwlinkedidentc{COL_SHIFT_TABLE}{NW1Xx3lK-1W8AJS-6}[\nwlinkedidentc{GAME_COLNUM}{NW1Xx3lK-10jlgu-7}]
LDA \nwlinkedidentc{PIXEL_MASK0}{NW1Xx3lK-1W8AJS-9},X
STA \nwlinkedidentc{MASK0}{NW1Xx3lK-10jlgu-7} ; \nwlinkedidentc{MASK0}{NW1Xx3lK-10jlgu-7} = \nwlinkedidentc{PIXEL_MASK0}{NW1Xx3lK-1W8AJS-9}[\nwlinkedidentc{COL_SHIFT_AMT}{NW1Xx3lK-10jlgu-7}]
LDA \nwlinkedidentc{PIXEL_MASK1}{NW1Xx3lK-1W8AJS-9},X
STA \nwlinkedidentc{MASK1}{NW1Xx3lK-10jlgu-7} ; \nwlinkedidentc{MASK1}{NW1Xx3lK-10jlgu-7} = \nwlinkedidentc{PIXEL_MASK1}{NW1Xx3lK-1W8AJS-9}[\nwlinkedidentc{COL_SHIFT_AMT}{NW1Xx3lK-10jlgu-7}]
JSR \nwlinkedidentc{COMPUTE_SHIFTED_SPRITE}{NW1Xx3lK-8jv1b-2}
LDA #$0B
STA \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4}
LDX #$00
LDA \nwlinkedidentc{COL_SHIFT_AMT}{NW1Xx3lK-10jlgu-7}
CMP #$05
BCS .need_3_bytes ; If \nwlinkedidentc{COL_SHIFT_AMT}{NW1Xx3lK-10jlgu-7} >= 5, we need to alter three
; screen bytes, otherwise just two bytes.
.loop1:
LDY \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7}
JSR \nwlinkedidentc{ROW_TO_ADDR}{NW1Xx3lK-8jv1b-3}
LDY \nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}
LDA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y
AND \nwlinkedidentc{MASK0}{NW1Xx3lK-10jlgu-7}
ORA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X
STA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y ; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}] =
; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}] & \nwlinkedidentc{MASK0}{NW1Xx3lK-10jlgu-7} | \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}[i]
INX ; X++
INY ; Y++
LDA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y
AND \nwlinkedidentc{MASK1}{NW1Xx3lK-10jlgu-7}
ORA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X
STA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y ; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+1] =
; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+1] & \nwlinkedidentc{MASK1}{NW1Xx3lK-10jlgu-7} | \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}[i+1]
INX
INX ; X += 2
INC \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7} ; \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7}++
DEC \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4} ; \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4}--
BNE .loop1 ; loop while \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4} > 0
RTS
.need_3_bytes
LDY \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7}
JSR \nwlinkedidentc{ROW_TO_ADDR}{NW1Xx3lK-8jv1b-3}
LDY \nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}
LDA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y
AND \nwlinkedidentc{MASK0}{NW1Xx3lK-10jlgu-7}
ORA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X
STA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y ; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}] =
; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}] & \nwlinkedidentc{MASK0}{NW1Xx3lK-10jlgu-7} | \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}[i]
INX ; X++
INY ; Y++
LDA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X
STA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y ; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+1] = \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}[i+1]
INX ; X++
INY ; Y++
LDA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y
AND \nwlinkedidentc{MASK1}{NW1Xx3lK-10jlgu-7}
ORA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X
STA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y ; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+2] =
; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+2] & \nwlinkedidentc{MASK1}{NW1Xx3lK-10jlgu-7} | \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}[i+2]
INX ; X++
INC \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7} ; \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7}++
DEC \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4} ; \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4}--
BNE .need_3_bytes ; loop while \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4} > 0
RTS
\nwindexdefn{\nwixident{DRAW{\_}SPRITE{\_}PAGE1}}{DRAW:unSPRITE:unPAGE1}{NW1Xx3lK-8jv1b-A}\nwindexdefn{\nwixident{DRAW{\_}SPRITE{\_}PAGE2}}{DRAW:unSPRITE:unPAGE2}{NW1Xx3lK-8jv1b-A}\eatline
\nwused{\\{NW1Xx3lK-1p0Y9w-1}}\nwidentdefs{\\{{\nwixident{DRAW{\_}SPRITE{\_}PAGE1}}{DRAW:unSPRITE:unPAGE1}}\\{{\nwixident{DRAW{\_}SPRITE{\_}PAGE2}}{DRAW:unSPRITE:unPAGE2}}}\nwidentuses{\\{{\nwixident{BLOCK{\_}DATA}}{BLOCK:unDATA}}\\{{\nwixident{COL{\_}BYTE{\_}TABLE}}{COL:unBYTE:unTABLE}}\\{{\nwixident{COL{\_}SHIFT{\_}AMT}}{COL:unSHIFT:unAMT}}\\{{\nwixident{COL{\_}SHIFT{\_}TABLE}}{COL:unSHIFT:unTABLE}}\\{{\nwixident{COLNUM}}{COLNUM}}\\{{\nwixident{COMPUTE{\_}SHIFTED{\_}SPRITE}}{COMPUTE:unSHIFTED:unSPRITE}}\\{{\nwixident{GAME{\_}COLNUM}}{GAME:unCOLNUM}}\\{{\nwixident{GAME{\_}ROWNUM}}{GAME:unROWNUM}}\\{{\nwixident{GET{\_}BYTE{\_}AND{\_}SHIFT{\_}FOR{\_}COL}}{GET:unBYTE:unAND:unSHIFT:unFOR:unCOL}}\\{{\nwixident{GET{\_}SCREEN{\_}COORDS{\_}FOR}}{GET:unSCREEN:unCOORDS:unFOR}}\\{{\nwixident{HGR{\_}PAGE}}{HGR:unPAGE}}\\{{\nwixident{MASK0}}{MASK0}}\\{{\nwixident{MASK1}}{MASK1}}\\{{\nwixident{PIXEL{\_}MASK0}}{PIXEL:unMASK0}}\\{{\nwixident{PIXEL{\_}MASK1}}{PIXEL:unMASK1}}\\{{\nwixident{ROW{\_}ADDR}}{ROW:unADDR}}\\{{\nwixident{ROW{\_}COUNT}}{ROW:unCOUNT}}\\{{\nwixident{ROW{\_}TO{\_}ADDR}}{ROW:unTO:unADDR}}\\{{\nwixident{ROWNUM}}{ROWNUM}}\\{{\nwixident{SCREEN{\_}ROW{\_}TABLE}}{SCREEN:unROW:unTABLE}}\\{{\nwixident{SPRITE{\_}NUM}}{SPRITE:unNUM}}}\nwindexuse{\nwixident{BLOCK{\_}DATA}}{BLOCK:unDATA}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{COL{\_}BYTE{\_}TABLE}}{COL:unBYTE:unTABLE}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{COL{\_}SHIFT{\_}AMT}}{COL:unSHIFT:unAMT}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{COL{\_}SHIFT{\_}TABLE}}{COL:unSHIFT:unTABLE}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{COLNUM}}{COLNUM}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{COMPUTE{\_}SHIFTED{\_}SPRITE}}{COMPUTE:unSHIFTED:unSPRITE}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{GAME{\_}COLNUM}}{GAME:unCOLNUM}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{GAME{\_}ROWNUM}}{GAME:unROWNUM}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{GET{\_}BYTE{\_}AND{\_}SHIFT{\_}FOR{\_}COL}}{GET:unBYTE:unAND:unSHIFT:unFOR:unCOL}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{GET{\_}SCREEN{\_}COORDS{\_}FOR}}{GET:unSCREEN:unCOORDS:unFOR}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{HGR{\_}PAGE}}{HGR:unPAGE}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{MASK0}}{MASK0}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{MASK1}}{MASK1}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{PIXEL{\_}MASK0}}{PIXEL:unMASK0}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{PIXEL{\_}MASK1}}{PIXEL:unMASK1}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{ROW{\_}ADDR}}{ROW:unADDR}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{ROW{\_}COUNT}}{ROW:unCOUNT}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{ROW{\_}TO{\_}ADDR}}{ROW:unTO:unADDR}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{ROWNUM}}{ROWNUM}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{SCREEN{\_}ROW{\_}TABLE}}{SCREEN:unROW:unTABLE}{NW1Xx3lK-8jv1b-A}\nwindexuse{\nwixident{SPRITE{\_}NUM}}{SPRITE:unNUM}{NW1Xx3lK-8jv1b-A}\nwendcode{}\nwbegindocs{53}\nwdocspar
There is a different routine which erases a sprite at a given screen coordinate.
It does this by drawing the inverse of the sprite on page 1, then drawing the sprite data
from page 2 (the background page) onto page 1.
Upon entry, the Y register needs to be set to the screen row coordinate (0-191). However, the
X register needs to be set to half the screen column coordinate (0-139) because otherwise
the maximum coordinate (279) wouldn't fit in a register.
\nwenddocs{}\nwbegincode{54}\sublabel{NW1Xx3lK-xFyle-1}\nwmargintag{{\nwtagstyle{}\subpageref{NW1Xx3lK-xFyle-1}}}\moddef{erase sprite at screen coordinate~{\nwtagstyle{}\subpageref{NW1Xx3lK-xFyle-1}}}\endmoddef\nwstartdeflinemarkup\nwusesondefline{\\{NW1Xx3lK-8jv1b-C}}\nwenddeflinemarkup
ORG $8336
\nwlinkedidentc{ERASE_SPRITE_AT_PIXEL_COORDS}{NW1Xx3lK-xFyle-1}:
SUBROUTINE
; Enter routine with A set to sprite number to draw,
; Y set to the screen row to erase it at, and X
; set to *half* the screen column to erase it at.
STY \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7}
STA \nwlinkedidentc{SPRITE_NUM}{NW1Xx3lK-10jlgu-4}
JSR \nwlinkedidentc{GET_BYTE_AND_SHIFT_FOR_HALF_SCREEN_COL}{NW1Xx3lK-8jv1b-7}
STA \nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}
STX \nwlinkedidentc{COL_SHIFT_AMT}{NW1Xx3lK-10jlgu-7}
JSR \nwlinkedidentc{COMPUTE_SHIFTED_SPRITE}{NW1Xx3lK-8jv1b-2}
LDX #$0B
STX \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4}
LDX #$00
LDA \nwlinkedidentc{COL_SHIFT_AMT}{NW1Xx3lK-10jlgu-7}
CMP #$05
BCS .need_3_bytes ; If \nwlinkedidentc{COL_SHIFT_AMT}{NW1Xx3lK-10jlgu-7} >= 5, we need to alter three
; screen bytes, otherwise just two bytes.
.loop1:
LDY \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7}
JSR \nwlinkedidentc{ROW_TO_ADDR_FOR_BOTH_PAGES}{NW1Xx3lK-8jv1b-4}
LDY \nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}
LDA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X
EOR #$7F
AND (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y
ORA (\nwlinkedidentc{ROW_ADDR2}{NW1Xx3lK-10jlgu-5}),Y
STA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y ; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}] =
; (screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}] & (\nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}[i] ^ 0x7F)) |
; screen2[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}]
INX ; X++
INY ; Y++
LDA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X
EOR #$7F
AND (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y
ORA (\nwlinkedidentc{ROW_ADDR2}{NW1Xx3lK-10jlgu-5}),Y
STA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y ; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+1] =
; (screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+1] & (\nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}[i+1] ^ 0x7F)) |
; screen2[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+1]
INX ; X++
INX ; X++
INC \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7}
DEC \nwlinkedidentc{ROW_COUNT}{NW1Xx3lK-10jlgu-4}
BNE .loop1
RTS
.need_3_bytes:
LDY \nwlinkedidentc{ROWNUM}{NW1Xx3lK-10jlgu-7}
JSR \nwlinkedidentc{ROW_TO_ADDR_FOR_BOTH_PAGES}{NW1Xx3lK-8jv1b-4}
LDY \nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}
LDA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X
EOR #$7F
AND (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y
ORA (\nwlinkedidentc{ROW_ADDR2}{NW1Xx3lK-10jlgu-5}),Y
STA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y ; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}] =
; (screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}] & (\nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}[i] ^ 0x7F)) |
; screen2[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}]
INX ; X++
INY ; Y++
LDA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X
EOR #$7F
AND (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y
ORA (\nwlinkedidentc{ROW_ADDR2}{NW1Xx3lK-10jlgu-5}),Y
STA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y ; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+1] =
; (screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+1] & (\nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}[i+1] ^ 0x7F)) |
; screen2[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+1]
INX ; X++
INY ; Y++
LDA \nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3},X
EOR #$7F
AND (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y
ORA (\nwlinkedidentc{ROW_ADDR2}{NW1Xx3lK-10jlgu-5}),Y
STA (\nwlinkedidentc{ROW_ADDR}{NW1Xx3lK-10jlgu-5}),Y ; screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+2] =
; (screen[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+2] & (\nwlinkedidentc{BLOCK_DATA}{NW1Xx3lK-10jlgu-3}[i+2] ^ 0x7F)) |
; screen2[\nwlinkedidentc{COLNUM}{NW1Xx3lK-10jlgu-7}+2]