-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathelite-6502sp-io-processor.asm
4901 lines (3907 loc) · 194 KB
/
elite-6502sp-io-processor.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
\ ******************************************************************************
\
\ ELITE-A MAIN GAME SOURCE (I/O PROCESSOR)
\
\ Elite-A is an extended version of BBC Micro Elite by Angus Duggan
\
\ The original Elite was written by Ian Bell and David Braben and is copyright
\ Acornsoft 1984, and the extra code in Elite-A is copyright Angus Duggan
\
\ The code in this file is identical to Angus Duggan's source discs (it's just
\ been reformatted, and the label names have been changed to be consistent with
\ the sources for the original BBC Micro disc version on which it is based)
\
\ The commentary is copyright Mark Moxon, and any misunderstandings or mistakes
\ in the documentation are entirely my fault
\
\ The terminology and notations used in this commentary are explained at
\ https://elite.bbcelite.com/terminology
\
\ The deep dive articles referred to in this commentary can be found at
\ https://elite.bbcelite.com/deep_dives
\
\ ------------------------------------------------------------------------------
\
\ This source file contains the I/O processor code for Elite-A on the 6502
\ Second Processor. This is the code that runs in the I/O processor (i.e. the
\ BBC Micro).
\
\ ------------------------------------------------------------------------------
\
\ This source file produces the following binary file:
\
\ * 2.H.bin
\
\ ******************************************************************************
INCLUDE "1-source-files/main-sources/elite-build-options.asm"
_RELEASED = (_VARIANT = 1)
_SOURCE_DISC = (_VARIANT = 2)
_BUG_FIX = (_VARIANT = 3)
GUARD &6000 \ Guard against assembling over screen memory
\ ******************************************************************************
\
\ Configuration variables
\
\ ******************************************************************************
CODE% = &1200 \ The address where the code will be run
LOAD% = &1200 \ The address where the code will be loaded
X = 128 \ The centre x-coordinate of the 256 x 192 space view
Y = 96 \ The centre y-coordinate of the 256 x 192 space view
tube_brk = &0016 \ The location of the Tube host code's break handler
BRKV = &0202 \ The break vector that we intercept to enable us to
\ handle and display system errors
WRCHV = &020E \ The WRCHV vector that we intercept with our custom
\ text printing routine
LASCT = &0346 \ The laser pulse count for the current laser, matching
\ the address in the main game code
HFX = &0348 \ A flag that toggles the hyperspace colour effect,
\ matching the address in the main game code
ESCP = &0386 \ The flag that determines whether we have an escape pod
\ fitted, matching the address in the main game code
IF _BUG_FIX
savews = &DD06 \ The address for the savews workspace routine from
\ the loader so we can call it to ensure the MOS
\ character definitions are loaded before printing
\ text on the BBC Master
restorews = &DD65 \ The address for the restorews workspace routine from
\ the loader so we can call it to ensure the MOS
\ character definitions are loaded before printing
\ text on the BBC Master
wsstate = &DDBA \ The address for the wsstate workspace routine from
\ the loader so we can call it to ensure the MOS
\ character definitions are loaded before printing
\ text on the BBC Master
ENDIF
VIA = &FE00 \ Memory-mapped space for accessing internal hardware,
\ such as the video ULA, 6845 CRTC and 6522 VIAs (also
\ known as SHEILA)
tube_r1s = &FEE0 \ The Tube's memory-mapped FIFO 1 status register
tube_r1d = &FEE1 \ The Tube's memory-mapped FIFO 1 data register
tube_r2s = &FEE2 \ The Tube's memory-mapped FIFO 2 status register
tube_r2d = &FEE3 \ The Tube's memory-mapped FIFO 2 data register
tube_r3s = &FEE4 \ The Tube's memory-mapped FIFO 3 status register
tube_r3d = &FEE5 \ The Tube's memory-mapped FIFO 3 data register
tube_r4s = &FEE6 \ The Tube's memory-mapped FIFO 4 status register
tube_r4d = &FEE7 \ The Tube's memory-mapped FIFO 4 data register
rawrch = &FFBC \ The address of the MOS's VDU character output routine
OSBYTE = &FFF4 \ The address for the OSBYTE routine
OSCLI = &FFF7 \ The address for the OSCLI routine
\ ******************************************************************************
\
\ Name: ZP
\ Type: Workspace
\ Address: &008B to &009F
\ Category: Workspaces
\ Summary: Important variables used by the I/O processor
\
\ ******************************************************************************
ORG &008B
.DL
SKIP 1 \ Vertical sync flag
\
\ DL gets set to 30 every time we reach vertical sync on
\ the video system, which happens 50 times a second
\ (50Hz). The WSCAN routine uses this to pause until the
\ vertical sync, by setting DL to 0 and then monitoring
\ its value until it changes to 30
ORG &0090
.key_tube
SKIP 2 \ Contains the address of the I/O processor's keyboard
\ translation table (as opposed to the parasite's
\ table), which is used to translate internal key
\ numbers to ASCII in the I/O processor code
.SC
SKIP 1 \ Screen address (low byte)
\
\ Elite draws on-screen by poking bytes directly into
\ screen memory, and SC(1 0) is typically set to the
\ address of the character block containing the pixel
\ we want to draw
.SCH
SKIP 1 \ Screen address (high byte)
.font
.ZZ
.bar_1
.angle_1
.missle_1
.picture_1
.print_bits
.X1
SKIP 1 \ Temporary storage, typically used for x-coordinates in
\ the line-drawing routines
.bar_2
.picture_2
.Y1
SKIP 1 \ Temporary storage, typically used for y-coordinates in
\ line-drawing routines
.bar_3
.K3
.COL
.X2
SKIP 1 \ Temporary storage, typically used for x-coordinates in
\ the line-drawing routines
.XSAV2
.Y2
SKIP 1 \ Temporary storage, typically used for y-coordinates in
\ line-drawing routines
.YSAV2
.P
SKIP 1 \ Temporary storage, used in a number of places
.T
.Q
SKIP 1 \ Temporary storage, used in a number of places
.R
SKIP 1 \ Temporary storage, used in a number of places
.S
SKIP 1 \ Temporary storage, used in a number of places
.SWAP
SKIP 1 \ Temporary storage, used to store a flag that records
\ whether or not we had to swap a line's start and end
\ coordinates around when clipping the line in routine
\ LL145 (the flag is used in places like BLINE to swap
\ them back)
SKIP 1
.XC
SKIP 1 \ The x-coordinate of the text cursor (i.e. the text
\ column), which can be from 0 to 32
\
\ A value of 0 denotes the leftmost column and 32 the
\ rightmost column, but because the top part of the
\ screen (the space view) has a border box that
\ clashes with columns 0 and 32, text is only shown
\ in columns 1-31
.YC
SKIP 1 \ The y-coordinate of the text cursor (i.e. the text
\ row), which can be from 0 to 23
\
\ The screen actually has 31 character rows if you
\ include the dashboard, but the text printing routines
\ only work on the top part (the space view), so the
\ text cursor only goes up to a maximum of 23, the row
\ just before the screen splits
\
\ A value of 0 denotes the top row, but because the
\ top part of the screen has a border box that clashes
\ with row 0, text is always shown at row 1 or greater
\ ******************************************************************************
\
\ ELITE I/O PROCESSOR
\
\ ******************************************************************************
ORG CODE%
\ ******************************************************************************
\
\ Name: tube_elite
\ Type: Subroutine
\ Category: Tube
\ Summary: Set the vectors to receive Tube communications, run the parasite
\ code, and terminate the I/O processor's loading process
\
\ ******************************************************************************
.tube_elite
LDX #&FF \ Set the stack pointer to &01FF, which is the standard
TXS \ location for the 6502 stack, so this instruction
\ effectively resets the stack
LDA #LO(tube_wrch) \ Set WRCHV to point to the tube_wrch routine, so when
STA WRCHV \ bytes are sent to the I/O processor from the parasite,
LDA #HI(tube_wrch) \ the tube_wrch routine is called to handle them
STA WRCHV+1
LDA #LO(tube_brk) \ Set BRKV to point to the tube_brk routine (i.e. to the
STA BRKV \ Tube host code's break handler)
LDA #HI(tube_brk)
STA BRKV+1
LDX #LO(tube_run) \ Set (Y X) to point to tube_run ("R.2.T")
LDY #HI(tube_run)
JMP OSCLI \ Call OSCLI to run the OS command in tube_run, which
\ *RUNs the parasite code in the 2.T file before
\ returning from the subroutine using a tail call
\ This terminates the I/O processor code, leaving the
\ BBC Micro to sit idle until a command arrives from the
\ parasite and calls tube_wrch via WRCHV
\ ******************************************************************************
\
\ Name: tube_run
\ Type: Variable
\ Category: Tube
\ Summary: The OS command string for running the Tube version's parasite code
\ in file 2.T
\
\ ******************************************************************************
.tube_run
EQUS "R.2.T" \ This is short for "*RUN 2.T"
EQUB 13
\ ******************************************************************************
\
\ Name: tube_get
\ Type: Subroutine
\ Category: Tube
\ Summary: As the I/O processor, fetch a byte that's been sent over the Tube
\ from the parasite
\ Deep dive: Tube communication in Elite-A
\
\ ------------------------------------------------------------------------------
\
\ Tube communication in Elite-A uses the following protocol:
\
\ Parasite -> I/O processor
\
\ * Uses the FIFO 1 status and data registers to transmit the data
\ * The parasite calls tube_write to send a byte to the I/O processor
\ * The I/O processor calls tube_get to receive that byte from the parasite
\
\ I/O processor -> Parasite
\
\ * Uses the FIFO 2 status and data registers to transmit the data
\ * The I/O processor calls tube_put to send a byte to the parasite
\ * The parasite calls tube_read to receive that byte from the I/O processor
\
\ This routine is called by the I/O processor to receive a byte from the
\ parasite.
\
\ The code is identical to Acorn's MOS routine that runs on the parasite to
\ implement OSWRCH across the Tube.
\
\ ******************************************************************************
.tube_get
BIT tube_r1s \ Check whether FIFO 1 has received a byte from the
\ parasite (which it will have sent by calling its own
\ tube_write routine). We do this by checking bit 7 of
\ the FIFO 1 status register
NOP \ Pause while the register is checked
BPL tube_get \ If FIFO 1 has received a byte then bit 7 of the status
\ register will be set, so this loops back to tube_get
\ until FIFO 1 contains the byte transmitted from the
\ parasite
LDA tube_r1d \ Fetch the transmitted byte by reading the FIFO 1 data
\ register into A
RTS \ Return from the subroutine
\ ******************************************************************************
\
\ Name: tube_put
\ Type: Subroutine
\ Category: Tube
\ Summary: As the I/O processor, send a byte across the Tube to the parasite
\ Deep dive: Tube communication in Elite-A
\
\ ------------------------------------------------------------------------------
\
\ Tube communication in Elite-A uses the following protocol:
\
\ Parasite -> I/O processor
\
\ * Uses the FIFO 1 status and data registers to transmit the data
\ * The parasite calls tube_write to send a byte to the I/O processor
\ * The I/O processor calls tube_get to receive that byte from the parasite
\
\ I/O processor -> Parasite
\
\ * Uses the FIFO 2 status and data registers to transmit the data
\ * The I/O processor calls tube_put to send a byte to the parasite
\ * The parasite calls tube_read to receive that byte from the I/O processor
\
\ This routine is called by the I/O processor to send a byte to the parasite.
\
\ The code is identical to Acorn's MOS routine that runs on the parasite to
\ implement OSWRCH across the Tube (except this uses FIFO 2 instead of FIFO 1).
\
\ ******************************************************************************
.tube_put
BIT tube_r2s \ Check whether FIFO 2 is available for use, so we can
\ use it to transmit a byte to the I/O processor. We do
\ this by checking bit 6 of the FIFO 2 status register
NOP \ Pause while the register is checked
BVC tube_put \ If FIFO 2 is available for use then bit 6 of the
\ status register will be set, so this loops back to
\ tube_put until FIFO 2 is available for us to use
STA tube_r2d \ FIFO 2 is available for use, so store the value we
\ want to transmit in the FIFO 2 data register, so it
\ gets sent to the parasite
RTS \ Return from the subroutine
\ ******************************************************************************
\
\ Name: tube_func
\ Type: Subroutine
\ Category: Tube
\ Summary: Call the corresponding routine for a Tube command
\ Deep dive: Tube communication in Elite-A
\
\ ------------------------------------------------------------------------------
\
\ This routine calls the routine given in the tube_table lookup table for the
\ Tube command specified in A.
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ A The command number (&80-&FF)
\
\ ******************************************************************************
.tube_func
CMP #&9D \ If A >= &9D then there isn't a corresponding command,
BCS return \ so jump to return to return from the subroutine
ASL A \ Set Y = A * 2, so we can use it as an index into the
TAY \ lookup table, which has two bytes per entry
\
\ Note that this also shifts bit 7 off the end, so the
\ result is actually ((A - 128) * 2), which means if A
\ starts out at &80, then Y = 0, if A is &81, Y = 2,
\ and so on
LDA tube_table,Y \ Copy the Y-th address from tube_table over the &FFFF
STA tube_jump+1 \ address of the JMP instruction below, so this modifies
LDA tube_table+1,Y \ the instruction so that it jumps to the corresponding
STA tube_jump+2 \ address from the lookup table
.tube_jump
JMP &FFFF \ Jump to the routine whose address we just copied from
\ the tube_table, which will be the routine that
\ corresponds to this Tube command, and return from the
\ subroutine using a tail call
.return
RTS \ Return from the subroutine
\ ******************************************************************************
\
\ Name: tube_table
\ Type: Variable
\ Category: Tube
\ Summary: Lookup table for Tube commands sent from the parasite to the I/O
\ processor
\ Deep dive: Tube communication in Elite-A
\
\ ------------------------------------------------------------------------------
\
\ This table lists all the commands that can be sent from the parasite to the
\ I/O processor.
\
\ The hexadecimal number is the number of that command, and is the first byte to
\ be sent over the Tube. The parameters shown in brackets are sent next, in the
\ order shown, and if the command returns a result (denoted by a leading = sign
\ in the command name), then this is then sent back to the parasite.
\
\ Consider the following command, which scans the keyboard or Delta 14B keypad
\ for a specific flight key:
\
\ =scan_y(key_offset, delta_14b)
\
\ To run this command, the parasite would first send a value of &96 to the I/O
\ processor (using the tube_write routine), then it would send the key_offset
\ and delta_14b parameters (in that order), and finally it would wait for the
\ result to be returned by calling the tube_read routine.
\
\ Meanwhile, at the other end, the receipt of the &96 value would trigger a call
\ to the routine in WRCHV, which is the tube_wrch routine. This routine sees
\ that the received value is greater than 127, so it calls the tube_func
\ routine, which then looks up the corresponding routine from this table
\ (routine scan_y in this case) and calls it to implement the command. The
\ scan_y routine then fetches the parameter values using the tube_get routine,
\ performs the keyboard or keypad scan according to the command's parameters,
\ and finally sends the result back to the parasite using the tube_put routine.
\
\ ******************************************************************************
.tube_table
EQUW LL30 \ &80 draw_line(x1, y1, x2, y2)
EQUW HLOIN \ &81 draw_hline(x1, y1, x2)
EQUW PIXEL \ &82 draw_pixel(x, y, distance)
EQUW clr_scrn \ &83 clr_scrn()
EQUW CLYNS \ &84 clr_line()
EQUW sync_in \ &85 =sync_in()
EQUW DILX \ &86 draw_bar(value, colour, screen_low, screen_high)
EQUW DIL2 \ &87 draw_angle(value, screen_low, screen_high)
EQUW MSBAR \ &88 put_missle(number, colour)
EQUW scan_fire \ &89 =scan_fire()
EQUW write_fe4e \ &8A =write_fe4e(value)
EQUW scan_xin \ &8B =scan_xin(key_number)
EQUW scan_10in \ &8C =scan_10in()
EQUW get_key \ &8D =get_key()
EQUW CHPR \ &8E write_xyc(x, y, char)
EQUW write_pod \ &8F write_pod(escp, hfx)
EQUW draw_blob \ &90 draw_blob(x, y, colour)
EQUW draw_tail \ &91 draw_tail(x, y, base_colour, alt_colour, height)
EQUW SPBLB \ &92 draw_S()
EQUW ECBLB \ &93 draw_E()
EQUW UNWISE \ &94 draw_mode()
EQUW DET1 \ &95 write_crtc(rows)
EQUW scan_y \ &96 =scan_y(key_offset, delta_14b)
EQUW write_0346 \ &97 write_0346(value)
EQUW read_0346 \ &98 =read_0346()
EQUW return \ &99 return()
EQUW HANGER \ &9A picture_h(line_count, multiple_ships)
EQUW HA2 \ &9B picture_v(line_count)
IF _BUG_FIX
EQUW savews \ &9C savews()
ENDIF
\ ******************************************************************************
\
\ Name: CHPR
\ Type: Subroutine
\ Category: Text
\ Summary: Implement the write_xyc command (write a character to the screen)
\
\ ------------------------------------------------------------------------------
\
\ This routine is run when the parasite sends a write_xyc command. It writes a
\ text character to the screen at specified position. If the character is null
\ (i.e. A = 0) then it just moves the text cursor and doesn't print anything.
\
\ ******************************************************************************
.CHPR
JSR tube_get \ Get the parameters from the parasite for the command:
STA XC \
JSR tube_get \ write_xyc(x, y, char)
STA YC \
JSR tube_get \ and store them as follows:
\
\ * XC = text column (x-coordinate)
\
\ * YC = text row (y-coordinate)
\
\ * A = the character to print
CMP #' ' \ If we are not printing a space character, jump to
BNE tube_wrch \ tube_wrch to print the character, returning from the
\ subroutine using a tail call
LDA #9 \ We are printing a space, so set A to 9 and fall
\ through into tube_wrch to print the character
\ ******************************************************************************
\
\ Name: tube_wrch
\ Type: Subroutine
\ Category: Text
\ Summary: Write characters to the screen and process Tube commands from the
\ parasite
\ Deep dive: Tube communication in Elite-A
\
\ ------------------------------------------------------------------------------
\
\ This routine prints characters to the screen.
\
\ It also processes Tube commands from the parasite, because those commands are
\ sent over the Tube via FIFO 1, and Acorn's Tube host code considers arrivals
\ on FIFO 1 to be OSWRCH commands executed on the parasite, and calls the WRCHV
\ handler to implement the call. We already set WRCHV to point here in the
\ tube_elite routine, so when the I/O processor receives a byte from the
\ parasite over FIFO 1, the Tube host code calls this routine.
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ A The character to be printed. Can be one of the
\ following:
\
\ * 0 (do not print anything)
\
\ * 9 (space)
\
\ * 10 (line feed)
\
\ * 13 (carriage return)
\
\ * 32 (space, but do not print anything if it's on
\ column 17, so the disc catalogue will fit on-screen)
\
\ * 33-126 (ASCII capital letters, numbers and
\ punctuation)
\
\ * 127 (delete the character to the left of the text
\ cursor and move the cursor to the left)
\
\ * 128-255 (Tube command &80-&FF)
\
\ ******************************************************************************
.tube_wrch
STA K3 \ Store the A, X and Y registers, so we can restore
STX XSAV2 \ them at the end (so they don't get changed by this
STY YSAV2 \ routine)
TAY \ Copy the character to be printed from A into Y
BMI tube_func \ If bit 7 of the character is set (i.e. A >= 128) then
\ this is a Tube command rather than a printable
\ character, so jump to tube_func to process it
BEQ wrch_quit \ If A = 0 then there is no character to print, so jump
\ to wrch_quit to return from the subroutine
IF _BUG_FIX
JSR SwitchToCharSet \ Switch &C000 to the MOS character definitions if we
\ are printing while there is disc activity
ENDIF
CMP #127 \ If A = 127 then this is a delete character, so jump
BEQ wrch_del \ to wrch_del to erase the character to the left of the
\ cursor
CMP #32 \ If A = 32 then this is a space character, so jump to
BEQ wrch_spc \ wrch_spc to move the text cursor to the right
BCS wrch_char \ If this is an ASCII character (A > 32), jump to
\ wrch_char to print the character on-screen
CMP #10 \ If A = 10 then this is a line feed, so jump to wrch_nl
BEQ wrch_nl \ to move the text cursor down a line
CMP #13 \ If A = 13 then this is a carriage return, so jump to
BEQ wrch_cr \ wrch_cr to move the text cursor to the start of the
\ line
CMP #9 \ If A <> 9 then this isn't a character we can print,
BNE wrch_quit \ so jump to wrch_quit to return from the subroutine
\ If we get here then A = 9, which is a space character
.wrch_tab
INC XC \ Move the text cursor to the right by 1 column
.wrch_quit
IF _BUG_FIX
JSR SwitchToFileSys \ Switch &C000 back to the filing system workspace
ENDIF
LDY YSAV2 \ Restore the values of the A, X and Y registers that we
LDX XSAV2 \ saved above
LDA K3
RTS \ Return from the subroutine
.wrch_char
\ If we get here then we want to print the character in
\ A onto the screen
JSR wrch_font \ Call wrch_font to set the following:
\
\ * font(1 0) points to the character definition of
\ the character to print in A
\
\ * SC(1 0) points to the screen address where we
\ should print the character
\ Now to actually print the character
INC XC \ Once we print the character, we want to move the text
\ cursor to the right, so we do this by incrementing
\ XC. Note that this doesn't have anything to do
\ with the actual printing below, we're just updating
\ the cursor so it's in the right position following
\ the print
LDY #7 \ We want to print the 8 bytes of character data to the
\ screen (one byte per row), so set up a counter in Y
\ to count these bytes
.wrch_or
LDA (font),Y \ The character definition is at font(1 0), so load the
\ Y-th byte from font(1 0), which will contain the
\ bitmap for the Y-th row of the character
EOR (SC),Y \ If we EOR this value with the existing screen
\ contents, then it's reversible (so reprinting the
\ same character in the same place will revert the
\ screen to what it looked like before we printed
\ anything); this means that printing a white pixel
\ onto a white background results in a black pixel, but
\ that's a small price to pay for easily erasable text
STA (SC),Y \ Store the Y-th byte at the screen address for this
\ character location
DEY \ Decrement the loop counter
BPL wrch_or \ Loop back for the next byte to print to the screen
BMI wrch_quit \ Jump to wrch_quit to return from the subroutine (the
\ BMI is effectively a JMP as we just passed through a
\ BPL instruction)
.wrch_del
\ If we get here then we want to delete the character to
\ the left of the text cursor, which we can do by
\ printing a space over the top of it
DEC XC \ We want to delete the character to the left of the
\ text cursor and move the cursor back one, so let's
\ do that by decrementing YC. Note that this doesn't
\ have anything to do with the actual deletion below,
\ we're just updating the cursor so it's in the right
\ position following the deletion
LDA #' ' \ Call wrch_font to set the following:
JSR wrch_font \
\ * font(1 0) points to the character definition of
\ the space character
\
\ * SC(1 0) points to the screen address where we
\ should print the space
LDY #7 \ We want to print the 8 bytes of character data to the
\ screen (one byte per row), so set up a counter in Y
\ to count these bytes
.wrch_sta
LDA (font),Y \ The character definition is at font(1 0), so load the
\ Y-th byte from font(1 0), which will contain the
\ bitmap for the Y-th row of the space character
STA (SC),Y \ Store the Y-th byte at the screen address for this
\ character location
DEY \ Decrement the loop counter
BPL wrch_sta \ Loop back for the next byte to print to the screen
BMI wrch_quit \ Jump to wrch_quit to return from the subroutine (the
\ BMI is effectively a JMP as we just passed through a
\ BPL instruction)
.wrch_nl
\ If we get here then we want to print a line feed
INC YC \ Print a line feed, simply by incrementing the row
\ number (y-coordinate) of the text cursor, which is
\ stored in YC
JMP wrch_quit \ Jump to wrch_quit to return from the subroutine
.wrch_cr
\ If we get here then we want to print a carriage return
LDA #1 \ Print a carriage return by returning the text cursor
STA XC \ to the start of the line, i.e. column 1
JMP wrch_quit \ Jump to wrch_quit to return from the subroutine
.wrch_spc
\ If we get here then we want to print a space, but not
\ if we are in column 17 (this is so the disc catalogue
\ will fit on-screen, and performs the same duty as the
\ CATF flag in the disc version)
LDA XC \ If the text cursor is in column 32, then we are
CMP #32 \ already at the right edge of the screen and can't
BEQ wrch_quit \ print a space, so jump to wrch_quit to return from
\ the subroutine
CMP #17 \ If the text cursor is in column 17, then we want to
BEQ wrch_quit \ omit this space, so jump to wrch_quit to return from
\ the subroutine
BNE wrch_tab \ Otherwise jump to wrch_tab to move the cursor right by
\ one character (the BNE is effectively a JMP as we just
\ passed through a BEQ)
\ ******************************************************************************
\
\ Name: wrch_font
\ Type: Subroutine
\ Category: Text
\ Summary: Set the font and screen address for printing characters on-screen
\
\ ------------------------------------------------------------------------------
\
\ Arguments:
\
\ A The character to be printed (ASCII)
\
\ ------------------------------------------------------------------------------
\
\ Returns:
\
\ font(1 0) The address of the MOS character definition of the
\ character to be printed
\
\ SC(1 0) The screen address where we should print the character
\ (i.e. the screen address of the text cursor)
\
\ ******************************************************************************
.wrch_font
LDX #&BF \ Set X to point to the first font page in ROM minus 1,
\ which is &C0 - 1, or &BF
ASL A \ If bit 6 of the character is clear (A is 32-63)
ASL A \ then skip the following instruction
BCC font_c0
LDX #&C1 \ A is 64-126, so set X to point to page &C1
.font_c0
ASL A \ If bit 5 of the character is clear (A is 64-95)
BCC font_cl \ then skip the following instruction
INX \ Increment X
\
\ By this point, we started with X = &BF, and then
\ we did the following:
\
\ If A = 32-63: skip then INX so X = &C0
\ If A = 64-95: X = &C1 then skip so X = &C1
\ If A = 96-126: X = &C1 then INX so X = &C2
\
\ In other words, X points to the relevant page. But
\ what about the value of A? That gets shifted to the
\ left three times during the above code, which
\ multiplies the number by 8 but also drops bits 7, 6
\ and 5 in the process. Look at the above binary
\ figures and you can see that if we cleared bits 5-7,
\ then that would change 32-53 to 0-31... but it would
\ do exactly the same to 64-95 and 96-125. And because
\ we also multiply this figure by 8, A now points to
\ the start of the character's definition within its
\ page (because there are 8 bytes per character
\ definition)
\
\ Or, to put it another way, X contains the high byte
\ (the page) of the address of the definition that we
\ want, while A contains the low byte (the offset into
\ the page) of the address
.font_cl
STA font \ Store the address of this character's definition in
STX font+1 \ font(1 0)
LDA XC \ Fetch XC, the x-coordinate (column) of the text cursor
\ into A
ASL A \ Multiply A by 8, and store in SC. As each character is
ASL A \ 8 pixels wide, and the special screen mode Elite uses
ASL A \ for the top part of the screen is 256 pixels across
STA SC \ with one bit per pixel, this value is not only the
\ screen address offset of the text cursor from the left
\ side of the screen, it's also the least significant
\ byte of the screen address where we want to print this
\ character, as each row of on-screen pixels corresponds
\ to one page. To put this more explicitly, the screen
\ starts at &6000, so the text rows are stored in screen
\ memory like this:
\
\ Row 1: &6000 - &60FF YC = 1, XC = 0 to 31
\ Row 2: &6100 - &61FF YC = 2, XC = 0 to 31
\ Row 3: &6200 - &62FF YC = 3, XC = 0 to 31
\
\ and so on
LDA YC \ Fetch YC, the y-coordinate (row) of the text cursor
ORA #&60 \ We already stored the least significant byte
\ of this screen address in SC above (see the STA SC
\ instruction above), so all we need is the most
\ significant byte. As mentioned above, in Elite's
\ square mode 4 screen, each row of text on-screen
\ takes up exactly one page, so the first row is page
\ &60xx, the second row is page &61xx, so we can get
\ the page for character (XC, YC) by OR'ing with &60.
\ To see this in action, consider that our two values
\ are, in binary:
\
\ YC is between: %00000000
\ and: %00010111
\ &60 is: %01100000
\
\ so YC OR &60 effectively adds &60 to YC, giving us
\ the page number that we want
STA SC+1 \ Store the page number of the destination screen
\ location in SC+1, so SC now points to the full screen
\ location where this character should go
RTS \ Return from the subroutine
\ ******************************************************************************
\
\ Name: SwitchToCharSet
\ Type: Subroutine
\ Category: Utility routines
\ Summary: Switch the MOS character definitions into memory at &C000 on a BBC
\ Master
\
\ ******************************************************************************
IF _BUG_FIX
.wsstatecopy
EQUB 0 \ We store a copy of the wstate here so we know which
\ state to switch back to after printing
.SwitchToCharSet
\ This routine switches the MOS character definitions
\ into memory at &C000 on a BBC Master
LDA #0 \ Call OSBYTE with A = 0 and X = 1 to fetch bit 0 of the
LDX #1 \ operating system version into X
JSR OSBYTE
CPX #3 \ If X =< 3 then this is not a BBC Master, so jump to
BCC char1 \ char1 to continue drawing the character
LDA wsstate \ Copy wsstate into wsstatecopy
STA wsstatecopy
JSR savews \ Call savews to put the character set in the correct
\ place
.char1
LDA K3 \ Set A to the character to print
RTS \ Return from the subroutine
ENDIF
\ ******************************************************************************
\
\ Name: SwitchToFileSys
\ Type: Subroutine
\ Category: Utility routines
\ Summary: Restore the filing system workspace to &C000 on a BBC Master
\
\ ******************************************************************************
IF _BUG_FIX
.SwitchToFileSys
\ This routine restores the filing system workspace to
\ &C000 on a BBC Master, but only if we overwrote it in
\ SwitchToCharSet
LDA #0 \ Call OSBYTE with A = 0 and X = 1 to fetch bit 0 of the
LDX #1 \ operating system version into X
JSR OSBYTE