From 2973d0c78e9b8eed3c5af239927c6bd36af64604 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Wed, 13 Mar 2019 02:50:42 -0400 Subject: initial commit --- src/col80_modified/cruft/col80_main.s | 824 ++++++++++++++++++++++++++++++++++ 1 file changed, 824 insertions(+) create mode 100644 src/col80_modified/cruft/col80_main.s (limited to 'src/col80_modified/cruft/col80_main.s') diff --git a/src/col80_modified/cruft/col80_main.s b/src/col80_modified/cruft/col80_main.s new file mode 100644 index 0000000..0ced210 --- /dev/null +++ b/src/col80_modified/cruft/col80_main.s @@ -0,0 +1,824 @@ +; THIS IS A MODIFIED VERSION, for use with FujiChat + +; COL80.COM, aka COL80E.COM, aka COL80HND.COM +; (and probably several other names) + +; Original author unknown +; License unknown +; Disassembly and comments by Urchlay + +; This is a widely-distributed software 80-column driver for the Atari +; 8-bit computers. It replaces the OS's E: driver, and uses GRAPHICS 8 +; for display, with 4x8 pixel character cells. + +; Disassembly was done with da65, with many iterations of "edit the +; .info file, disassemble again", and the results were tweaked by hand +; into something assemblable by dasm (and fairly compatible with other +; assemblers). + + + .include "col80_include.s" + +; START_ADDRESS is defined in col80_startaddr.s + .org START_ADDRESS + +; ---------------------------------------------------------------------------- +; Start of COL80. The font is stored in packed form. Each group of 8 bytes +; defines two glyphs: the upper 4 bits of the 8 bytes, taken together, +; define the bitmap for the first glyph, and the lower 4 bits are the second. +; Note that the bits that make up a single character are spread across 8 +; bytes, so it's hard to visualize these even if you're used to reading hex +; dumps. + +; The first 2 characters look like: + +; .... .O.. ; $04 +; .... .O.. ; $04 +; O.O. .O.. ; $A4 +; OOO. .O.. ; $E4 +; OOO. .OOO ; $E7 +; .O.. .O.. ; $44 +; .... .O.. ; $04 +; .... .O.. ; $04 + +; These are the ATASCII heart symbol (character code 0) and the ATASCII +; control-A line-drawing symbol (code 1). + +; Note: unlike the ROM font, this font is stored in ATASCII order instead +; of the standard Atari character order imposed by the hardware. Like +; the ROM font, inverse characters are not stored here (the bitmaps get +; inverted by the driver) + +font_data: + .ifdef FUJICHAT + .include "new_font.s" + .else + ; Low ATASCII graphics symbols (code 0-31) + .byte $04,$04,$A4,$E4,$E7,$44,$04,$04 + .byte $14,$14,$14,$14,$1C,$10,$10,$10 + .byte $40,$40,$40,$40,$CC,$44,$44,$44 + .byte $18,$18,$24,$24,$42,$42,$81,$81 + .byte $10,$10,$30,$30,$73,$73,$F3,$F3 + .byte $83,$83,$C3,$C3,$E0,$E0,$F0,$F0 + .byte $CF,$CF,$C0,$C0,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$0C,$0C,$FC,$FC + .byte $00,$00,$00,$40,$A7,$44,$E4,$04 + .byte $04,$04,$04,$04,$FF,$04,$04,$04 + .byte $00,$00,$60,$F0,$FF,$6F,$0F,$0F + .byte $80,$80,$80,$80,$8F,$84,$84,$84 + .byte $4C,$4C,$4C,$4C,$FC,$0C,$0C,$0C + .byte $40,$4C,$48,$4C,$78,$0C,$06,$00 + .byte $00,$44,$E4,$44,$4E,$44,$00,$00 + .byte $00,$24,$42,$FF,$42,$24,$00,$00 + + ; Space ! " # etc (codes 32-63) + .byte $00,$04,$04,$04,$04,$00,$04,$00 + .byte $00,$A0,$AA,$AE,$0A,$0E,$0A,$00 + .byte $00,$40,$68,$82,$44,$28,$C2,$40 + .byte $00,$C4,$64,$E4,$60,$C0,$40,$00 + .byte $00,$44,$82,$82,$82,$82,$82,$44 + .byte $00,$04,$A4,$4E,$E4,$44,$A0,$00 + .byte $00,$00,$00,$0E,$00,$40,$40,$80 + .byte $00,$02,$02,$04,$04,$08,$48,$00 + .byte $00,$E4,$AC,$A4,$A4,$A4,$EE,$00 + .byte $00,$EE,$22,$22,$EE,$82,$EE,$00 + .byte $00,$AE,$A8,$AE,$E2,$22,$2E,$00 + .byte $00,$EE,$82,$E2,$A4,$A4,$E4,$00 + .byte $00,$EE,$AA,$EA,$AE,$A2,$EE,$00 + .byte $00,$00,$00,$44,$00,$44,$04,$08 + .byte $00,$20,$4E,$80,$4E,$20,$00,$00 + .byte $00,$8C,$42,$22,$44,$80,$04,$00 + + ; @ A B C etc (codes 64-95) + .byte $00,$6E,$9A,$BA,$BE,$8A,$6A,$00 + .byte $00,$C6,$A8,$C8,$A8,$A8,$C6,$00 + .byte $00,$CE,$A8,$AC,$A8,$A8,$CE,$00 + .byte $00,$E6,$88,$C8,$8A,$8A,$86,$00 + .byte $00,$AE,$A4,$E4,$A4,$A4,$AE,$00 + .byte $00,$2A,$2A,$2C,$2A,$2A,$CA,$00 + .byte $00,$8A,$8E,$8E,$8A,$8A,$EA,$00 + .byte $00,$C4,$AA,$AA,$AA,$AA,$A4,$00 + .byte $00,$EE,$AA,$EA,$8A,$8A,$8E,$03 + .byte $00,$C6,$A8,$AC,$C2,$A2,$AC,$00 + .byte $00,$EA,$4A,$4A,$4A,$4A,$4E,$00 + .byte $00,$AA,$AA,$AA,$AE,$AE,$4A,$00 + .byte $00,$AA,$4A,$4E,$44,$44,$A4,$00 + .byte $00,$EE,$28,$48,$88,$88,$E8,$0E + .byte $00,$8E,$82,$42,$42,$22,$22,$0E + .byte $00,$00,$40,$A0,$00,$00,$00,$0F + + ; diamond, lowercase letters, control codes (codes 96-127) + .byte $00,$00,$00,$46,$E2,$4E,$0E,$00 + .byte $00,$80,$80,$C6,$A8,$A8,$C6,$00 + .byte $00,$20,$20,$6E,$AE,$A8,$6E,$00 + .byte $00,$00,$C0,$86,$CA,$8E,$82,$0C + .byte $00,$80,$84,$80,$C4,$A4,$A4,$00 + .byte $00,$08,$28,$0A,$2C,$2A,$2A,$C0 + .byte $00,$40,$40,$4A,$4E,$4A,$4A,$00 + .byte $00,$00,$00,$CE,$AA,$AA,$AE,$00 + .byte $00,$00,$00,$C6,$AA,$C6,$82,$82 + .byte $00,$00,$00,$6E,$88,$86,$8E,$00 + .byte $00,$00,$40,$EA,$4A,$4A,$6E,$00 + .byte $00,$00,$00,$AA,$AA,$AE,$4A,$00 + .byte $00,$00,$00,$AA,$4A,$A6,$A2,$0C + .byte $00,$00,$04,$EE,$4E,$84,$EE,$00 + .byte $40,$4E,$4C,$4E,$4A,$42,$42,$40 + .byte $00,$28,$6C,$EE,$6C,$28,$00,$00 + .endif + +right_margin: + ; Default value is 79 decimal. Unsure why the author didn't use RMARGN at $53 + .byte $4F + +; ---------------------------------------------------------------------------- +; Start of COL80 code. + +; Callback for CIO OPEN command. + +col80_open: + jsr init_graphics_8 + lda #$00 + sta ROWCRS + sta COLCRS + .ifndef FUJICHAT + nop + nop + .endif + sta BUFCNT + lda #$4F + sta right_margin + rts + +; ---------------------------------------------------------------------------- +; Assembly version of GRAPHICS 8+16 command. + +init_graphics_8: + lda #$08 + sta ICAX2Z + lda #$0C + sta ICAX1Z + jsr open_s_dev + + ; Set COL80's default colors + lda #$08 + sta COLOR2 + .ifndef FUJICHAT + nop + nop + nop + .endif + lda #$00 + sta COLOR1 + + ; Protect ourselves from BASIC and the OS + lda #START_ADDRESS + 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 CIO CLOSE command. Note that the routine does nothing, really +; (the OS will mark the E: device as being closed, but COL80 doesn't do any +; cleanup). +; The SPECIAL and GET STATUS callbacks in col80_vector_tab also point here. + +col80_close: + jmp return_success + +; ---------------------------------------------------------------------------- +; Callback for the internal put-one-byte, used by the OS to implement the +; CIO PUT RECORD and PUT BYTES commands. This routine's one argument is +; the byte in the accumulator (the character to print). + +; First, the routine checks for the cursor control characters it supports. +; COL80 only handles the EOL and clear-screen codes; trying to print +; backspaces, arrows, deletes, inserts, etc just causes their ATASCII +; graphics character to print instead. + +col80_putbyte: + ; EOL (decimal 155)? + cmp #$9B + bne check_clear + lda right_margin + sta COLCRS + jmp skip_write + +check_clear: + .ifndef FUJICHAT ; 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 #$FF + sta inverse_mask + bne skip_ninv + +not_inverse: + lda #$00 + sta inverse_mask + +skip_ninv: + txa + and #$7F + .ifdef FUJICHAT ; mask out low ASCII + sec + sbc #$20 + bcs not_low_ascii + jmp return_success +not_low_ascii: + .endif + sta TMPCHR + lda DINDEX + cmp #$08 + beq graphics_ok + ; If we're not in GRAPHICS 8 mode, reinitialize ourselves + jsr col80_open + +graphics_ok: + ; Call the routines that actually print the character + jsr setup_font_ptr + jsr setup_screen_ptr + jsr write_font_data + +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 + +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 + +; ---------------------------------------------------------------------------- +; Scroll the screen up one line (8 scanlines). This has to move almost 8K of +; data, so it's noticeably slower than scrolling the GR.0 text screen. + +scroll_screen: + lda SAVMSC + sta screen_ptr_lo + clc + adc #$40 + ; font_ptr_lo is actually being used here as a second pointer into + ; screen RAM, instead of its usual use as a pointer into the + ; font_data table + sta font_ptr_lo + lda SAVMSC+1 + sta screen_ptr_hi + adc #$01 + sta font_ptr_hi + ldx #$1D + ldy #$00 + +scroll_line_loop: + lda (font_ptr_lo),y + sta (screen_ptr_lo),y + dey + bne scroll_line_loop + inc font_ptr_hi + inc screen_ptr_hi + dex + bne scroll_line_loop + +blank_bottom_row: + lda SAVMSC + clc + adc #$C0 + sta screen_ptr_lo + lda SAVMSC+1 + adc #$1C + sta screen_ptr_hi + lda #$00 + tay + +blank_loop: + sta (screen_ptr_lo),y + dey + bne blank_loop + inc screen_ptr_hi + ldy #$40 + +blank_tail: + sta (screen_ptr_lo),y + dey + bpl blank_tail + rts + +; ---------------------------------------------------------------------------- +; Set up font_ptr_lo/hi 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. + +setup_font_ptr: + lda #$00 + sta font_ptr_hi + sta lo_nybble_flag + lda TMPCHR + clc + ror + bcc font_hi_nybble + ldx #$FF + stx lo_nybble_flag + +font_hi_nybble: + clc + rol + rol + rol font_ptr_hi + rol + rol font_ptr_hi + adc #font_data + adc font_ptr_hi + sta font_ptr_hi + 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 right_margin + cmp COLCRS + bcs same_line + lda LMARGN + sta COLCRS + lda ROWCRS + ; $17 is 25 decimal, one row below the lowest on the screen + cmp #$17 + bcc no_scroll + jsr scroll_screen + ; Move to row 24 after scrolling + lda #$16 + sta ROWCRS + +no_scroll: + inc ROWCRS + +same_line: + rts + +; ---------------------------------------------------------------------------- +; Clear the screen by setting all screen RAM bytes to zero. Slow, but not +; as slow as scrolling. + + .ifndef FUJICHAT +clear_screen: + lda SAVMSC + sta screen_ptr_lo + lda SAVMSC+1 + sta screen_ptr_hi + ldy #$00 + ldx #$1D + lda #$00 + +cls_loop: + sta (screen_ptr_lo),y + dey + bne cls_loop + inc screen_ptr_hi + dex + bne cls_loop + jsr blank_bottom_row + lda LMARGN + sta COLCRS + lda #$00 + sta ROWCRS + ; redundant JMP + jmp return_success + .endif + +; ---------------------------------------------------------------------------- +; CIO expects the Y register to contain a status code. +; 1 means success (no error). Lots of COL80's routines +; jump here. + +return_success: + ldy #$01 + rts + +; ---------------------------------------------------------------------------- +; Set screen_ptr_lo/hi to point to the address of the first byte of graphics +; data at the current cursor position. + +setup_screen_ptr: + ldy ROWCRS + lda SAVMSC + clc + adc row_low_offset_tab,y + sta screen_ptr_lo + lda SAVMSC+1 + adc row_high_offset_tab,y + sta screen_ptr_hi + lda COLCRS + lsr + clc + adc screen_ptr_lo + bcc hi_byte_ok + inc screen_ptr_hi + +hi_byte_ok: + sta screen_ptr_lo + rts + +; ---------------------------------------------------------------------------- +; Tables of offsets for setup_screen_ptr, to avoid doing multiplication at +; runtime (the 6502 lacks a MUL instruction, so it's slow...) + +row_low_offset_tab: + .byte $00,$40,$80,$C0,$00,$40,$80,$C0 + .byte $00,$40,$80,$C0,$00,$40,$80,$C0 + .byte $00,$40,$80,$C0,$00,$40,$80,$C0 + +row_high_offset_tab: + .byte $00,$01,$02,$03,$05,$06,$07,$08 + .byte $0A,$0B,$0C,$0D,$0F,$10,$11,$12 + .byte $14,$15,$16,$17,$19,$1A,$1B,$1C + +; ---------------------------------------------------------------------------- +; Copy pixel data from the font table to screen RAM. +; font_ptr_lo/hi must point to the correct character, and screen_ptr_lo/hi +; must point to the correct screen address for the current cursor position. +; This routine has separate execution paths for even- and odd-numbered +; cursor positions, since each byte of screen RAM holds data for two +; adjacent characters (and when printing to one of them, the other needs +; to be left undisturbed!) + +write_font_data: + lda COLCRS + clc + ror + bcc write_font_data_even + ldx #$00 + ldy #$00 + +get_font_nybble_odd: + lda (font_ptr_lo),y + bit lo_nybble_flag + bne lo_nybble_odd + ; glyph we want is stored in top 4 bits of font byte, + ; shift it down to the bottom 4 bits + lsr + lsr + lsr + lsr + +lo_nybble_odd: + eor inverse_mask + and #$0F + sta TMPCHR + ldy scanline_offset_tab,x + lda (screen_ptr_lo),y + and #$F0 + ora TMPCHR + sta (screen_ptr_lo),y + inx + cpx #$07 + bne screen_ptr_ok_odd + inc screen_ptr_hi + +screen_ptr_ok_odd: + cpx #$08 + beq write_font_done_odd + txa + tay + bne get_font_nybble_odd + +write_font_done_odd: + rts + +; ---------------------------------------------------------------------------- +; Write data to even-numbered columns, very similar to the above + +write_font_data_even: + ldx #$00 + ldy #$00 + +get_font_nybble_even: + lda (font_ptr_lo),y + bit lo_nybble_flag + beq hi_nybble_even + asl + asl + asl + asl + +hi_nybble_even: + eor inverse_mask + and #$F0 + sta TMPCHR + ldy scanline_offset_tab,x + lda (screen_ptr_lo),y + and #$0F + ora TMPCHR + sta (screen_ptr_lo),y + inx + cpx #$07 + bne screen_ptr_ok_even + inc screen_ptr_hi + +screen_ptr_ok_even: + cpx #$08 + beq write_font_done_even + txa + tay + bne get_font_nybble_even + +write_font_done_even: + rts + +; ---------------------------------------------------------------------------- + +scanline_offset_tab: + .byte $00,$28,$50,$78,$A0,$C8,$F0,$18 + +; ---------------------------------------------------------------------------- +; 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, COL80 maintains a line buffer. Each time col80_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. + +col80_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: + .ifdef FUJICHAT + lda #$00 + .else + lda #$20 + .endif + sta TMPCHR + lda #$FF + sta inverse_mask + jsr setup_font_ptr + jsr setup_screen_ptr + jsr write_font_data + jsr get_keystroke + cpy #$01 + beq keystroke_ok + .ifdef FUJICHAT + dey ; yes, we really care about 1-byte optimizations + .else + ldy #$00 + .endif + sty line_buffer_index + sty BUFCNT + +keystroke_ok: + .ifdef FUJICHAT + cmp #$20 + bcc show_cursor ; ignore low ASCII + .endif + cmp #$9B + 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 + .ifdef FUJICHAT + jmp show_cursor + .else + jmp beep + .endif + +buffer_character: + sta line_buffer,x + jsr col80_putbyte + inc BUFCNT + jmp show_cursor + +return_key_hit: + jsr print_space + lda #$9B + ldx BUFCNT + sta line_buffer,x + inc BUFCNT + jsr col80_putbyte + jmp get_next_byte + +clear_key_hit: + .ifndef FUJICHAT + jsr clear_screen + .endif + 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 right_margin + sta COLCRS + dec ROWCRS + +backs_same_line: + dec BUFCNT + +backs_key_done: + jmp show_cursor + +; ---------------------------------------------------------------------------- +; Ring the margin bell. COL80 doesn't implement the ctrl-2 bell (character +; 253), and instead of using the GTIA keyclick speaker, it uses POKEY to +; make a beep + + .ifndef FUJICHAT +beep: ldy #$00 + ldx #$AF + +beep_delay_x: + stx AUDF1 + stx AUDC1 + +beep_delay_y: + dey + bne beep_delay_y + dex + cpx #$9F + bne beep_delay_x + jmp show_cursor + .endif + +; ---------------------------------------------------------------------------- +; Print a space character at the current cursor position. Does not +; update the cursor position. +print_space: + lda #$00 + sta inverse_mask + .ifndef FUJICHAT + lda #$20 + .endif + sta TMPCHR + jsr setup_font_ptr + jsr setup_screen_ptr + jsr write_font_data + 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 + + .ifndef FUJICHAT + .include "col80_init.s" + .endif + +; ---------------------------------------------------------------------------- +; COL80 vector table, in the format required by the OS. Our HATABS entry +; will point to this table, and the OS will call the routines listed here +; via the "call by RTS" method (which is why they're address-minus-one). + +; See the entry on HATABS in "Mapping the Atari" or the OS manual. + +col80_vector_tab: + .word col80_open-1 + .word col80_close-1 + .word col80_getbyte-1 + .word col80_putbyte-1 + .word col80_close-1 + .word col80_close-1 + .ifdef FUJICHAT + .byte 0, 0, 0 ; heh. + .else + jmp col80_init + .endif + + .ifndef FUJICHAT + .include "col80_entry.s" + .endif + +; ---------------------------------------------------------------------------- +; Various bits of runtime state here. It's unclear to me why the standard +; OS buffer location couldn't have been used instead (normally the top +; half of page 5), or why the other stuff couldn't have been stored in +; zero page, in locations used by the ROM E: handler (thus unused when +; it's replaced with COL80). line_buffer_index needs to be preserved +; across calls to col80_getbyte, but lo_nybble_flag and inverse_mask are +; freshly calculated every time they're used, so they could be almost +; anywhere. + + .ifdef FUJICHAT + .segment "CODE" + .endif + +lo_nybble_flag: + .byte $00 + +inverse_mask: + .byte $00 + +line_buffer_index: + .byte $12 + +; ---------------------------------------------------------------------------- +; There's absolutely no reason why this data needs to be included in the +; binary load file: the line buffer's initial contents are meaningless, they +; will be blown away the first time anything reads from the E: device. + +; Notice the author was running his debugger in COL80 when he built the +; binary (ASCII "S COL80 7A00 7F80" command still in the buffer). + + .ifdef FUJICHAT +line_buffer = $03FD ; cassette buffer + .else +line_buffer: + .byte $53,$20,$43,$4F,$4C,$38,$30,$20 + .byte $37,$41,$30,$30,$20,$37,$46,$38 + .byte $30,$9B,$20,$20,$20,$20,$9B,$27 + .byte $40,$40,$40,$40,$28,$28,$28,$28 + .byte $40,$40,$40,$40,$40,$40,$40,$40 + .byte $40,$40,$40,$40,$40,$40,$40,$40 + .byte $9B,$FD,$FD,$FD,$FD,$9B + .endif + +END_ADDRESS = *-1 + +; I've found a variant (modified version?) of this code, that doesn't +; include the line_buffer in the file (no reason for it to be there), +; or the $0C segment, and that has another segment, loaded at $6000, +; with the run address changed to $6000. The code looks like: + +; .org $6000 +; jsr dosini_entry_point +; lda #$50 +; sta RMARGN +; lda #$00 +; sta COLOR2 + +; also, the default colors have been changed in init_graphics_8. + +; There are at least two binaries floating around that contain +; extra (garbage) bytes at the end, presumably from being transferred +; over XMODEM or similar. They are otherwise identical. + -- cgit v1.2.3