aboutsummaryrefslogtreecommitdiff
path: root/src/col64/cruft/col64.dasm.works
blob: 1e0292186a0e671c0846a127ac7623c7a7ee6941 (plain)
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

; ----------------------------------------------------------------------------
; 64x32 software text driver
; Uses 4x5 font in 5x6 character cells
; Based on disassembled COL80 driver

; This driver is missing quite a few things a full E: driver should have:

; - No support for cursor controls, insert/delete, or even clear screen
;   (instead the control-codes are printed as normal characters, if they're
;   above 31 decimal)

; - No support for low ASCII at all: any attempt to input or print a character
;   in the range 0-31 decimal is just ignored (this includes the arrow keys)

; - Does not attempt to survive a warmstart

; - Will fail disastrously if an application sets COLCRS/ROWCRS to any
;   out-of-range values

; - Only displays the cursor during get-byte operations

; - Backspace key is supported during get-byte, but printing a backspace
;   with put-byte prints a tilde instead

; On the other hand, this driver is tiny, and plays nice with cc65's stdio
; support (though NOT conio!)

 processor 6502 ; DASM-specific

; ----------------------------------------------------------------------------
 include equates.inc
s_dev_open_lo   = $E410 ; (not named in OS sources)
s_dev_open_hi   = $E411 ; ""
k_dev_get_lo    = $E424 ; ""
k_dev_get_hi    = $E425 ; ""

; ----------------------------------------------------------------------------
; Constants

LAST_ROW = $1F
EOL      = $9B
INVERTED_MASK = $F8

; ----------------------------------------------------------------------------
; Defaults

DEFAULT_TEXT_COLOR = $00
DEFAULT_BG_COLOR   = $08
;DEFAULT_LMARGN     = $00 ; not used
DEFAULT_RMARGN     = $3F

; ----------------------------------------------------------------------------
; ZP storage
; Reuse some of the E:/S: devices' storage
; These all get recalculated on col64_putbyte entry,
; so it's OK to mix with GR.8 PLOT/DRAWTO stuff

 seg.u "data"
 org $5A ; aka OLDROW
mask_lo         ds 1
mask_hi         ds 1
screen_ptr_lo   ds 1
screen_ptr_hi   ds 1
font_index      ds 1
shift_amount    ds 1
line_count      ds 1
glyph_data_lo   ds 1
glyph_data_hi   ds 1

; ----------------------------------------------------------------------------
; Non-ZP storage (cassette buffer for now)

 org CASBUF
lo_nybble_flag    ds 1
inverse_mask      ds 1
line_buffer_index ds 1
line_buffer       ds $80

 seg "code"
; ----------------------------------------------------------------------------
; XEX segment header
START_ADDRESS_1 = $9A00
 org START_ADDRESS_1-6

 word $FFFF
 word START_ADDRESS_1
 word END_ADDR_1

; ----------------------------------------------------------------------------
; Font data should be aligned on a page boundary
; (for speed; still works unaligned). Each 5 bytes stores 2 glyphs
; side-by-side. When rendering into 5x6 cells, the bottom line and
; the left column are set to all 0, so the glyphs can take up the
; entire 4x5 space.
; Only character codes 32 through 127 are supported; this is 96
; characters. At 2 glyphs per 5 bytes, that's 96/2*5=240 bytes,
; which means the whole font fits into one page and can be
; accessed with X or Y indexed mode rather than (indirect),Y.

font_data:
 include font4x5.inc

; ----------------------------------------------------------------------------
; Mask tables, used by setup_mask. Indexed by (COLCRS % 8), since they
; would repeat every 8 bytes anyway.

mask_lo_tab:
 byte $07, $F8, $C1, $FE, $F0, $83, $FC, $E0

; In mask_hi_tab, $00 is never a valid mask, while $FF means "don't touch any
; bits in 2nd byte". As a minor optimization, $00 appears in the table in
; place of $FF. This allows us to just test the Z flag after loading the mask
; hi byte, instead of needing to compare against $FF.

mask_hi_tab:
 byte $00, $3F, $00, $0F, $7F, $00, $1F, $00

; ----------------------------------------------------------------------------
; Indexed by COLCRS%8, how many bits to shift the font data right
; The mask_*_tab stuff above could be calculated at runtime from this,
; the tables are only there for speed.
shift_amount_tab:
 byte $00, $05, $02, $07, $04, $01, $06, $03

; ----------------------------------------------------------------------------
; Line address tables, used by setup_screen_ptr.

line_addr_tab_hi:
 byte $00, $00, $01, $02, $03, $04, $05, $06
 byte $07, $08, $09, $0a, $0b, $0c, $0d, $0e
 byte $0f, $0f, $10, $11, $12, $13, $14, $15
 byte $16, $17, $18, $19, $1a, $1b, $1c, $1d

line_addr_tab_lo:
 byte $00, $f0, $e0, $d0, $c0, $b0, $a0, $90
 byte $80, $70, $60, $50, $40, $30, $20, $10
 byte $00, $f0, $e0, $d0, $c0, $b0, $a0, $90
 byte $80, $70, $60, $50, $40, $30, $20, $10

; ----------------------------------------------------------------------------
; Byte offset within scanline, indexed by COLCRS value
; Used by setup_screen_ptr

column_byte_tab:
 byte $00, $00, $01, $01, $02, $03, $03, $04
 byte $05, $05, $06, $06, $07, $08, $08, $09
 byte $0A, $0A, $0B, $0B, $0C, $0D, $0D, $0E
 byte $0F, $0F, $10, $10, $11, $12, $12, $13
 byte $14, $14, $15, $15, $16, $17, $17, $18
 byte $19, $19, $1A, $1A, $1B, $1C, $1C, $1D
 byte $1E, $1E, $1F, $1F, $20, $21, $21, $22
 byte $23, $23, $24, $24, $25, $26, $26, $27

; ----------------------------------------------------------------------------
; Handler table (HATABS will point to this).
; See the HATABS and EDITRV entries in Mapping the Atari for details.

col64_vector_tab:
 word col64_open-1     ; OPEN vector
 word col64_close-1    ; CLOSE vector
 word col64_getbyte-1  ; GET BYTE vector
 word col64_putbyte-1  ; PUT BYTE vector
 word col64_close-1    ; GET STATUS vector
 word col64_close-1    ; SPECIAL vector
 jmp col64_init      ; Jump to initialization code (JMP LSB/MSB)

; ----------------------------------------------------------------------------
; Assembly version of GRAPHICS 8+16 command.

init_graphics_8:
 lda     #$08 ; graphics mode 8
 sta     ICAX2Z
 lda     #$0C ; R/W access
 sta     ICAX1Z
 jsr     open_s_dev

 ; Set default colors
 lda     #DEFAULT_BG_COLOR
 sta     COLOR2
 sta     COLOR4
 lda     #DEFAULT_TEXT_COLOR
 sta     COLOR1

 ; Protect ourselves from the OS
 lda     #<START_ADDRESS_1
 sta     MEMTOP
 lda     #>START_ADDRESS_1
 sta     MEMTOP+1
 rts

; ----------------------------------------------------------------------------
; Call the OPEN vector for the S: device, using the ROM vector table
; at $E410. The table stores address-minus-one of each routine, which is
; meant to actually be called via the RTS instruction (standard 6502
; technique, but confusing the first time you encounter it)

open_s_dev:
 lda     s_dev_open_hi
 pha
 lda     s_dev_open_lo
 pha
 rts

; ----------------------------------------------------------------------------
; Callback for the internal get-one-byte, used by the OS to implement the
; CIO GET RECORD and GET BYTES commands. This routine takes no arguments,
; and returns the read byte in the accumulator.

; Internally, COL64 maintains a line buffer. Each time col64_getbyte is
; called, it returns the next character in the buffer. If the buffer's
; empty (or if the last call returned the last character), a new line
; of input is read from the user (and the first character is returned).
; This is exactly how the OS E: device works.

col64_getbyte:
        lda     BUFCNT
        beq     get_line

get_next_byte:
        ldx     line_buffer_index
        lda     line_buffer,x
        dec     BUFCNT
        inc     line_buffer_index
        jmp     return_success

; ----------------------------------------------------------------------------
; Get a line of input from the user, terminated by the Return key.

get_line:
        lda     #$00
        sta     BUFCNT
        sta     line_buffer_index

show_cursor:
        lda     #$00
        sta     TMPCHR
        lda     #INVERTED_MASK
        sta     inverse_mask
        jsr     render_glyph
        jsr     get_keystroke
        cpy     #$01
        beq     keystroke_ok
        dey ; yes, we really care about 1-byte optimizations
        sty     line_buffer_index
        sty     BUFCNT

keystroke_ok:
        cmp #$20
		  bcc show_cursor ; ignore low ASCII
        cmp     #EOL
        bne     check_backs_key
        jmp     return_key_hit

check_backs_key:
        cmp     #$7E
        bne     check_clear_key
        jmp     backs_key_hit

check_clear_key:
        cmp     #$7D
        bne     normal_key_hit
        jmp     clear_key_hit

normal_key_hit:
        ldx     BUFCNT
        bpl     buffer_character
;        jmp     beep ; if we implemented it...
        jmp show_cursor

buffer_character:
        sta     line_buffer,x
        jsr     col64_putbyte
        inc     BUFCNT
        jmp     show_cursor

return_key_hit:
        jsr     print_space
        lda     #EOL
        ldx     BUFCNT
        sta     line_buffer,x
        inc     BUFCNT
        jsr     col64_putbyte
        jmp     get_next_byte

clear_key_hit:
;        jsr     clear_screen ; if we implemented it...
        lda     #$00
        sta     line_buffer_index
        sta     BUFCNT
        jmp     get_line

backs_key_hit:
        jsr     print_space
        lda     BUFCNT
        beq     backs_key_done
        dec     COLCRS
        lda     COLCRS
        clc
        adc     #$01
        cmp     LMARGN
        bne     backs_same_line
        lda     RMARGN
        sta     COLCRS
        dec     ROWCRS

backs_same_line:
        dec     BUFCNT

backs_key_done:
        jmp     show_cursor

; ----------------------------------------------------------------------------
; Print a space character at the current cursor position. Does not
; update the cursor position.
print_space:
        lda     #$00
        sta     inverse_mask

        sta     TMPCHR
        jsr     render_glyph
        rts

; ----------------------------------------------------------------------------
; Get a keystroke (blocking). Just calls the OS K: get-one-byte routine
; (call by pushing address-minus-one then doing an RTS)

get_keystroke:
        lda     k_dev_get_hi
        pha
        lda     k_dev_get_lo
        pha
        rts


; ----------------------------------------------------------------------------
; Unimplemented CIO callbacks here. Also various other routines jump here
; to return success to the caller.

col64_close:
return_success:
 ldy #$01
 rts

; ----------------------------------------------------------------------------
; CIO OPEN command callback

col64_open:
 jsr     init_graphics_8
 lda     #$00
 sta     ROWCRS
 sta     COLCRS
 sta     BUFCNT
 sta     LMARGN
 lda     #DEFAULT_RMARGN
 sta     RMARGN
 rts

; ----------------------------------------------------------------------------
; CIO PUT BYTE command callback
; The byte to put is passed to us in the accumulator.

col64_putbyte:
 ; EOL (decimal 155)?
 cmp     #EOL
;;;        bne     check_clear
 bne     regular_char
 lda     RMARGN
 sta     COLCRS
 jmp     skip_write

;;;check_clear:
;;; ; save memory by not including clear_screen
;;; ; (also, this lets us print the } character)
;;;        ; Clear (decimal 125)?
;;;        cmp     #$7D
;;;        bne     regular_char
;;;        jmp     clear_screen
;;; .endif
;;;

; See if this is an inverse video char (code >= 128)
regular_char:
 tax
 bpl not_inverse
 lda #INVERTED_MASK
 sta inverse_mask
 bne skip_ninv

not_inverse:
 lda #$00
 sta inverse_mask

skip_ninv:
 txa
 and #$7F
 sec
 sbc #$20
 bcs not_low_ascii
 jmp return_success

not_low_ascii:
 sta TMPCHR

 lda DINDEX ; OS stores current graphics mode here
 cmp #$08
 beq graphics_ok
 ; If we're not in GRAPHICS 8 mode, reinitialize ourselves
 jsr col64_open

graphics_ok:
 jsr render_glyph

skip_write:
 ; Move the cursor 1 space to the right. This will
 ; advance us to the next line if we're at the margin,
 ; and scroll the screen if needed
 jsr     advance_cursor

; Could implement SSFLAG logic here
;check_ssflag:
 ; The OS keyboard interrupt handler will toggle SSFLAG (start/stop fla
 ; any time the user presses ctrl-1
 ;lda     SSFLAG
 ;bne     check_ssflag
 jmp     return_success

; ----------------------------------------------------------------------------
; Call the routines that actually print the character.
; render_glyph prints the character in TMPCHR at the current
; COLCRS and ROWCRS, and does NOT advance the cursor.
; TMPCHR should already have bit 7 stripped; render_glyph will
; use inverse_mask, so the caller should have set that up as well.
render_glyph:
 jsr     setup_mask
 jsr     setup_screen_ptr
 jsr     setup_font_index
 jmp     write_font_data

; ----------------------------------------------------------------------------
; mask is used to avoid overwriting pixels outside the character cell
; we're currently writing. Since 5 pixel wide cells don't align on byte
; boundaries in screen RAM, we have to read/modify/write 2 bytes, and
; the mask also has to be 2 bytes wide.
; Also we set up shift_amount here.

setup_mask:
 lda COLCRS
 and #$07
 tax
 lda mask_lo_tab,x
 sta mask_lo
 lda mask_hi_tab,x
 sta mask_hi
 lda shift_amount_tab,x
 sta shift_amount
 rts


; ----------------------------------------------------------------------------
; Make (screen_ptr_lo) point to the first byte of screen RAM on the top scanline
; of the current char cell. Assumes COLCRS/ROWCRS are never out of range!
setup_screen_ptr:
 ; first the row... table lookup quicker than mult by 240
 ldx ROWCRS
 clc
 lda SAVMSC
 adc line_addr_tab_lo,x
 sta screen_ptr_lo
 lda SAVMSC+1
 adc line_addr_tab_hi,x
 sta screen_ptr_hi

 ; now do the column
 ldx COLCRS
 lda screen_ptr_lo
 clc
 adc column_byte_tab,x
 sta screen_ptr_lo
 lda #0
 adc screen_ptr_hi
 sta screen_ptr_hi

 rts

; ----------------------------------------------------------------------------
; Set up font_index to point to the font_data bitmap for the character in
; TMPCHR. Also sets lo_nybble_flag to let the caller know whether the
; bitmap is in the upper or lower 4 bits of the bytes pointed to.

; Calculation is:
; lo_nybble_flag = (TMPCHR & 1) ? $FF : $00;
; font_index = (TMPCHR >> 1) * 5;

setup_font_index:
        lda     #$00
        sta     lo_nybble_flag
        lda     TMPCHR
        lsr     ; a = (TMPCHR >> 1)
        tay     ; y = a
        bcc     font_hi_nybble
        dec     lo_nybble_flag ; = $FF

font_hi_nybble:
        clc
        asl ; a *= 2
        asl ; a *= 2
        sta font_index
        tya
        adc font_index
        sta font_index

        rts


; ----------------------------------------------------------------------------
; When write_font_data is called:
; - font_index is the 1-byte index into font_data where the current glyph is
; - lo_nybble_flag is 0 for high nybble of glyph data, $FF for low
; - mask_lo/hi is our 16-bit pixel mask (1's are "leave original data")
; - shift_amount is # of times to shift glyph data right
; - screen_ptr_lo/hi points to the 1st byte on the top line

; Loop 5 times, each time thru the loop:
; - extract 4-bit glyph data, store in glyph_data_lo
; - shift right shift_amount times
; ...write data...
; - add 40 to 16-bit screen_ptr_lo/hi (to get to next line)

write_font_data:
 lda #$05
 sta line_count

wfont_line_loop:
 lda #$00
 tay
 sta glyph_data_hi

 ldx font_index
 lda font_data,x

 bit lo_nybble_flag
 beq use_hi_nybble

 asl
 asl
 asl
 asl

use_hi_nybble: ; 4-bit glyph data now in hi nybble
 and #$F0
 eor inverse_mask
 sta glyph_data_lo

 ldx shift_amount
 beq wfont_no_shift

wfont_shift_loop:
 lsr glyph_data_lo
 ror glyph_data_hi
 dex
 bne wfont_shift_loop

wfont_no_shift:
 lda mask_lo
 and (screen_ptr_lo),y
 ora glyph_data_lo
 sta (screen_ptr_lo),y

 lda mask_hi
 beq wfont_skip_hi
 iny
 and (screen_ptr_lo),y
 ora glyph_data_hi
 sta (screen_ptr_lo),y

wfont_skip_hi:
 dec line_count
 bmi wfont_done
 bne wfont_not_bottom

 stx font_index ; X always 0: for last line, cheat and use the space glyph
 stx lo_nybble_flag

wfont_not_bottom:
 lda #$28 ; 40 bytes to next line
 clc
 adc screen_ptr_lo
 sta screen_ptr_lo
 bcc wfont_noinc
 inc screen_ptr_hi

wfont_noinc:
 inc font_index
 bne wfont_line_loop ; branch always

wfont_done:
 rts

; ----------------------------------------------------------------------------
; Not the fastest scroller in the west... TODO: make faster :)

; glyph_data_lo points to line N
; screen_ptr_lo points to line N+1
scroll_screen:
 lda #0
 sta COLCRS
 sta ROWCRS
 jsr setup_screen_ptr

scroll_line_loop:
 lda screen_ptr_lo
 sta glyph_data_lo
 lda screen_ptr_hi
 sta glyph_data_hi
 ldx ROWCRS
 cpx #LAST_ROW
 beq scroll_blank
 inx
 stx ROWCRS
 jsr setup_screen_ptr
 ldy #0

scroll_byte_loop:
 lda (screen_ptr_lo),y
 sta (glyph_data_lo),y
 iny
 cpy #$F0
 bne scroll_byte_loop
 beq scroll_line_loop

scroll_blank:
 jsr setup_screen_ptr
 ldy #0
 tya
sblank_loop:
 sta (screen_ptr_lo),y
 iny
 cpy #$F0
 bne sblank_loop

 rts

; ----------------------------------------------------------------------------
; Move the cursor one space to the right (to the next line if at the margin,
; and scroll screen if on the last row)

advance_cursor:
        inc     COLCRS
        lda     RMARGN
        cmp     COLCRS
        bcs     same_line
        lda     LMARGN
        sta     COLCRS
        lda     ROWCRS
        cmp     #LAST_ROW
        bcc     no_scroll
        jsr     scroll_screen
        ; Move to last row after scrolling
        lda     #LAST_ROW-1
        sta     ROWCRS

no_scroll:
        inc     ROWCRS

same_line:
        rts

; ----------------------------------------------------------------------------
; Initialization. If we don't want the handler to survive a warmstart, we
; can load this bit into e.g. the cassette buffer (throw away after running)

col64_init:
        ldy     #$00

next_hatab_slot:
        lda     HATABS,y
        beq     register_x_handler
        iny
        iny
        iny
        cpy     #$20
        bcc     next_hatab_slot
        jmp     return_success

register_x_handler:
        lda     #$58
        sta     HATABS,y
        lda     #<col64_vector_tab
        iny
        sta     HATABS,y
        lda     #>col64_vector_tab
        iny
        sta     HATABS,y
        jmp     return_success

main_entry_point:
        jsr     col64_init
        lda     #$0C
        sta     ICCOM
        ldx     #$00
        jsr     CIOV
        lda     #$58
        sta     font_index
        lda     #$03
        sta     ICCOM
        lda     #font_index
        sta     ICBAL
        lda     #$00
        sta     ICBAH
        ldx     #$00
        jsr     CIOV
        ldy     #$07
        lda     #<col64_vector_tab
        sta     HATABS,y
        lda     #>col64_vector_tab
        iny
        sta     HATABS,y
no_e_handler:
        lda     #<START_ADDRESS_1
        sta     MEMTOP
        lda     #>START_ADDRESS_1
        sta     MEMTOP+1
        jmp     return_success

END_ADDR_1 = *-1

; XEX segment (run address)
 word INITAD
 word INITAD+1
 word main_entry_point