From 6e6ca2b20de0e6d7fdb31852fef528f9860675c7 Mon Sep 17 00:00:00 2001 From: Ivo van Poorten Date: Thu, 10 Oct 2024 18:28:39 +0200 Subject: [PATCH 1/5] Ohio Scientific Instruments port. Systems supported: - 400 series with ASCII keyboard, 32x32 Model 440 video, 5.25" or 8" floppy. - 500 series with polled keyboard, 64x32 Model 540 video, 5.25" or 8" floppy, optional 540B color mode screen driver. - 600 series with inverse polled keyboard, 64x16 video, 5.25" or 8" floppy. - 500 series based serial based systems with 8" floppy drives, loadable VT100 screen driver. --- .gitignore | 2 + build.py | 11 + diskdefs | 24 +- src/arch/osi/ascii.S | 65 +++ src/arch/osi/build.py | 301 +++++++++++++ src/arch/osi/floppy.S | 478 ++++++++++++++++++++ src/arch/osi/keyboard.S | 204 +++++++++ src/arch/osi/osi.S | 821 ++++++++++++++++++++++++++++++++++ src/arch/osi/osi.ld | 46 ++ src/arch/osi/serial.S | 147 ++++++ src/arch/osi/utils/build.py | 4 + src/arch/osi/utils/scrvt100.S | 371 +++++++++++++++ src/arch/osi/utils/tty540b.S | 605 +++++++++++++++++++++++++ tools/build.py | 25 ++ tools/img2osi.c | 186 ++++++++ tools/osi.h | 14 + 16 files changed, 3303 insertions(+), 1 deletion(-) create mode 100644 src/arch/osi/ascii.S create mode 100644 src/arch/osi/build.py create mode 100644 src/arch/osi/floppy.S create mode 100644 src/arch/osi/keyboard.S create mode 100644 src/arch/osi/osi.S create mode 100644 src/arch/osi/osi.ld create mode 100644 src/arch/osi/serial.S create mode 100644 src/arch/osi/utils/build.py create mode 100644 src/arch/osi/utils/scrvt100.S create mode 100644 src/arch/osi/utils/tty540b.S create mode 100644 tools/img2osi.c create mode 100644 tools/osi.h diff --git a/.gitignore b/.gitignore index eec12562..04382370 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ neo6502.zip sorbus.zip nano6502.img nano6502_sysonly.img +*.os5 +*.os8 diff --git a/build.py b/build.py index f481283c..550b28e0 100644 --- a/build.py +++ b/build.py @@ -26,6 +26,17 @@ "atari800xlhd.atr": "src/arch/atari800+atari800xlhd_diskimage", "c64.d64": "src/arch/commodore+c64_diskimage", "neo6502.zip": "src/arch/neo6502+diskimage", + "osi400mf.os5": "src/arch/osi+osi400mf_diskimage", + "osi500mf.os5": "src/arch/osi+osi500mf_diskimage", + "osi600mf.os5": "src/arch/osi+osi600mf_diskimage", + "osimf-b.os5": "src/arch/osi+osimf-b_diskimage", + "osimf-c.os5": "src/arch/osi+osimf-c_diskimage", + "osimf-d.os5": "src/arch/osi+osimf-d_diskimage", + "osi400f.os8": "src/arch/osi+osi400f_diskimage", + "osi500f.os8": "src/arch/osi+osi500f_diskimage", + "osi600f.os8": "src/arch/osi+osi600f_diskimage", + "osif-b.os8": "src/arch/osi+osif-b_diskimage", + "osiserf.os8": "src/arch/osi+osiserf_diskimage", "pet4032.d64": "src/arch/commodore+pet4032_diskimage", "pet8032.d64": "src/arch/commodore+pet8032_diskimage", "pet8096.d64": "src/arch/commodore+pet8096_diskimage", diff --git a/diskdefs b/diskdefs index d715228f..d2078a89 100644 --- a/diskdefs +++ b/diskdefs @@ -114,4 +114,26 @@ diskdef sdcard maxdir 1024 boottrk 1 os 2.2 -end \ No newline at end of file +end + +# 640 sectors = 80kB +diskdef osi5 + seclen 128 + tracks 40 + sectrk 16 + blocksize 1024 + maxdir 64 + boottrk 1 + os 2.2 +end + +# 1848 sectors = 231kB +diskdef osi8 + seclen 128 + tracks 77 + sectrk 24 + blocksize 1024 + maxdir 64 + boottrk 1 + os 2.2 +end diff --git a/src/arch/osi/ascii.S b/src/arch/osi/ascii.S new file mode 100644 index 00000000..60babdb0 --- /dev/null +++ b/src/arch/osi/ascii.S @@ -0,0 +1,65 @@ +; Ohio Scientific Instruments ASCII keyboard routines. +; Copyright © 2024 by Ivo van Poorten +; This file is licensed under the terms of the 2-clause BSD license. Please +; see the COPYING file in the root project directory for the full text. + +ZEROPAGE + +cur_char: .fill 1 +wait_cntr: .fill 1 +last_char: .fill 1 + +KEYBD = $df01 + +; ---------------------------------------------------------------------------- + + .section .text, "ax" + +scan_keyboard: + + zloop + lda KEYBD + + zif_mi + sta last_char + sta cur_char + ldx #2 + stx wait_cntr + rts + zendif + + cmp cur_char + zif_ne + sta cur_char + lda #2 + sta wait_cntr + zcontinue + zendif + + dec wait_cntr + zbreakif_eq + + ldy #$10 + zloop + ldx #$40 + zloop + dex + zuntil_eq + dey + zuntil_eq + zendloop + + ldx #$64 ; long delay on first character + cmp last_char + zif_eq + ldx #$0f ; shorter repeat rate + zendif + + stx wait_cntr + sta last_char + + rts + +; ---------------------------------------------------------------------------- + +; vim: filetype=asm sw=4 ts=4 et diff --git a/src/arch/osi/build.py b/src/arch/osi/build.py new file mode 100644 index 00000000..aa9aafe8 --- /dev/null +++ b/src/arch/osi/build.py @@ -0,0 +1,301 @@ +from tools.build import mkcpmfs, img2os5, img2os8 +from build.llvm import llvmrawprogram +from config import ( + MINIMAL_APPS, + MINIMAL_APPS_SRCS, + BIG_APPS, + BIG_APPS_SRCS, + SCREEN_APPS, + SCREEN_APPS_SRCS, + BIG_SCREEN_APPS, + PASCAL_APPS, +) + +# ---------------------------------------------------------------------------- +# 400, 500, 600 Mini-Floppy (5.25") + +llvmrawprogram( + name="osi400mf_bios", + srcs=["./osi.S"], + deps=["include", + "src/lib+bioslib", + "src/arch/osi/floppy.S", + "src/arch/osi/ascii.S"], + cflags=["-DOSI400"], + linkscript="./osi.ld", +) + +llvmrawprogram( + name="osi500mf_bios", + srcs=["./osi.S"], + deps=["include", + "src/lib+bioslib", + "src/arch/osi/floppy.S", + "src/arch/osi/keyboard.S"], + cflags=["-DOSI500"], + linkscript="./osi.ld", +) + +llvmrawprogram( + name="osi600mf_bios", + srcs=["./osi.S"], + deps=["include", + "src/lib+bioslib", + "src/arch/osi/floppy.S", + "src/arch/osi/keyboard.S"], + cflags=["-DOSI600"], + linkscript="./osi.ld", +) + +mkcpmfs( + name="osi400mf_rawdiskimage", + format="osi5", + bootimage=".+osi400mf_bios", + size=128 * 640, + items={ + "0:ccp.sys@sr": "src+ccp", + "0:bdos.sys@sr": "src/bdos", + } + | MINIMAL_APPS +) + +mkcpmfs( + name="osi500mf_rawdiskimage", + format="osi5", + bootimage=".+osi500mf_bios", + size=128 * 640, + items={ + "0:ccp.sys@sr": "src+ccp", + "0:bdos.sys@sr": "src/bdos", + } + | MINIMAL_APPS +) + +mkcpmfs( + name="osi600mf_rawdiskimage", + format="osi5", + bootimage=".+osi600mf_bios", + size=128 * 640, + items={ + "0:ccp.sys@sr": "src+ccp", + "0:bdos.sys@sr": "src/bdos", + } + | MINIMAL_APPS +) + +mkcpmfs( + name="osimf-b_rawdiskimage", + format="osi5", + size=128 * 640, + items={ + } + | BIG_APPS + | PASCAL_APPS +) + +mkcpmfs( + name="osimf-c_rawdiskimage", + format="osi5", + size=128 * 640, + items={ + } + | MINIMAL_APPS_SRCS + | BIG_APPS_SRCS +) + +mkcpmfs( + name="osimf-d_rawdiskimage", + format="osi5", + size=128 * 640, + items={ + "0:tty540b.com": "src/arch/osi/utils+tty540b", + } + | SCREEN_APPS + | SCREEN_APPS_SRCS + | BIG_SCREEN_APPS +) + +img2os5( + name="osi400mf_diskimage", + src=".+osi400mf_rawdiskimage", +) + +img2os5( + name="osi500mf_diskimage", + src=".+osi500mf_rawdiskimage", +) + +img2os5( + name="osi600mf_diskimage", + src=".+osi600mf_rawdiskimage", +) + +img2os5( + name="osimf-b_diskimage", + src=".+osimf-b_rawdiskimage", +) + +img2os5( + name="osimf-c_diskimage", + src=".+osimf-c_rawdiskimage", +) + +img2os5( + name="osimf-d_diskimage", + src=".+osimf-d_rawdiskimage", +) + +# ---------------------------------------------------------------------------- +# 400, 500, 600, Floppy (8") + +llvmrawprogram( + name="osi400f_bios", + srcs=["./osi.S"], + deps=["include", + "src/lib+bioslib", + "src/arch/osi/floppy.S", + "src/arch/osi/ascii.S"], + cflags=["-DOSI400", "-DFLOPPY8"], + linkscript="./osi.ld", +) + +llvmrawprogram( + name="osi500f_bios", + srcs=["./osi.S"], + deps=["include", + "src/lib+bioslib", + "src/arch/osi/floppy.S", + "src/arch/osi/ascii.S"], + cflags=["-DOSI500", "-DFLOPPY8"], + linkscript="./osi.ld", +) + +llvmrawprogram( + name="osi600f_bios", + srcs=["./osi.S"], + deps=["include", + "src/lib+bioslib", + "src/arch/osi/floppy.S", + "src/arch/osi/ascii.S"], + cflags=["-DOSI600", "-DFLOPPY8"], + linkscript="./osi.ld", +) + +mkcpmfs( + name="osi400f_rawdiskimage", + format="osi8", + bootimage=".+osi400f_bios", + size=128 * 1848, + items={ + "0:ccp.sys@sr": "src+ccp", + "0:bdos.sys@sr": "src/bdos", + } + | MINIMAL_APPS + | BIG_APPS + | PASCAL_APPS + | MINIMAL_APPS_SRCS + | BIG_APPS_SRCS +) + +mkcpmfs( + name="osi500f_rawdiskimage", + format="osi8", + bootimage=".+osi500f_bios", + size=128 * 1848, + items={ + "0:ccp.sys@sr": "src+ccp", + "0:bdos.sys@sr": "src/bdos", + "0:tty540b.com": "src/arch/osi/utils+tty540b", + } + | MINIMAL_APPS + | BIG_APPS + | PASCAL_APPS + | MINIMAL_APPS_SRCS + | BIG_APPS_SRCS + | SCREEN_APPS + | SCREEN_APPS_SRCS + | BIG_SCREEN_APPS +) + +mkcpmfs( + name="osi600f_rawdiskimage", + format="osi8", + bootimage=".+osi600f_bios", + size=128 * 1848, + items={ + "0:ccp.sys@sr": "src+ccp", + "0:bdos.sys@sr": "src/bdos", + } + | MINIMAL_APPS + | BIG_APPS + | PASCAL_APPS + | MINIMAL_APPS_SRCS + | BIG_APPS_SRCS +) + +mkcpmfs( + name="osif-b_rawdiskimage", + format="osi8", + size=128 * 1848, + items={ + } +) + +img2os8( + name="osi400f_diskimage", + src=".+osi400f_rawdiskimage", +) + +img2os8( + name="osi500f_diskimage", + src=".+osi500f_rawdiskimage", +) + +img2os8( + name="osi600f_diskimage", + src=".+osi600f_rawdiskimage", +) + +img2os8( + name="osif-b_diskimage", + src=".+osif-b_rawdiskimage", +) + +# ---------------------------------------------------------------------------- +# Serial system with 8" floppy + +llvmrawprogram( + name="osiserf_bios", + srcs=["./osi.S"], + deps=["include", + "src/lib+bioslib", + "src/arch/osi/floppy.S", + "src/arch/osi/serial.S"], + cflags=["-DOSISERIAL", "-DFLOPPY8"], + linkscript="./osi.ld", +) + +mkcpmfs( + name="osiserf_rawdiskimage", + format="osi8", + bootimage=".+osiserf_bios", + size=128 * 1848, + items={ + "0:ccp.sys@sr": "src+ccp", + "0:bdos.sys@sr": "src/bdos", + "0:scrvt100.com": "src/arch/osi/utils+scrvt100", + } + | MINIMAL_APPS + | BIG_APPS + | PASCAL_APPS + | MINIMAL_APPS_SRCS + | BIG_APPS_SRCS + | SCREEN_APPS + | SCREEN_APPS_SRCS + | BIG_SCREEN_APPS +) + +img2os8( + name="osiserf_diskimage", + src=".+osiserf_rawdiskimage", +) diff --git a/src/arch/osi/floppy.S b/src/arch/osi/floppy.S new file mode 100644 index 00000000..f6f40df2 --- /dev/null +++ b/src/arch/osi/floppy.S @@ -0,0 +1,478 @@ +; Ohio Scientific Instruments floppy routines. +; Copyright © 2024 by Ivo van Poorten +; This file is licensed under the terms of the 2-clause BSD license. Please +; see the COPYING file in the root project directory for the full text. + +DATA_DIRECTION_ACCESS = $04 ; bit 2, 0 active (!) + +ORA = $c000 ; output register A +DDRA = $c000 ; data directrion A +CRA = $c001 ; control register A + +ORB = $c002 ; output register B +DDRB = $c002 ; data direction B +CRB = $c003 ; control register B + +PORTA = $c000 ; alias for ORA +PORTB = $c002 ; alias for ORB + +; ---------------------------------------------------------------------------- + +; PORTA + +DRIVE0_NOT_READY_MASK = 0x01 ; 0 = drive0 reads, 1 = not ready +HEAD_NOT_TRACK0_MASK = 0x02 ; 0 = above, 1 = not above +DRIVE1_NOT_READY_MASK = 0x10 ; 0 = drive1 reads, 1 = not ready +DISK_R_W_MASK = 0x20 ; 0 = protected, 1 = r/w +DRIVE0_SELECT_MASK = 0x40 ; 0 = drive1, 1 = drive0 +NOT_INDEX_HOLE_MASK = 0x80 ; 0 = above hole, 1 = not above + +; PORTB + +READ_FROM_DISK_MASK = 0x01 ; nWRITE: 0 = write, 1 = read +ERASE_ENABLE_MASK = 0x02 ; nERASE: 0 = enabled, 1 = disabled +DIRECTION_MASK = 0x04 ; nSTEPDIR: 0 = to trk 39, 1 = to trk 0 +MOVE_HEAD_MASK = 0x08 ; nSTEP: 1->0 move, 1 = steady +FAULT_RESET_MASK = 0x10 ; nRESET: 0 = reset, 1 = normal +DRIVE_01_23_MASK = 0x20 ; 0 = drive 2/3, 1 = drive 0/1 +LOW_CURRENT_MASK = 0x40 ; mostly 1, 0 on 8" trk >= 44 +HEAD_NOT_ON_DISK_MASK = 0x80 ; nHEADLOAD 0 = on disk, 1 = lifted + +; ---------------------------------------------------------------------------- + +ACIA_CONTROL = $c010 ; control register +ACIA_STATUS = $c010 ; status register +ACIA_TDR = $c011 ; transmit data register +ACIA_RDR = $c011 ; receive data register + +CONTROL_DIV_MASK = 0x03 ; divider 1,16,64,master reset +CONTROL_RESET = 0x03 +CONTROL_WS_MASK = 0x1c ; word select, see below +CONTROL_TX_CTRL = 0x60 ; transmit control bits, see below +CONTROL_RX_IRQE = 0x80 ; receive interrupt enable + +STATUS_RDRF_MASK = 0x01 ; Rx data register full +STATUS_TDRE_MASK = 0x02 ; Tx data register empty +STATUS_nDCD_MASK = 0x04 ; /DCD Data Carrier Detect +STATUS_nCTS_MASK = 0x08 ; /CTS Clear To Send +STATUS_FE_MASK = 0x10 ; Rx Frame Error +STATUS_OVRN_MASK = 0x20 ; Rx Overrun +STATUS_PE_MASK = 0x40 ; Rx Parity Error +STATUS_IRQ_MASK = 0x80 ; /IRQ, if pin output is low, bit is 1 + ; clear by read of RDR + +CONTROL_DIV_1 = 0 +CONTROL_DIV_16 = 1 +CONTROL_DIV_64 = 2 + +WS_SHIFT = 2 + +WS_7E2 = 0x00 +WS_7O2 = 0x01 +WS_7E1 = 0x02 +WS_7O1 = 0x03 +WS_8N2 = 0x04 +WS_8N1 = 0x05 +WS_8E1 = 0x06 +WS_8O1 = 0x07 + +TCB_SHIFT = 5 + +TCB_nRTS_LOW_IRQ_DIS = 0x00 +TCB_nRTS_LOW_IRQ_ENA = 0x01 +TCB_nRTS_HIGH_IRQ_DIS = 0x02 +TCB_nRTS_LOW_BREAK_LVL_IRQ_DIS = 0x03 + +; ---------------------------------------------------------------------------- + +zproc init_pia + ldy #0 + ldx #DATA_DIRECTION_ACCESS + + sty CRA ; select DDRA + lda #$40 + sta DDRA ; set all pins to input, except bit 6 + stx CRA ; select ORA + sta PORTA ; select drive 0 + + sty CRB ; select DDRB + dey ; Y=$ff + sty DDRB ; set all pins to output + stx CRB ; select ORB + sty PORTB ; set all outputs high + + rts +zendproc + +; ---------------------------------------------------------------------------- + +zproc init_acia + lda #CONTROL_RESET + sta ACIA_CONTROL + lda #(TCB_nRTS_HIGH_IRQ_DIS << TCB_SHIFT) | (WS_8E1 << WS_SHIFT) + sta ACIA_CONTROL + + rts +zendproc + +; ---------------------------------------------------------------------------- + +zproc wait_past_index_hole + zloop ; not above hole, might be a no-op if index is + lda PORTA ; already triggered + zuntil_pl + zloop ; above hole + lda PORTA + zuntil_mi + rts ; not above hole +zendproc + +; ---------------------------------------------------------------------------- + +; Head Movement, step in and step out + +zproc step_out ; to edge, track-- + lda PORTB + ora #DIRECTION_MASK + bne step +zendproc + +zproc step_in ; to center hole, track++ + lda PORTB + and #DIRECTION_MASK^0xff + ; [[fallthrough]] +zendproc + +zproc step + sta PORTB + + jsr short_delay + + and #MOVE_HEAD_MASK^0xff + sta PORTB ; 1->0 transition, move + + jsr short_delay + + ora #MOVE_HEAD_MASK + sta PORTB ; back to 1, ready for next transition + + ldx #$18 + bne long_delay_X ; branch always +zendproc + +; SEEK TO TRACK 0 ENTRY POINT + +zproc seek_to_track0 + jsr step_in + + jsr long_delay ; always returns with X=Y=0, and Z=1 + + sty curtrk ; set curtrk to zero + +;keep_moving: + zloop + lda #HEAD_NOT_TRACK0_MASK + bit PORTA ; bit 1 is 0, means track 0 sensor is triggered + zbreakif_eq ; fallthrough to long delay + + jsr step_out ; step_out ends with long_delay_X, hence Z=1 + zuntil_ne ; so this is a branch always + + ; [[fallthrough]] +zendproc + +; Long delays always return with X=Y=0 and Z=1 + +zproc long_delay + ldx #$18 + + ; [[fallthrough]] +zendproc + +zproc long_delay_X + zloop + ldy #$f8 + +_short_delay_X_Y: ; enter here with Y set, and X=1 + zloop + dey ; 2 cycles + zuntil_eq ; 3 cycles if looped, $f8*5-1 = 1239 cycles + + dex ; 2 cycles + zuntil_eq ; 3 cycles if looped, $18*1244-1 = 29855 cycles + + ; [[fallthrough]] +zendproc + +zproc short_delay + rts +zendproc + +; ---------------------------------------------------------------------------- + +zproc put_head_on_disk + lda PORTB + and #HEAD_NOT_ON_DISK_MASK^0xff + + ; [[fallthrough]] +zendproc + +zproc move_head + sta PORTB + ldx #$28 + jmp long_delay_X +zendproc + +zproc lift_head_from_disk + lda PORTB + ora #HEAD_NOT_ON_DISK_MASK + bne move_head +zendproc + +; ---------------------------------------------------------------------------- + +zproc enable_write_and_erase + lda PORTB + and #(READ_FROM_DISK_MASK | ERASE_ENABLE_MASK)^0xff + ; [[fallthrough]] +zendproc + +zproc common_write_and_erase + sta PORTB + rts +zendproc + +zproc disable_write_and_erase + lda PORTB + ora #(READ_FROM_DISK_MASK | ERASE_ENABLE_MASK) + bne common_write_and_erase +zendproc + +; ---------------------------------------------------------------------------- + +zproc tobcd + ldx #0 + zloop + cmp #10 + zbreakif_cc + + inx + + ; carry is always set + sbc #10 + zuntil_cc + + sta trkbcd + + txa + asl + asl + asl + asl + +; carry is always clear + adc trkbcd + sta trkbcd + + rts +zendproc + +; ---------------------------------------------------------------------------- + +zproc read_byte ; Returns result from disk in A + zloop + lda ACIA_STATUS ; wait for STATUS_RDRF_MASK (0x01), RDR full + lsr + zuntil_cs + + lda ACIA_RDR + rts +zendproc + +zproc write_byte ; Write byte in X to disk + zloop + lda ACIA_STATUS ; wait for STATUS_TDRE_MASK (0x02), TDR empty + lsr + lsr + zuntil_cs + + stx ACIA_TDR + rts +zendproc + +; ---------------------------------------------------------------------------- + +; Common code for reading and writing a track. Exit code in Z(!) +; The following code initializes the "controller", reads the +; track marker and verifies the track number. + +zproc read_write_track_common_intro + lda curtrk + jsr tobcd + + lda #0 + sta ptr + lda ptrkbuf + sta ptr+1 + + jsr put_head_on_disk + jsr wait_past_index_hole + jsr init_acia + + jsr read_byte + cmp #$43 + bne common_error_out + + jsr read_byte + cmp #$57 + bne common_error_out + + jsr read_byte + cmp trkbcd + bne common_error_out + + jsr read_byte + cmp #$58 + +common_error_out: + rts +zendproc + +zproc read_track + jsr read_write_track_common_intro + bne read_error_out + + jsr read_byte + cmp #$76 + bne read_error_out + + jsr read_byte + cmp #$01 + bne read_error_out + + jsr read_byte + cmp #PAGES_PER_TRACK + bne read_error_out + + tax ; X=8 or X=12, read X pages + + ldy #0 + zloop + zloop + jsr read_byte + sta (ptr),y + iny + zuntil_eq + + inc ptr+1 + dex + zuntil_eq + +read_ok_out: +write_ok_out: + jsr lift_head_from_disk + clc + rts + +read_error_out: +write_error_out: + jsr lift_head_from_disk + sec + rts +zendproc + +zproc write_track + jsr read_write_track_common_intro + bne write_error_out + + ldx #1 + ldy #$10 + jsr _short_delay_X_Y + +#ifdef FLOPPY8 + lda curtrk + cmp #44 + zif_cs + lda PORTB + and #LOW_CURRENT_MASK^0xff + sta PORTB + zendif +#endif + + jsr enable_write_and_erase + + ldx #1 + ldy #$f0 + jsr _short_delay_X_Y + + ldx #$76 + jsr write_byte + + ldx #$01 + jsr write_byte + + ldx #PAGES_PER_TRACK + stx save_x ; X pages counter for later + jsr write_byte + + ldy #0 + zloop + zloop + lda (ptr),y + tax + jsr write_byte + iny + zuntil_eq + + inc ptr+1 + dec save_x + zuntil_eq + + ldx #$47 + jsr write_byte + + ldx #$53 + jsr write_byte + + zloop ; wait until index hole + lda PORTA + zuntil_pl + + jsr disable_write_and_erase + +#ifdef FLOPPY8 + lda PORTB + ora #LOW_CURRENT_MASK + sta PORTB +#endif + + jmp write_ok_out +zendproc + +; Pass drive number in A (0-3) + +zproc select_drive_number + lda drive_number + and #1 + zif_eq + lda #DRIVE0_SELECT_MASK + zelse + lda #0 + zendif + sta PORTA + + lda drive_number + and #2 + zif_eq + lda PORTB + ora #DRIVE_01_23_MASK + zelse + lda PORTB + and #DRIVE_01_23_MASK^$ff + zendif + sta PORTB + + rts +zendproc + +; vim: filetype=asm sw=4 ts=4 et diff --git a/src/arch/osi/keyboard.S b/src/arch/osi/keyboard.S new file mode 100644 index 00000000..00a4d278 --- /dev/null +++ b/src/arch/osi/keyboard.S @@ -0,0 +1,204 @@ +; Ohio Scientific Instruments polled keyboard routines. +; Copyright © 2024 by Ivo van Poorten +; This file is licensed under the terms of the 2-clause BSD license. Please +; see the COPYING file in the root project directory for the full text. + +ZEROPAGE + +cur_char: .fill 1 +wait_cntr: .fill 1 +last_char: .fill 1 +modifiers: .fill 1 +tmpval: .fill 1 + +KEYBD = $df00 + +#ifdef OSI500 + .macro STA_KEYBD + sta KEYBD + .endm + .macro LDA_KEYBD + lda KEYBD + .endm +#endif +#ifdef OSI600 + .macro STA_KEYBD + eor #$ff + sta KEYBD + eor #$ff + .endm + .macro LDA_KEYBD + lda KEYBD + eor #$ff + .endm +#endif + +; ---------------------------------------------------------------------------- + + .section .text, "ax" + +scan_keyboard: + +scan_again: + lda #1 ; Row mask + ldy #0 ; Row counter + sty modifiers + dey + + zloop + STA_KEYBD + + pha + LDA_KEYBD + tax ; Key pressed in X + pla + + cpx #0 + bne key_pressed + + next: + iny ; Increase row counter + asl + zuntil_eq + + sta last_char ; no key pressed... + sta cur_char + lda #$80 + rts + +key_pressed: + lsr ; check if row 0 ($01) + + zif_cs ; modifier key(s) or ESC pressed + rol ; correct back to $01 + stx modifiers ; save modifiers + + pha + txa + and #$20 + tax + pla + cpx #$20 + bne next + + lda #$1b ; ESC + bne lookup_done + zendif + + lda matrix_index_tab,y + sta tmpval ; store "corrected" row*7 + + txa ; A=column + ldy #7 ; convert to index 0-7 + zloop + dey + asl + zuntil_cs + + tya ; A=column + adc tmpval ; add saved row*7 + 1 (carry always set) + tay + + lda keyboard_matrix-1,y ; retrieve ASCII from table (-1 compensates C) + +lookup_done: + cmp cur_char + zif_ne + sta cur_char + lda #2 + sta wait_cntr + bne scan_again ; branch always + zendif + + dec wait_cntr + beq debounce_done + + ldy #$10 + zloop + ldx #$40 + zloop + dex + zuntil_eq + dey + zuntil_eq + + beq scan_again + +debounce_done: + ldx #$64 ; long delay on first character + cmp last_char + + zif_eq + ldx #$0f ; shorter repeat rate + zendif + + stx wait_cntr + sta last_char + sta tmpval + +; Apply key modifiers -------------------------------------------------------- + + lda modifiers + tay ; save modifiers in Y + + and #7 ; shift or caps + tax + zif_ne ; handle shift or caps + lda tmpval + cmp #$7f ; RUB, case modifiers have no effect + beq getkey_done + + cmp #$61 ; >= 0x61 always toupper() + zif_cs + eor #$20 + bne case_adjust_done + zendif + + cpx #1 ; just CAPS? + beq case_adjust_done ; for < 0x60, CAPS has no effect + + cmp #$30 ; special case, add #$10 + zif_eq + clc + adc #$10 + bne case_adjust_done + zendif + + cmp #$21 ; don't adjust space (0x20) and below + zif_cs + eor #$10 ; all other keys with lshift or rshift + zendif + + case_adjust_done: + sta tmpval + zendif + + tya ; restore saved modifiers + and #$40 ; control key? + zif_ne + lda tmpval + and #$1f + rts + zendif + +getkey_done: + lda tmpval + rts + +; ---------------------------------------------------------------------------- + +keyboard_matrix: + .ascii "p;/ zaq" + .ascii ",mnbvcx" + .ascii "kjhgfds" + .ascii "iuytrew" + .byte $0d,$0a ; removed unused columns + .ascii "ol." + .byte $7F ; idem (RUB as $7f instead of $5f) + .ascii "-:098" + .ascii "7654321" + +matrix_index_tab: + .byte 0, 7, 14, 21, 26, 32, 39 ; corrected indeces for removed unused keys + +; vim: filetype=asm sw=4 ts=4 et diff --git a/src/arch/osi/osi.S b/src/arch/osi/osi.S new file mode 100644 index 00000000..161bf2d5 --- /dev/null +++ b/src/arch/osi/osi.S @@ -0,0 +1,821 @@ +; CP/M-65 Copyright © 2022 David Given +; This file is licensed under the terms of the 2-clause BSD license. Please +; see the COPYING file in the root project directory for the full text. + +; Ohio Scientific Instruments Port, Copyright © 2024 Ivo van Poorten +; +; - Minimum of 16 kB RAM (ramtop at $3fff) +; - BIOS init loads at $2200. Could be lower, but some older ROMs ignore +; the address in the boot sector and have jmp $2200 hardcoded. +; - Floppy drive interface at $c000 +; - 400 series: +; + Model 440 video 32x32 +; + 65V+65F ROM with ASCII keyboard @ $df01 +; - 500 series: +; + Model 540 video 64x32 +; + Model 542 polled keyboard @ $df00 +; - 600 series: +; + Model 600 polled keyboard @ $df00, and 64x16 video + +#include "zif.inc" +#include "cpm65.inc" +#include "driver.inc" +#include "jumptables.inc" + +ZEROPAGE + +; We have all of page zero, which is a lot compared to several other ports. +; Hence, all variables are here. This results in smaller and faster code, too. +; For example, we can use (dma),y directly instead of first copying it to +; a ptr. + +.global ptr, ptr1 + +ptr: .fill 2 +ptr1: .fill 2 + +mem_end: .fill 1 +ptrkbuf: .fill 1 ; MSB only, LSB=0 + +save_x: .fill 1 +save_y: .fill 1 + +#ifndef OSISERIAL +curx: .fill 1 +cury: .fill 1 + +key_pending:.fill 1 +#endif + +trkbcd: .fill 1 +curtrk: .fill 1 +curdrv: .fill 1 +reqtrk: .fill 1 +reqsec: .fill 1 +retry: .fill 1 + +drive_number: .fill 1 +sector_num: .fill 3 +dma: .fill 2 + +#if OSI400 + SCREEN_WIDTH = 32 + SCREEN_HEIGHT = 32 + SCREEN_PAGES = 4 +#elif OSI500 + SCREEN_WIDTH = 64 + SCREEN_HEIGHT = 32 + SCREEN_PAGES = 8 + CONTROL = $de00 +#elif OSI600 + SCREEN_WIDTH = 64 + SCREEN_HEIGHT = 16 + SCREEN_PAGES = 4 + CONTROL = $d800 +#endif + +#ifdef FLOPPY8 +PAGES_PER_TRACK = 12 +#else +PAGES_PER_TRACK = 8 +#endif + +SCREENMEM = $d000 + +BDOS = __USERTPA_START__ + COMHDR_ENTRY + + .text + +.global _start + +.section loader, "ax" + +_start: + +init: + ldx #$ff + txs + + ldy #0 + sty ptr + +#ifndef OSISERIAL + sty curx + sty cury + + lda #>SCREENMEM + sta ptr+1 + ldx #SCREEN_PAGES + lda #' ' + + zloop ; clear screen + zloop + sta (ptr),y + dey + zuntil_eq + inc ptr+1 + dex + zuntil_eq +#endif + +#if OSI500 | OSI600 + iny ; Y=1 + sty CONTROL ; switch to 64x32 (500 series) or 64x16 (600 series) +#endif + + ; Copy BIOS to lower RAM + + zrepeat + memload = .+1 + lda bios_load_addr + memstore = .+1 + sta bios_exec_addr + + inc memload + zif_eq + inc memload+1 + zendif + + inc memstore + zif_eq + inc memstore+1 + zendif + + lda memstore + cmp #bios_end_addr + zuntil_eq + + ; Determine memory size. $0000-$3fff is assumed RAM (the 16kB minimum). + ; Systems without BASIC can have RAM at $a000-$bfff. Floppy controller + ; starts at $c000 + + ; ptr is still zero + + lda #$3f + sta ptr+1 + + zloop + inc ptr+1 + lda ptr+1 + cmp #$c0 + zbreakif_eq + sta (ptr),y + cmp (ptr),y + zuntil_ne + + sec + sbc #PAGES_PER_TRACK + sta ptrkbuf ; put track buffer at ramtop + sta mem_end + + jsr init_pia + jsr init_acia +#if OSISERIAL + jsr init_serial +#endif + jsr initdrivers ; BIOS initialisation + + ldy #banner_end - banner + zrepeat ; Print banner + lda banner-1,y + sty save_y + jsr tty_conout + ldy save_y + dey + zuntil_eq + + ldx #$ff + stx curtrk + inx + stx curdrv + + lda #bdos_filename + ldy #>__USERTPA_START__ + jsr loadfile ; Load the BDOS image + + lda #>__USERTPA_START__ + ldx #__USERZEROPAGE_START__ + jsr bios_RELOCATE + + lda #biosentry + jmp BDOS ; run! + +banner: ; reversed + .byte 10,13 + .ascii "cifitneicS oihO eht rof 56-M/PC" +#if OSISERIAL + .byte 10,13 +#endif +banner_end: + +bdos_filename: + .ascii "BDOS SYS" + +; --- BIOS entrypoints ------------------------------------------------------ + +.data + +.global drvtop +drvtop: .word drv_TTY + +#ifndef OSISERIAL +defdriver TTY, DRVID_TTY, drvstrat_TTY, 0 +#else +defdriver TTY, DRVID_TTY, drvstrat_TTY, drv_SERIAL +#endif + +; TTY driver strategy routine. +; Y=TTY opcode. +zproc drvstrat_TTY + + cpy #0 + beq tty_const + dey + beq tty_conin + bne tty_conout + +; Memory is getting tight. The above dispatcher is less code than the +; official jmpdispatcher. +; +; jmpdispatch jmptable_lo, jmptable_hi +; +;jmptable_lo: +; jmptablo tty_const +; jmptablo tty_conin +; jmptablo tty_conout +;jmptable_hi: +; jmptabhi tty_const +; jmptabhi tty_conin +; jmptabhi tty_conout +;zendproc + +; --- TTY DRIVER ------------------------------------------------------------ + +#if OSISERIAL +#include "serial.S" +#else + +; Blocks and waits for the next keypress; returns it in A. + +zproc tty_conin + lda key_pending + + ldx #$80 ; clear pending key + stx key_pending + + cmp #$80 + zif_cc + rts + zendif + + jsr show_cursor + + zloop + jsr scan_keyboard + cmp #$80 ; works for both polled and ASCII keyboards + zuntil_cc ; < $80, valid key + + pha + jsr hide_cursor + pla + rts +zendproc + +; Return 0 if no key is pending, $ff if there is + +zproc tty_const + jsr scan_keyboard + sta key_pending + cmp #$80 + zif_cc + lda #$ff ; < $80, valid key + zelse + lda #0 + zendif + rts +zendproc + +; cursor char: 0xa1 = white square, 0xbb = checkerboard, 0x80 = underline +; 0x5f = underscore (works with any font ROM) + +zproc show_cursor + lda #$5f + pha + bne do_cursor +zendproc + +zproc hide_cursor + lda #' ' + pha + ; [[fallthrough]] +zendproc + +zproc do_cursor + jsr calculate_cursor_address + ldy #0 + pla + sta (ptr),y + rts +zendproc + +; ---------------------------------------------------------------------------- + +; Output character in A + +zproc tty_conout + cmp #13 + zif_eq + lda #0 + sta curx + rts + zendif + cmp #10 + zif_eq +increase_cury: + inc cury + lda cury + cmp #SCREEN_HEIGHT + zif_eq + dec cury + jsr scroll_up + zendif + rts + zendif + cmp #127 + zif_eq + dec curx + zif_mi + lda #SCREEN_WIDTH-1 + sta curx + + dec cury + zif_mi + lda #0 + sta cury + zendif + zendif + jsr calculate_cursor_address + lda #' ' + sta (ptr),y + rts + zendif + + pha + jsr calculate_cursor_address + pla + + ldy #0 + sta (ptr),y + + inc curx + lda curx + cmp #SCREEN_WIDTH + zif_eq + sty curx ; Y = 0 + beq increase_cury + zendif + + rts +zendproc + +; Set cursor address in ptr + +zproc calculate_cursor_address + ; multiply cury by 64 --> * 256 / 4 + lda #0 + sta ptr + lda cury + lsr + ror ptr + lsr + ror ptr +#if OSI400 + lsr ; multiply cury by 32 --> * 256 / 8 + ror ptr +#endif + adc #>SCREENMEM ; only add MSB because SCREENMEM is page aligned + sta ptr+1 + + ; add curx + lda ptr +; clc ; C = 0 because of ror and previous adc + adc curx + sta ptr + zif_cs + inc ptr+1 + zendif + + rts +zendproc + +zproc scroll_up + lda #<(SCREENMEM) + sta ptr + lda #<(SCREENMEM+SCREEN_WIDTH) + sta ptr1 + lda #>SCREENMEM + sta ptr+1 + sta ptr1+1 + + ldy #0 + zloop ; Copy all lines one up + lda (ptr1),y + sta (ptr),y + inc ptr + zif_eq + inc ptr+1 + zendif + inc ptr1 + zif_eq + inc ptr1+1 + zendif + lda ptr1+1 + cmp #>(SCREENMEM+SCREEN_PAGES*256) + zuntil_eq + + lda #' ' + zloop ; Clear last line + sta (ptr),y + iny + cpy #SCREEN_WIDTH + zuntil_eq + + rts +zendproc + +#endif + +; --------------------------------------------------------------------------- + +; Sets the current DMA address. + +zproc bios_SETDMA + sta dma+0 + stx dma+1 + rts +zendproc + +; Select a disk. +; A is the disk number. +; Returns the DPH in XA. +; Sets carry on error. + +zproc bios_SELDSK + cmp #0 + zif_eq + sta drive_number + lda #dph_a + clc + rts + zendif + cmp #1 + zif_eq + sta drive_number + lda #dph_b + clc + rts + zendif +#ifndef FLOPPY8 + cmp #2 + zif_eq + sta drive_number + lda #dph_c + clc + rts + zendif + cmp #3 + zif_eq + sta drive_number + lda #dph_d + clc + rts + zendif +#endif + + sec + rts +zendproc + +; Set the current absolute sector number. +; XA is a pointer to a three-byte number. +; BDOS looks at DPH to see how many sectors to skip. + +zproc bios_SETSEC + sta ptr+0 + stx ptr+1 + ldy #2 + zrepeat + lda (ptr), y + sta sector_num, y + dey + zuntil_mi + rts +zendproc + +; --------------------------------------------------------------------------- + +; Decided against a custom 128-byte per sector format, because it's a pain to +; maintain, it's difficult to support different CPU speeds at the same time, +; and writing several sectors during one rotation is nigh impossible if you +; do not want to accidentally overwrite part of the previous or next sector. +; You also need sector skew, and it would be a lot of code which won't fit in +; the boot sector. +; +; Hence the format is as follows: +; +; track 0 Boot ROM boot sector, 2048 bytes +; track 1-39 OS65D compatible track marker and 1 sector of 8 pages (2048B) +; or +; track 1-76 OS65D compatible track marker and 1 sector of 12 pages (3072B) +; +; Tracks are always read or written to/from trkbuf in one go. +; This means that: +; a) Reading is very fast because full tracks are buffered. Multiple +; sectors from the same track will just be memcopies. +; b) Writing a sector to the current track takes one rotation. +; c) Writing a sector to a different track takes two rotations +; (one for reading the track, one for writing the modified track). +; d) Disks can be copied with standard OS65D disk copier, even though +; there is no OS65D directory. +; e) Works out-of-the-box for every CPU speed (0.98Mhz-2MHz) +; f) Code is small and easy to understand (BIOS+init needs to be less +; than 2kB and memory is scarce). + +; --------------------------------------------------------------------------- + +#ifndef FLOPPY8 + +; If we run out of memory again, calculate_reqtrk_and_reqsec can be inlined +; to save another four bytes. There's only one caller. + +; 5.25" Mini-Floppy +; reqtrk = sector_num / 16, remainder in reqsec + +zproc calculate_reqtrk_and_reqsec + lda sector_num + sta reqtrk + and #$0f + sta reqsec + + lda sector_num+1 + + ldx #3 + zloop ; loop is smaller than unrolled + lsr + ror reqtrk + dex + zuntil_mi + + rts +zendproc + +#else + +; 8" Floppy +; reqtrk = sector_num / 24, remainder in reqsec + +zproc calculate_reqtrk_and_reqsec + lda sector_num + sta reqtrk + lda sector_num+1 + sta ptr1+1 ; temporary msb of reqtrk + + lda #0 ; initialize remainder to zero + sta reqsec + sta ptr1 ; temporary msb of remainder + ldx #15 + +divloop: + asl reqtrk ; shift hi bit of reqtrk into remainder + rol ptr1+1 ; (vacating the lo bit for the quotient) + rol reqsec + rol ptr1 + + lda reqsec + sec ; trial subtraction + sbc #24 + tay + lda ptr1 + sbc #0 + bcc failed ; did subtraction succeed? + + sta ptr1 ; if yes, save it + sty reqsec + inc reqtrk ; and record a 1 in the quotient + +failed: + dex + bpl divloop + + rts +zendproc + +#endif + +zproc calculate_trkbuf_sector_location ; and set Y to zero! + lda reqsec ; * 128 --> * 256 / 2 + lsr + sta ptr+1 + lda #0 + tay + ror + sta ptr + lda ptr+1 + adc ptrkbuf + sta ptr+1 + + rts +zendproc + +zproc fill_trkbuf + jsr calculate_reqtrk_and_reqsec + + lda drive_number + cmp curdrv + zif_ne + sta curdrv + lda #$ff + sta curtrk + zendif + + jsr select_drive_number + + ; check at which track we are + + lda #3 + sta retry + +do_retry: + lda curtrk + cmp reqtrk + zif_ne + cmp #$ff + zif_eq + jsr seek_to_track0 + zendif + ; seek track, step in/out + lda curtrk + cmp reqtrk + zif_cc ; we need to step in + lda reqtrk + sec + sbc curtrk + tay + zloop + sty save_y + jsr step_in + ldy save_y + dey + zuntil_eq + zelse ; we need to step out + lda curtrk + sec + sbc reqtrk + tay + zloop + sty save_y + jsr step_out + ldy save_y + dey + zuntil_eq + zendif + lda reqtrk + sta curtrk + + jsr read_track + zif_cs + lda #$ff ; force resync on retry + sta curtrk + dec retry + bne do_retry + sec + rts + zendif + zendif + clc + rts +zendproc + +zproc bios_READ + jsr fill_trkbuf + + zif_cs + rts + zendif + + jsr calculate_trkbuf_sector_location ; and set Y to zero + + zloop ; copy sector from trkbuf to (dma) + lda (ptr),y + sta (dma),y + iny + zuntil_mi + + clc + rts +zendproc + +zproc bios_WRITE + jsr fill_trkbuf ; no-op if current track + + zif_cs + rts + zendif + + jsr calculate_trkbuf_sector_location ; and set Y to zero + + zloop ; copy sector from (dma) to trkbuf + lda (dma),y + sta (ptr),y + iny + zuntil_mi + + jsr write_track ; returns status in C + + rts ; ...and so do we +zendproc + +; --------------------------------------------------------------------------- + +zproc bios_GETTPA + lda mem_base + ldx mem_end + rts +zendproc + +zproc bios_SETTPA + sta mem_base + stx mem_end + rts +zendproc + +zproc bios_GETZP + lda zp_base + ldx zp_end + rts +zendproc + +zproc bios_SETZP + sta zp_base + stx zp_end + rts +zendproc + +zproc bios_SETBANK + rts +zendproc + +; --------------------------------------------------------------------------- + +#if OSI400 +#include "ascii.S" +#endif +#if OSI500 | OSI600 +#include "keyboard.S" +#endif + +#include "floppy.S" + +; --------------------------------------------------------------------------- + + .data + +zp_base: .byte __USERZEROPAGE_START__ +zp_end: .byte __USERZEROPAGE_END__ + +mem_base: .byte __USERTPA_START__@mos16hi +; mem_end moved to page zero + +; DPH for all drives + +; number of sectors, blocksize, direntries, reserved _sectors_ + +#ifndef FLOPPY8 +define_drive dph_a, 640, 1024, 64, 16 +define_drive dph_b, 640, 1024, 64, 16 +define_drive dph_c, 640, 1024, 64, 16 +define_drive dph_d, 640, 1024, 64, 16 +#else +define_drive dph_a, 1848, 1024, 64, 24 +define_drive dph_b, 1848, 1024, 64, 24 +#endif + + .section .noinit, "ax", @nobits + + .global directory_buffer + +directory_buffer: .fill 128 + +; --------------------------------------------------------------------------- + +; vim: filetype=asm sw=4 ts=4 et diff --git a/src/arch/osi/osi.ld b/src/arch/osi/osi.ld new file mode 100644 index 00000000..8f5c1195 --- /dev/null +++ b/src/arch/osi/osi.ld @@ -0,0 +1,46 @@ +/* + * Ohio Scientific Instruments Linker Script + */ + +MEMORY { + zp : ORIGIN = 0x00, LENGTH = 0xff + ram (rw) : ORIGIN = 0x0200, LENGTH = 0x9e00 + loader(rw) : ORIGIN = 0x2200, LENGTH = 0x0800 +} + +SECTIONS { + .zp : { + *(.zp .zp.*) + __USERZEROPAGE_START__ = .; + __USERZEROPAGE_END__ = 0xff; + + . = 0xf0; + *(.loaderzp) + } >zp + + .loader : { + *(loader) + *loader.o(.text .text.*) + } >loader + + .text : { + bios_exec_addr = .; + bios_load_addr = LOADADDR(.text); + *(.text .text.*) + } >ram AT>loader + + .data : { + *(.data .data.* .rodata .rodata.*) + bios_end_addr = .; + } >ram AT>loader + + .noinit (NOLOAD) : { + *(.noinit .noinit.*) + . = ALIGN(256); + __USERTPA_START__ = .; + } >ram +} + +OUTPUT_FORMAT { + TRIM(loader) +} diff --git a/src/arch/osi/serial.S b/src/arch/osi/serial.S new file mode 100644 index 00000000..633644da --- /dev/null +++ b/src/arch/osi/serial.S @@ -0,0 +1,147 @@ +; Ohio Scientific Instruments serial tty routines. +; Copyright © 2024 by Ivo van Poorten +; This file is licensed under the terms of the 2-clause BSD license. Please +; see the COPYING file in the root project directory for the full text. + +SERIAL_ACIA_CONTROL = $fc00 +SERIAL_ACIA_STATUS = $fc00 +SERIAL_ACIA_RDR = $fc01 +SERIAL_ACIA_TDR = $fc01 + +; ---------------------------------------------------------------------------- + +zproc init_serial + jmp serial_open +zendproc + +zproc tty_conin + jmp serial_in +zendproc + +zproc tty_const + lda SERIAL_ACIA_STATUS + lsr + zif_cc + lda #0 ; no key + zelse + lda #$ff ; key is pending + zendif + rts +zendproc + +zproc tty_conout + cmp #127 ; workaround for now, see issue #148 + zif_eq ; and PR #138 + lda #8 + jsr serial_out + lda #' ' + jsr serial_out + lda #8 + zendif + jmp serial_out +zendproc + +; ---------------------------------------------------------------------------- + +; RAW SERIAL DRIVER + +defdriver SERIAL, DRVID_SERIAL, drvstrat_SERIAL, 0 + +zproc drvstrat_SERIAL + jmpdispatch drv_serial_jump_lo, drv_serial_jump_hi + +drv_serial_jump_lo: + jmptablo serial_inp + jmptablo serial_out + jmptablo serial_open + jmptablo serial_close + jmptablo serial_outp + jmptablo serial_in + +drv_serial_jump_hi: + jmptabhi serial_inp + jmptabhi serial_out + jmptabhi serial_open + jmptabhi serial_close + jmptabhi serial_outp + jmptabhi serial_in +zendproc + +; Non-Blocking I/O +; ---------------- + +; exit: C if no char pending, !C return char in A +zproc serial_inp + lda SERIAL_ACIA_STATUS + lsr + zif_cc ; no char is pending + sec + rts + zendif + + lda SERIAL_ACIA_RDR + clc + rts +zendproc + +; exit: C if unable to send, !C A is sent +zproc serial_outp + pha + lda SERIAL_ACIA_STATUS + lsr + lsr + zif_cc ; TDR is not empty + pla + sec + rts + zendif + + pla + sta SERIAL_ACIA_TDR + clc + rts +zendproc + +; Blocking I/O +; ------------ + +zproc serial_in + zrepeat + lda SERIAL_ACIA_STATUS ; wait for bit 0 set, RDR full + lsr + zuntil_cs + + lda SERIAL_ACIA_RDR + rts +zendproc + +zproc serial_out + pha + zrepeat + lda SERIAL_ACIA_STATUS ; wait for bit 1 set, TDR empty + lsr + lsr + zuntil_cs + pla + sta SERIAL_ACIA_TDR + rts +zendproc + +; Open/Close + +zproc serial_open + lda #CONTROL_RESET + sta SERIAL_ACIA_CONTROL + ; div1 --> 4800 baud default, up to 19200 with faster base clock + lda #(TCB_nRTS_HIGH_IRQ_DIS << TCB_SHIFT) | (WS_8N2 << WS_SHIFT) + sta SERIAL_ACIA_CONTROL + rts +zendproc + +zproc serial_close + rts +zendproc + +; ------------------------------------------------------------------------- + +; vim: filetype=asm sw=4 ts=4 et diff --git a/src/arch/osi/utils/build.py b/src/arch/osi/utils/build.py new file mode 100644 index 00000000..3749be20 --- /dev/null +++ b/src/arch/osi/utils/build.py @@ -0,0 +1,4 @@ +from build.llvm import llvmprogram + +llvmprogram( name="tty540b", srcs=["./tty540b.S"], deps=[ "include", ],) +llvmprogram( name="scrvt100", srcs=["./scrvt100.S"], deps=[ "include", ],) diff --git a/src/arch/osi/utils/scrvt100.S b/src/arch/osi/utils/scrvt100.S new file mode 100644 index 00000000..3c477069 --- /dev/null +++ b/src/arch/osi/utils/scrvt100.S @@ -0,0 +1,371 @@ +; ------------------------------------------------------------------------- +; +; VT100 Serial Screen Driver for Ohio Scientific serial systems +; +; Copyright © 2024 Ivo van Poorten +; This file is licensed under the terms of the 2-clause BSD license. Please +; see the COPYING file in the root project directory for the full text. +; +; ------------------------------------------------------------------------- + +#include "zif.inc" +#include "cpm65.inc" +#include "driver.inc" +#include "jumptables.inc" + +ZEROPAGE + +drv_zp_begin: + +ptr: .fill 2 +val: .fill 1 + +drv_zp_end: + +; ------------------------------------------------------------------------- + +zproc main + jmp init +zendproc + +; ------------------------------------------------------------------------- + +defdriver SCREEN, DRVID_SCREEN, drv_screen_strat, 0 + +zproc drv_screen_strat + jmpdispatch drv_screen_jump_lo, drv_screen_jump_hi + +drv_screen_jump_lo: + jmptablo screen_version + jmptablo screen_getsize + jmptablo screen_clear + jmptablo screen_setcursor + jmptablo screen_getcursor + jmptablo screen_putchar + jmptablo screen_putstring + jmptablo screen_getchar + jmptablo screen_showcursor + jmptablo screen_scrollup + jmptablo screen_scrolldown + jmptablo screen_cleartoeol + jmptablo screen_setstyle + +drv_screen_jump_hi: + jmptabhi screen_version + jmptabhi screen_getsize + jmptabhi screen_clear + jmptabhi screen_setcursor + jmptabhi screen_getcursor + jmptabhi screen_putchar + jmptabhi screen_putstring + jmptabhi screen_getchar + jmptabhi screen_showcursor + jmptabhi screen_scrollup + jmptabhi screen_scrolldown + jmptabhi screen_cleartoeol + jmptabhi screen_setstyle + +zendproc + +; ------------------------------------------------------------------------- + +zproc init + ldy #BDOS_GET_BIOS + jsr BDOS + sta BIOS+1 + stx BIOS+2 + +; register new driver + + lda #drv_SCREEN + ldy #BIOS_ADDDRV + jsr BIOS + +; claim memory + + ldy #BIOS_GETTPA + jsr BIOS + lda #>(reserve_tpa_end+256) + ldy #BIOS_SETTPA + jsr BIOS + + ldy #BIOS_GETZP + jsr BIOS + clc + adc #drv_zp_end-drv_zp_begin + ldy #BIOS_SETZP + jsr BIOS + +; display banner + + lda #banner + ldy #BDOS_WRITE_STRING + jsr BDOS + + rts +zendproc + +; ------------------------------------------------------------------------- + +zproc screen_version + lda #0 + rts +zendproc + +zproc screen_getsize + lda #80-1 + ldx #24-1 + rts +zendproc + +zproc screen_clear + lda #clshome + jmp screen_putstring +zendproc + +zproc screen_cleartoeol + lda #cleartoeol + jmp screen_putstring +zendproc + +; entry: A=number [0-79], exit: Y=decades A=remainder, YA [1-80], X unaffected +zproc _convert_to_screen_decimal + ldy #0 + clc + adc #1 + zloop + cmp #10 + zbreakif_cc + sbc #10 + iny + zendloop + rts +zendproc + +; A = xpos [0-width), X = ypos [0-height) --> ^[ [ ypos+1 ; xpos+1 f +zproc screen_setcursor + jsr _convert_to_screen_decimal + ora #$30 + sta setcursor+6 + tya + ora #$30 + sta setcursor+5 + + txa + jsr _convert_to_screen_decimal + ora #$30 + sta setcursor+3 + tya + ora #$30 + sta setcursor+2 + + ldx #0 + zrepeat + lda setcursor,x + jsr screen_putchar + inx + cpx #8 + zuntil_eq + rts +zendproc + +; entry: A=terminator character, exit: X=value-1 +zproc _parse_decimal + sta 1f+1 + lda #0 + sta val + zloop + jsr screen_getchar +1: + cmp #';' + zbreakif_eq + pha + + ldy #9 + lda val + clc + zrepeat + adc val + dey + zuntil_eq + sta val + + pla + sec + sbc #$30 + clc + adc val + sta val + zendloop + ldx val + dex + rts +zendproc + +; exit: A = xpos [0-width), X = ypos [0-height] +zproc screen_getcursor + lda #getcursor + jsr screen_putstring + +; parse result: ^[ [ vertical ; horizontal R + + zloop + jsr screen_getchar + cmp #27 + zuntil_eq + + jsr screen_getchar + cmp #'[' + zif_eq + lda #';' + jsr _parse_decimal + stx 2f+1 + lda #'R' + jsr _parse_decimal + stx 1f+1 + zendif + +1: + lda #0 +2: + ldx #0 + rts +zendproc + +zproc screen_setstyle + cmp #STYLE_REVERSE + zif_eq + lda #'7' + zelse + lda #'0' + zendif + sta style+2 + lda #