-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcbm2.asm
1667 lines (1488 loc) · 53.4 KB
/
cbm2.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
;CBM-II Port of Softbox CP/M Loader
;Requires expansion RAM in Bank 15 (System Bank)
;
;Configure VICE
; Settings > CBM2 Settings > Memory > Enable Bank 15 $4000-5FFF RAM
;
;B-series
; bload"cbm2.prg",b15 ;BLOAD into bank 15
; sys 16384 ;Start terminal
;
;P-series
; sys 4 ;Start monitor
; l"cbm2.prg",08 ;"08" is the device number in hex
; g 4000 ;Start terminal
;
cinv_lo = $0300 ;KERNAL IRQ vector LO
cinv_hi = $0301 ;KERNAL IRQ vector HI
keyd = $03ab ;Keyboard Buffer
screen = $d000 ;Start of screen RAM
vic = $d800 ;6567/6569 VIC-II (P-series)
crtc = $d800 ;6545 CRTC (B-series)
sid = $da00 ;6581 SID
cia2_pa = $dc00 ;6526 CIA #2 Port A
cia2_ddra = $dc02 ;6526 CIA #2 Data Direction Register A
cia2_cra = $dc0e ;6526 CIA #2 Control Register A
tpi1_pa = $de00 ;6525 TPI #1 Port A
tpi1_pb = $de01 ;6525 TPI #1 Port B
tpi1_pc = $de02 ;6525 TPI #1 Port C
tpi1_ddra = $de03 ;6525 TPI #1 Data Direction Register A
tpi1_ddrc = $de05 ;6525 TPI #1 Data Direction Register C
tpi1_cr = $de06 ;6525 TPI #1 Control Register
tpi1_air = $de07 ;6525 TPI #1 Active Interrupt Register
tpi2_pa = $df00 ;6525 TPI #1 Port A - Keyboard Row select LO
tpi2_pb = $df01 ;6525 TPI #1 Port B - Keyboard Row select HI
tpi2_pc = $df02 ;6525 TPI #1 Port C - Keyboard Col read
scrorg = $ffed ;KERNAL Get screen dimensions
;
e6509 = $00 ;6509 Execute Register
i6509 = $01 ;6509 Indirect Register
scrline_lo = $02 ;Pointer to start of current line in screen RAM - LO
scrline_hi = $03 ;Pointer to start of current line in screen RAM - HI
cursor_x = $04 ;Current X position: 0-79
cursor_y = $05 ;Current Y position: 0-24
cursor_on = $06 ;Cursor visible flag (hide = $00, show = $80)
cursor_tmp = $07 ;Saves original cursor_on value during screen updates
scrcode = $08 ;Screen code under the cursor
keycount = $09 ;Number of keys in the buffer at keyd
columns = $0a ;Screen width (X) in characters (40 or 80)
lines = $0b ;Screen height (Y) in characters (24 or 25)
moveto_cnt = $0c ;Counts down bytes to consume in a move-to (CTRL_1B) seq
source_lo = $0d ;Pointer to source address for memory operations - LO
source_hi = $0e ;Pointer to source address for memory operations - HI
target_lo = $0f ;Pointer to target address for memory operations - LO
target_hi = $10 ;Pointer to target address for memory operations - HI
xfer_lo = $11 ;Memory transfer byte counter - LO
xfer_hi = $12 ;Memory transfer byte counter - HI
char_mask = $13 ;Masks incoming bytes for 7- or 8-bit character mode
rtc_jiffies = $14 ;Real Time Clock Jiffies
rtc_secs = $15 ;Real Time Clock Seconds Note: the RTC and Jiffy
rtc_mins = $16 ;Real Time Clock Minutes locations can't be changed
rtc_hours = $17 ;Real Time Clock Hours because they are accessed by
jiffy2 = $18 ;Jiffy Counter (MSB) the SoftBox BIOS to support
jiffy1 = $19 ;Jiffy Counter CP/M programs like TIME.COM.
jiffy0 = $1a ;Jiffy Counter (LSB)
blink_cnt = $1b ;Counts down number of IRQs until cursor reverses
blink_on = $1c ;Cursor blink flag (blink off = $00, blink on = $80)
uppercase = $1d ;Uppercase graphics flag (lower = $00, upper = $80)
rvs_mask = $1e ;Reverse video mask (normal = $00, reverse = $80)
got_srq = $1f ;IEEE-488 SRQ detect: 0=no SRQ, 1=SRQ pending
hertz = $20 ;Stores the system interrupt frequency: 50 or 60 Hz
*=$4000
init:
sei ;Disable interrupts
ldx #$ff
txs ;Initialize stack pointer
lda #$0f
sta i6509 ;Bank 15 (System Bank)
lda #<irq_handler
sta cinv_lo
lda #>irq_handler
sta cinv_hi ;Install our interrupt handler
lda #%0000011 ;Bit 1: IEEE-488 SRQ
;Bit 0: 50/60 Hz
sta tpi1_ddrc ;TPI Interrupt Mask Register
lda tpi1_pb
and #%10111111
sta tpi1_pb ;Turn cassette motor off
lda #$15
sta sid+$18 ;SID mode/vol (used by bell)
lda #$00
sta rtc_jiffies ;Reset software real time clock
sta rtc_secs
sta rtc_mins
sta rtc_hours
sta jiffy2 ;Reset jiffy counter
sta jiffy1
sta jiffy0
sta keycount ;Reset key counter (no keys hit)
lda #$0a
sta repeatcount1 ;Number of interrupts between repeats of a key
init_scrn:
;Detect 40/80 column screen and store in columns.
;Initialize VIC-II registers for P500.
;Initialize cursor state.
;
jsr scrorg ;Returns X=columns, Y=lines
stx columns ;Initialize screen width to 40 or 80 columns
dey
sty lines ;Initialize screen height to 24 lines
cpx #40 ;40 column screen?
bne init_scrn_done ; No: Skip P500 init
lda #$00
sta vic+$20 ;VIC-II Border color = Black
sta vic+$21 ;VIC-II Background color = Black
init_scrn_done:
lda #$00
sta cursor_x ;Initialize cursor position
sta cursor_y
jsr init_scrlines ;Initialize screen line pointer table
jsr get_scrline ;Initialize current screen line pointer
lda #$80
sta cursor_on ;Cursor state = show the cursor
sta blink_on ;Cursor blink = blinking on
lda #$20 ;Screen code for space, also initial value for blink
sta scrcode ;Initialize screen code under the cursor
sta blink_cnt ;Initialize cursor blink countdown
;Fall through to init_hertz
init_hertz:
;Initialize the powerline frequency constant used by our software clock.
;On power up, the KERNAL routine IOINIT on both B-series and P-series will
;detect 50 or 60 Hz and set the TODIN bit in CIA 2's Control Register A.
;
lda #50
bit cia2_cra ;CRA Bit 7 (TODIN): off = 60 Hz, on = 50 Hz
bmi store_hertz ;Did the KERNAL set TODIN for 50 Hz?
lda #60 ; No: hertz = 60
store_hertz:
sta hertz ; Yes: hertz = 50
init_term:
jsr ctrl_16 ;Go to lowercase mode
jsr ctrl_02 ;Go to 7-bit character mode
jsr ctrl_06 ;Clear all tab stops
lda #$00
sta moveto_cnt ;Move-to counter = not in a move-to seq
lda #$1a ;Load $1A = CTRL_1A Clear Screen
jsr process_byte ;Call into terminal to execute clear screen
init_ieee:
;
;6525 TPI #1 ($DE00)
; PA7 NRFD
; PA6 NDAC
; PA5 EOI
; PA4 DAV
; PA3 ATN
; PA2 REN
; PA1 75161A pin 1 TE
; PA0 75161A pin 11 DC
;
; PB1 SRQ
; PB0 IFC
;
;6526 CIA #2 ($DC00)
; PA0-7 Data
;
lda #$00
sta got_srq ;Initialize SRQ pending flag
lda #$c6
sta cia2_pa ;Put $39 (SoftBox address) on IEEE data lines
lda #$ff
sta cia2_ddra ;Data lines all outputs
lda #%00100010 ;EOI=high, DAV=low, ATN=low, REN=low
sta tpi1_pa
lda #%00111111 ;PA7 NRFD Input
;PA6 NDAC Input
;PA5 EOI Output
;PA4 DAV Output
;PA3 ATN Output
;PA2 REN Output
;PA1 TE Output
;PA0 DC Output
sta tpi1_ddra
ldx #$02
atn_wait:
ldy #$00
atn_wait_1:
dey
bne atn_wait_1 ;Let $39 sit on the lines so the SoftBox sees it
dex
bne atn_wait
lda #%00111010 ;EOI=high, DAV=high, ATN=high, REN=low, TE=high, DC=low
sta tpi1_pa
cli ;Enable interrupts again
main_loop:
lda #$00
sta cia2_ddra ;Data lines all inputs
lda #%11001000 ;NRFD=high, NDAC=high, ATN=high, REN=low, TE=low, DC=low
sta tpi1_pa
lda #%11001111 ;PA7 NRFD Output
;PA6 NDAC Output
;PA5 EOI Input
;PA4 DAV Input
;PA3 ATN Output
;PA2 REN Output
;PA1 TE Output
;PA0 DC Output
sta tpi1_ddra
wait_srq_low:
lda got_srq
beq wait_srq_low ;Wait until the 6525 detects a falling edge on SRQ.
dec got_srq ;Clear SRQ pending flag (decrement 1 to 0)
wait_srq_high:
lda tpi1_pb ;Wait until SRQ returns high.
and #%00000010 ; The command byte is only valid after SRQ returns
beq wait_srq_high ; high. Unfortunately, SRQ is wired to a pin on the
; the 6525 that can't detect a rising edge.
lda cia2_pa ;Read IEEE data byte from SoftBox
eor #$ff ;Invert it (IEEE-488 uses negative logic)
and #%00111111 ;Remove bits 7-6 which are only used for handshaking
tax ;Save the command byte in X
;The hardware of both the PET/CBM machines and the
;SoftBox allows each IEEE-488 data line to be
;individually selected as an input or output. The
;SoftBox exploits this by using the lower 6 bits of
;the data bus to output a command, while the upper
;2 bits are used as input:
;
;Bits 7-6: CBM to SoftBox: Handshake & key status
;Bits 5-0: SoftBox to CBM: Command
;
;The CBM-II machines have different IEEE-488 hardware
;and are not capable of driving some data lines while
;reading others. See the note below.
lda #$bf ;Response will be $40 (no key available)
ldy keycount ;Is the keyboard buffer empty?
beq send_key_avail ; Yes: Keep response as $40 (no key available)
lda #$7f ; No: Response will be $80 (key available)
send_key_avail:
sta cia2_pa ;Put keyboard status on the bus
lda #$ff
sta cia2_ddra ;Data lines all outputs
lda #%00111010 ;EOI=high, DAV=high, ATN=high, REN=low, TE=high, DC=low
sta tpi1_pa
lda #%00111111 ;PA7 NRFD Input
;PA6 NDAC Input
;PA5 EOI Output
;PA4 DAV Output
;PA3 ATN Output
;PA2 REN Output
;PA1 TE Output
;PA0 DC Output
sta tpi1_ddra
;At this point the SoftBox is guaranteed to be waiting
;for bit 6 or 7 to be pulled low. It will be within
;this loop in its cbm_srq routine:
;
; lfc95h:
; in a,(ppi1_pa) ;Read IEEE data byte
; and 0c0h ;Mask off all except bits 6 and 7
; jr z,lfc95h ;Wait one of those bits to go low
;
;One pass through the loop takes ~10 microseconds.
;The PET/CBM code pulls bits 7-6 low, then waits for
;the SoftBox to release bits 5-0 to high. The CBM-II
;hardware is not capable of this, so we must wait:
ldy #$06 ;Delay count for B-series
bit columns ;80 columns?
bvs send_k_a_wait ; Yes: Keep B-series count
ldy #$02 ; No: Delay count for P-series
send_k_a_wait: ;Wait at least 20 microseconds after the keyboard
dey ; status is put on the data bus to ensure the
bne send_k_a_wait ; SoftBox receives it.
;After the SoftBox receives the keyboard status, it
;loops waiting for all data lines to return high.
;The data lines will return high when we switch the
;drivers back to input mode:
lda #%11001111 ;PA7 NRFD Output
;PA6 NDAC Output
;PA5 EOI Input
;PA4 DAV Input
;PA3 ATN Output
;PA2 REN Output
;PA1 TE Output
;PA0 DC Output
sta tpi1_ddra
lda #%00001000 ;NRFD=low, NDAC=low, ATN=high, REN=low, TE=low, DC=low
sta tpi1_pa
lda #$00
sta cia2_ddra ;Data lines all inputs
dispatch_command:
cpx #$01 ;$01 = Key availability (sent with handshake, so done)
beq main_loop
cpx #$02 ;$02 = Wait for a key and send it
beq do_get_key
cpx #$04 ;$04 = Write to the terminal screen
beq do_terminal
cpx #$08 ;$08 = Execute a subroutine in CBM memory
beq do_execute
cpx #$10 ;$10 = Transfer from CBM memory to the SoftBox
beq do_peek
cpx #$20 ;$20 = Transfer from the SoftBox to CBM memory
beq do_poke
jmp main_loop ;Bad command
do_get_key:
;Wait for a key and send it to the SoftBox.
;
;At the CP/M "A>" prompt, the SoftBox sends this command and then
;waits for the CBM to return a key.
;
jsr get_key ;Block until we get a key. Key will be in A.
jsr ieee_put_byte ;Send the key to the Softbox.
jmp main_loop
do_terminal:
;Write to the terminal screen
;
jsr ieee_get_byte
jsr process_byte
jmp main_loop
do_execute:
;Execute a subroutine in CBM memory
;
jsr ieee_get_byte ;Get byte
sta target_lo ; -> Target vector lo
jsr ieee_get_byte ;Get byte
sta target_hi ; -> Target vector hi
jsr do_execute_ind ;Jump to the subroutine through TARGET_LO
jmp main_loop
do_execute_ind:
jmp (target_lo)
do_peek:
;Transfer bytes from CBM memory to the SoftBox
;
jsr ieee_get_byte
sta xfer_lo
jsr ieee_get_byte
sta xfer_hi
jsr ieee_get_byte
sta source_lo
jsr ieee_get_byte
sta source_hi
ldy #$00
l_05a5:
dey
bne l_05a5 ;Delay loop
l_05a8:
lda (source_lo),y
jsr ieee_put_byte
iny
bne l_05b2
inc source_hi
l_05b2:
lda xfer_lo
sec
sbc #$01
sta xfer_lo
lda xfer_hi
sbc #$00
sta xfer_hi
ora xfer_lo
bne l_05a8
jmp main_loop
do_poke:
;Transfer bytes from the SoftBox to CBM memory
;
jsr ieee_get_byte
sta xfer_lo
jsr ieee_get_byte
sta xfer_hi
jsr ieee_get_byte
sta target_lo
jsr ieee_get_byte
sta target_hi
ldy #$00
l_0571:
jsr ieee_get_byte
sta (target_lo),y
iny
bne l_057b
inc target_hi
l_057b:
lda xfer_lo
sec
sbc #$01
sta xfer_lo
lda xfer_hi
sbc #$00
sta xfer_hi
ora xfer_lo
bne l_0571
jmp main_loop
ieee_get_byte:
;Receive a byte from the SoftBox over the IEEE-488 bus.
;
lda tpi1_pa
ora #%10000000
sta tpi1_pa ;NRFD=high
l_05d7:
lda tpi1_pa
and #%00010000
bne l_05d7 ;Wait until DAV=low
lda cia2_pa ;Read the data byte
eor #$ff ;Invert it
pha ;Push data byte
lda tpi1_pa
and #%01111111 ;NRFD=low
ora #%01000000 ;NDAC=high
sta tpi1_pa
l_05ef:
lda tpi1_pa
and #%00010000
beq l_05ef ;Wait until DAV=high
lda tpi1_pa
and #%10111111 ;NDAC=low
sta tpi1_pa
pla
rts
ieee_put_byte:
;Send a byte to the SoftBox over the IEEE-488 bus.
;
eor #$ff ;Invert the byte
sta cia2_pa ;Put byte on IEEE data output lines
;Switch IEEE to Output
lda #$ff
sta cia2_ddra ;Data lines all outputs
lda #%00111010 ;EOI=high, DAV=high, ATN=high, REN=low, TE=high, DC=low
sta tpi1_pa
lda #%00111111 ;PA7 NRFD Input
;PA6 NDAC Input
;PA5 EOI Output
;PA4 DAV Output
;PA3 ATN Output
;PA2 REN Output
;PA1 TE Output
;PA0 DC Output
sta tpi1_ddra
l_060d:
bit tpi1_pa
bpl l_060d ;Wait until NRFD=high
lda tpi1_pa
and #%11101111 ;DAV=low
sta tpi1_pa
l_0617:
bit tpi1_pa
bvc l_0617 ;Wait until NDAC=high
lda tpi1_pa
ora #%00010000 ;DAV=high
sta tpi1_pa
l_0627:
bit tpi1_pa
bvs l_0627 ;Wait until NDAC=low
;Switch IEEE to Input
lda #$00
sta cia2_ddra ;Data lines all inputs
lda #%00001000 ;NRFD=low, NDAC=low, ATN=high, REN=low, TE=low, DC=low
sta tpi1_pa
lda #%11001111 ;PA7 NRFD Output
;PA6 NDAC Output
;PA5 EOI Input
;PA4 DAV Input
;PA3 ATN Output
;PA2 REN Output
;PA1 TE Output
;PA0 DC Output
sta tpi1_ddra
rts
get_key:
;Get the next key waiting from the keyboard buffer and return
;it in the accumulator. If there is no key, this routine will
;block until it gets one. Meanwhile, the interrupt handler
;calls SCAN_KEYB and puts any key into the buffer.
;
lda #$ff ;FF = no key
sei ;Disable interrupts
ldx keycount ;Is there a key waiting in the buffer?
beq l_0649 ; No: nothing to do with the buffer.
lda keyd ;Read the next key in the buffer (FIFO)
pha ;Push the key onto the stack
ldx #$00
dec keycount ;Keycount = Keycount - 1
l_063d:
lda keyd+1,x ;Remove the key from the buffer by rotating
sta keyd,x ; bytes in the buffer to the left
inx
cpx keycount ;Finished updating the buffer?
bne l_063d ; No: loop until we're done.
pla ;Pull the key off the stack.
l_0649:
cli ;Enable interrupts again
cmp #$ff ;No key or key is "NONE" in the tables?
beq get_key ; No key: loop until we get one.
rts ; Got key: done. Key is now in A.
irq_handler:
;On the CBM-II machines, an IRQ occurs at 50 or 60 Hz depending on the
;power line frequency. The 6502 calls the main IRQ entry point which
;pushes A, X, and Y onto the stack and then executes JMP (cinv_lo).
;We install this routine, irq_handler, into cinv_lo during init.
;
lda tpi1_air ;Read the active interrupt
check_ieee:
cmp #$02 ;IRQ from IEEE-488 SRQ?
bne irq_50hz
inc got_srq ;Set SRQ pending flag (increment 0 to 1)
jmp irq_done
;IRQ must have been caused by 50/60 Hz
irq_50hz:
;Update the jiffy clock
inc jiffy0 ;Counts number of Interrupts
bne irq_clock
inc jiffy1 ;counter
bne irq_clock
inc jiffy2 ;counter
irq_clock:
;Update the software real time clock
inc rtc_jiffies ;Increment Jiffies
lda rtc_jiffies
cmp hertz ;50 or 60 Hz
bne irq_blink
lda #$00 ;Reset RTC_JIFFIES counter
sta rtc_jiffies
inc rtc_secs ;Increment Seconds
lda rtc_secs
cmp #$3c ;Have we reached 60 seconds?
bne irq_blink ; No, skip
lda #$00 ; Yes, reset seconds
sta rtc_secs
inc rtc_mins ;Increment Minutes
lda rtc_mins
cmp #$3c ;Have we reached 60 minutes?
bne irq_blink ; No, skip
lda #$00 ; Yes, reset minutes
sta rtc_mins
inc rtc_hours ;Increment hours
lda rtc_hours
cmp #$18 ;Have we reached 24 hours
bne irq_blink ; No, skip
lda #$00 ; Yes, reset hours
sta rtc_hours
irq_blink:
;Blink the cursor
bit cursor_on ;Is the cursor off?
bpl irq_repeat ; No: skip cursor blink
ldy cursor_x
lda (scrline_lo),y ;Read character at cursor
bit blink_on ;Is cursor blink on?
bmi irq_blink_on ; Yes: branch to blink the cursor
ora #$80 ;Set reverse bit for steady cursor
bmi irq_blink_store
irq_blink_on:
dec blink_cnt ;Decrement cursor blink countdown
bne irq_repeat ;Not time to blink? Done.
ldx #$14
stx blink_cnt ;Reset cursor blink countdown
eor #$80 ;Flip reverse bit to blink cursor
irq_blink_store:
sta (scrline_lo),y
irq_repeat:
;Repeat key handling
lda scancode ;Get the SCANCODE
cmp repeatcode ;Compare to SAVED scancode
beq l_06b1 ;They are the same, so continue
sta repeatcode ;if not, save it
lda #$10 ;reset counter
sta repeatcount0 ;save to counter
bne irq_scan
l_06b1:
cmp #$ff ;NO KEY?
beq irq_scan ;Yes, jump to scan for another
lda repeatcount0 ;No, there was a key, so get the counter
beq l_06bf ;Is it zero?
dec repeatcount0 ;Count Down
bne irq_scan ;Is it Zero
l_06bf:
dec repeatcount1 ;Count Down
bne irq_scan ;Is it zero? No, scan for another
lda #$04 ;Yes, Reset it to 4
sta repeatcount1 ;Store it
lda #$00 ;Clear the SCANCODE and allow the key to be processed
sta scancode ;Store it
lda #$02
sta blink_cnt ;Blink cursor faster when key is repeating
irq_scan:
;Scan the keyboard
jsr scan_keyb ;Scan the keyboard
beq irq_done ;Nothing to do if no key was pressed.
cmp #$05 ;F10 pressed for Softbox reset?
beq irq_reset ; Yes: rti will start the reset routine
cmp #$06 ;F9 pressed for simulate SRQ?
bne irq_scan_2 ; No: process the key normally
inc got_srq ; Yes: set the SRQ flag (incr 0 to 1) and exit
jmp irq_done
irq_scan_2:
ldx keycount
cpx #$50 ;Is the keyboard buffer full?
beq irq_done ; Yes: Nothing we can do. Forget the key.
sta keyd,x ; No: Store the key in the buffer
inc keycount ; and increment the keycount.
irq_done:
sta tpi1_air ;Pop interrupt off TPI interrupt stack
pla
tay ;Restore Y
pla
tax ;Restore X
pla ;Restore A
rti
irq_reset:
sta tpi1_air ;Pop interrupt off TPI interrupt stack
ldx #$ff ;Reset the stack pointer
tsx
lda #>reset_softbox ;Push reset routine as return address
pha
lda #<reset_softbox
pha
lda #$00 ;Push new status register
pha
rti
reset_softbox:
;Pulse IFC to reset the IEEE-488 bus, wait enough time for the
;SoftBox to start up, then start again from the beginning.
;
jsr ctrl_1a ;Clear screen
lda #%00000010
sta tpi1_pa ;75161A TE=high
lda tpi1_pb
and #$fe
sta tpi1_pb ;IFC=low
ldx #$80
stx cursor_tmp ;Turn cursor on
stx cursor_on
stx rtc_jiffies ;Reset jiffy counter
ldx #$0d
reset_ifc:
cpx rtc_jiffies
bcs reset_ifc ;Wait while IFC is asserted
ora #$01
sta tpi1_pb ;IFC=high
ldx #$00
stx rtc_secs ;Reset seconds counter
ldx #$05
reset_wait:
cpx rtc_secs
bcs reset_wait ;Wait for SoftBox to start
jmp init ;Start again from the beginning
process_byte:
;This is the core of the terminal emulator. It accepts a byte in
;the accumulator, determines if it is a control code or character
;to display, and handles it accordingly.
;
tax ;Save A in X
lda cursor_on ;Get the current cursor state
sta cursor_tmp ; Remember it
lsr cursor_on ;Hide the cursor
lda scrcode ;Get the screen code under the cursor
ldy cursor_x
sta (scrline_lo),y ;Put it on the screen to erase the cursor
txa ;Restore A
and char_mask ;Mask off bits depending on char mode
ldx moveto_cnt ;More bytes to consume for a move-to sequence?
bne process_move ; Yes: branch to jump to move-to handler
cmp #$20 ;Is this byte a control code?
bcs process_char ; No: branch to put char on screen
process_ctrl:
asl ;a
tax
lda ctrl_codes,x ;Load vector from control code table
sta target_lo
lda ctrl_codes+1,x
sta target_hi
jsr process_ctrl_ind ;JSR to control code handler through vector
jmp process_done
process_ctrl_ind:
jmp (target_lo)
process_move:
jsr move_to ;JSR to move-to sequence handler
jmp process_done
process_char:
tax
lda asc_trans,x ;Get CBM screen code for the character
eor rvs_mask ;Reverse the screen code if needed
sta (scrline_lo),y ;Write the screen code to screen RAM
jsr ctrl_0c ;Advance the cursor
process_done:
jsr get_scrline ;Update screen RAM pointer
lda (scrline_lo),y ;Get the screen code under the cursor
sta scrcode ; Remember it
lda cursor_tmp ;Get the previous state of the cursor
sta cursor_on ; Restore it
rts
move_to:
;Implements CTRL_1B by handling the X-position byte on the first call
;and the Y-position byte on the second call. After the Y-position byte
;has been consumed, MOVETO_CNT = 0, exiting the move-to sequence.
;
sec
sbc #$20 ;Pos = Pos - $20 (ADM-3A compatibility)
dec moveto_cnt ;Decrement bytes remaining to consume
beq move_to_y ;Already got X pos? Handle this byte as Y.
;Fall through into move_to_x
move_to_x:
cmp columns ;Requested X position out of range?
bcs move_to_x_done ; Yes: Do nothing.
sta cursor_x ; No: Move cursor to requested X.
move_to_x_done:
rts
move_to_y:
cmp lines ;Requested Y position out of range?
bcs move_to_y_done ; Yes: Do nothing.
sta cursor_y ; No: Move cursor to requested Y.
move_to_y_done:
rts
init_asc_trans:
;Build the ASCII to CBM screen code translation table
;
ldy #$00 ;Start at ASCII code 0
init_ct_loop:
tya
jsr trans_char ;Translate to a CBM screen code
sta asc_trans,y ;Store it in the table
iny
bne init_ct_loop ;Loop until 256 codes are translated
rts
trans_char:
;Convert an ASCII (not PETSCII) character in the accumulator to its
;equivalent CBM screen code.
;
;Bytes $00-7F (bit 7 off) always correspond to the 7-bit standard
;ASCII character set and are converted to the equivalent CBM screen code.
;
;Bytes $80-FF (bit 7 on) are a special extended mode that display
;the CBM graphics characters if the terminal is in 8-bit mode (CTRL_01):
;
; Byte Screen Code
; $80-BF -> $40-7F
; $C0-FF -> $40-7F
;
ldx #$5d ;Change to PETSCII vertical line
cmp #$7c ; from ASCII pipe ("|") character
beq l_07c6
ldx #$64 ;Change to PETSCII underscore
cmp #$5f ; from ASCII underscore ("_") character
beq l_07c6
cmp #$40 ;Is it < 64?
bcc trans_done ; Yes: done, no translation
cmp #$60 ;Is it >= 96?
bcs l_07a6 ; Yes: branch to L_07A6
and #$3f ;Turn off bits 6 and 7
jmp l_07ac ;Jump to L_07AC
l_07a6:
cmp #$80 ;Is bit 7 set?
bcs l_07ca ; Yes: branch to L_07CA
and #$5f
l_07ac:
tax
and #$3f ;Turn off bit 7 and bit 6
beq l_07c6
cmp #$1b
bcs l_07c6
txa
eor #$40 ;Flip bit 6
bit uppercase
bpl trans_done ;Branch if lowercase mode
and #$1f
jmp trans_done
l_07c6:
txa
jmp trans_done
l_07ca:
and #$7f ;Turn off bit 7
ora #$40 ;Turn on bit 6
trans_done:
rts
ctrl_codes:
;Terminal control code dispatch table. These control codes are based
;on the Lear Seigler ADM-3A terminal. Some bytes that are unused on
;that terminal are used for other purposes here.
;
;Hex Keyboard
!word ctrl_00 ;00 CTRL-@ Do nothing
!word ctrl_01 ;01 CTRL-A Go to 8-bit character mode
!word ctrl_02 ;02 CTRL-B Go to 7-bit character mode
!word ctrl_03 ;03 CTRL-C Do nothing
!word ctrl_04 ;04 CTRL-D Set a TAB stop at current position
!word ctrl_05 ;05 CTRL-E Clear TAB stop at current position
!word ctrl_06 ;06 CTRL-F Clear all TAB stops
!word ctrl_07 ;07 CTRL-G Ring bell
!word ctrl_08 ;08 CTRL-H Cursor left
!word ctrl_09 ;09 CTRL-I Perform TAB
!word ctrl_0a ;0A CTRL-J Cursor down (Line feed)
!word ctrl_0b ;0B CTRL-K Cursor up
!word ctrl_0c ;0C CTRL-L Cursor right
!word ctrl_0d ;0D CTRL-M Carriage return
!word ctrl_0e ;0E CTRL-N Reverse video on
!word ctrl_0f ;0F CTRL-O Reverse video off
!word ctrl_10 ;10 CTRL-P Cursor on
!word ctrl_11 ;11 CTRL-Q Insert line
!word ctrl_12 ;12 CTRL-R Delete line
!word ctrl_13 ;13 CTRL-S Clear to end of line
!word ctrl_14 ;14 CTRL-T Clear to end of screen
!word ctrl_15 ;15 CTRL-U Go to uppercase mode
!word ctrl_16 ;16 CTRL-V Go to lowercase mode
!word ctrl_17 ;17 CTRL-W Set line spacing for graphics
!word ctrl_18 ;18 CTRL-X Set line spacing for text
!word ctrl_19 ;19 CTRL-Y Cursor off
!word ctrl_1a ;1A CTRL-Z Clear screen
!word ctrl_1b ;1B ESC Move cursor to X,Y position
!word ctrl_1c ;1C CTRL-/ Insert character
!word ctrl_1d ;1D CTRL-] Delete character
!word ctrl_1e ;1E CTRL-^ Home cursor
!word ctrl_1f ;1F Do nothing
ctrl_00:
ctrl_03:
ctrl_1f:
;Do nothing
rts
ctrl_01:
;Go to 8-bit character mode
;See trans_char for how this mode is used to display CBM graphics.
;
lda #$ff
sta char_mask
rts
ctrl_02:
;Go to 7-bit character mode
;
lda #$7f
sta char_mask
rts
ctrl_15:
;Go to uppercase mode
;
lda #$80
sta uppercase ;Set flag to indicate uppercase = on
jsr init_asc_trans ;Rebuild character translation table
bit columns ;40 columns?
bvc ctrl_15_1 ; Yes: branch to P-series routine
lda tpi1_cr
ora #%00010000
sta tpi1_cr ;B-series graphic mode = uppercase
rts
ctrl_15_1:
lda #$41
sta vic+$18 ;P-series graphic mode = uppercase
rts
ctrl_16:
;Go to lowercase mode
;
lda #$00
sta uppercase ;Set flag to indicate uppercase = off
jsr init_asc_trans ;Rebuild character translation table
bit columns ;40 columns?
bvc ctrl_16_1 ; Yes: branch to P-series routine
lda tpi1_cr
and #%11101111
sta tpi1_cr ;B-series graphic mode = lowercase
rts
ctrl_16_1:
lda #$43
sta vic+$18 ;P-series graphic mode = lowercase
rts
ctrl_17:
;Set line spacing for graphics (the default spacing for uppercase mode).
;The current graphic mode will not be changed.
;
rts ;No effect on CBM-II.
ctrl_18:
;Set line spacing for text (the default spacing for lowercase mode).
;The current graphic mode will not be changed.
;
rts ;No effect on CBM-II.
ctrl_07:
;Ring bell
;
bit columns ;80 columns?
bvs ctrl_07_b ; Yes: branch to B-series settings
ctrl_07_p:
lda #$40 ;P-series settings
ldx #$09
jmp ctrl_07_bell
ctrl_07_b:
lda #$20 ;B-series settings
ldx #$0a
ctrl_07_bell:
sta sid+$01 ;Voice 1 Freq Hi
stx sid+$05 ;Voice 1 Attack/Decay
lda #$00
sta sid+$06 ;Voice 1 Sustain/Release
sta sid+$04 ;Voice 1 = No waveform, Gate off
lda #$11
sta sid+$04 ;Voice 1 = Triangle waveform, Gate on
rts
ctrl_08:
;Cursor left
;
ldx cursor_x
bne ctrl_08_decx ;X > 0? Y will not change.
ldx columns ;X = max X + 1
lda cursor_y
beq ctrl_08_done ;Y=0? Can't move up.
dec cursor_y ;Y=Y-1