From d5fcd1c93445f00c24c7b4c3d891a7ded1d4a409 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 10 Jan 2019 13:13:03 +0000 Subject: [PATCH] cromemco: switch to proper IRQ driven serial bufers Not yet tested --- Kernel/platform-cromemco/Makefile | 2 + Kernel/platform-cromemco/commonmem.s | 4 - Kernel/platform-cromemco/crt0.s | 11 ++ Kernel/platform-cromemco/devtty.c | 72 +++++---- Kernel/platform-cromemco/devtty.h | 26 ++- Kernel/platform-cromemco/discard.c | 7 +- Kernel/platform-cromemco/fuzix.lnk | 4 +- Kernel/platform-cromemco/irq.h | 6 +- Kernel/platform-cromemco/main.c | 4 +- Kernel/platform-cromemco/tuart.s | 226 +++++++++++++++++++++++++++ Kernel/platform-cromemco/vector.s | 61 ++------ 11 files changed, 330 insertions(+), 93 deletions(-) create mode 100644 Kernel/platform-cromemco/tuart.s diff --git a/Kernel/platform-cromemco/Makefile b/Kernel/platform-cromemco/Makefile index 6adaab76..1c97f8b9 100644 --- a/Kernel/platform-cromemco/Makefile +++ b/Kernel/platform-cromemco/Makefile @@ -21,6 +21,8 @@ $(COBJS): %.rel: %.c $(CDOBJS): %.rel: %.c $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< +vector.rel: tuart.s + clean: rm -f *.rel *.lst *.asm *.sym *.rst core *~ rm -f cloader-16fdc.ihx cloader-16fdc.tmp diff --git a/Kernel/platform-cromemco/commonmem.s b/Kernel/platform-cromemco/commonmem.s index 522503a8..df459e1b 100644 --- a/Kernel/platform-cromemco/commonmem.s +++ b/Kernel/platform-cromemco/commonmem.s @@ -1,7 +1,3 @@ -; -; Common on z80pack is at 0xF000 as defined by hardware. -; - .module commonmem .area _COMMONMEM diff --git a/Kernel/platform-cromemco/crt0.s b/Kernel/platform-cromemco/crt0.s index 6e524d63..cbfe051a 100644 --- a/Kernel/platform-cromemco/crt0.s +++ b/Kernel/platform-cromemco/crt0.s @@ -10,6 +10,7 @@ .area _CODE2 .area _CONST .area _INITIALIZED + .area _INTDATA .area _DATA .area _BSEG .area _BSS @@ -22,6 +23,8 @@ .area _DISCARD .area _INITIALIZER .area _COMMONMEM + .area _BOOT + .area _SERIAL ; imported symbols .globl _fuzix_main @@ -36,9 +39,17 @@ .globl l__DATA .globl kstack_top + ; For the boot vector + .globl init + .include "../kernel.def" .include "kernel.def" + .area _BOOT + + ; boot code (overlaps serial buffers to be) + jp init + ; startup code .area _CODE init: diff --git a/Kernel/platform-cromemco/devtty.c b/Kernel/platform-cromemco/devtty.c index e2c0eba3..f803ee83 100644 --- a/Kernel/platform-cromemco/devtty.c +++ b/Kernel/platform-cromemco/devtty.c @@ -17,10 +17,6 @@ struct s_queue ttyinq[NUM_DEV_TTY+1] = { /* ttyinq[0] is never used */ { tbuf3, tbuf3, tbuf3, TTYSIZ, 0, TTYSIZ/2 }, }; -static uint8_t ttybase[NUM_DEV_TTY+1] = { - 0, 0, 32, 80 -}; - tcflag_t uart_mask[4] = { _ISYS, _OSYS, @@ -37,28 +33,38 @@ tcflag_t *termios_mask[NUM_DEV_TTY + 1] = { static uint8_t ttypoll; +static uint8_t ttybase[] = { 0, 0, 32, 80 }; + +__sfr __at 0x00 tuart0_status; +__sfr __at 0x01 tuart0_data; + /* Write to system console */ void kputchar(char c) { - /* handle CRLF */ - if(c=='\n') - tty_putc(1, '\r'); - tty_putc(1, c); + /* Has to run polled */ + while(!(tuart0_status & 0x80)); + tuart0_data = c; } char tty_writeready(uint8_t minor) { - uint8_t r = ttybase[minor]; - r = in(r); - if (r & 0x80) - return 1; - return 0; + if (minor == 1 && tuart0_txl < 63) + return TTY_READY_NOW; + if (minor == 2 && tuart1_txl < 63) + return TTY_READY_NOW; + if (minor == 3 && tuart2_txl < 63) + return TTY_READY_NOW; + return TTY_READY_SOON; } void tty_putc(uint8_t minor, unsigned char c) { - uint8_t r = ttybase[minor]; - out(r + 1, c); + if (minor == 1) + tuart0_txqueue(c); + else if (minor == 2) + tuart1_txqueue(c); + else + tuart2_txqueue(c); } void tty_sleeping(uint8_t minor) @@ -68,24 +74,31 @@ void tty_sleeping(uint8_t minor) void tty_data_consumed(uint8_t minor) { + used(minor); } -void tty_irq(uint8_t minor) +void tty_drain(void) { - if (minor != 1) - return; /* For the moment */ - if (rx0a_int) { - uint8_t c = rx0a_char; - rx0a_int = 0; - tty_inproc(1, rx0a_char); - } - if (tx0a_int) { - tx0a_int = 0; + while (tuart0_rxl) + tty_inproc(1, tuart0_rx_get()); + if (tuart0_txl < 32 && (ttypoll & (1 << 1))) { ttypoll &= ~(1 << 1); - wakeup(&ttydata[1]); + tty_outproc(1); + } + while (tuart1_rxl) + tty_inproc(1, tuart1_rx_get()); + if (tuart1_txl < 32 && (ttypoll & (1 << 2))) { + ttypoll &= ~(1 << 2); + tty_outproc(2); + } + while (tuart2_rxl) + tty_inproc(1, tuart2_rx_get()); + if (tuart2_txl < 32 && (ttypoll & (1 << 3))) { + ttypoll &= ~(1 << 3); + tty_outproc(3); } } - + static uint8_t baudbits[] = { 1, 1, @@ -111,15 +124,16 @@ void tty_setup(uint8_t minor, uint8_t flags) struct termios *t = &ttydata[minor].termios; uint8_t baud = t->c_cflag & CBAUD; uint8_t r = ttybase[minor]; + + used(flags); + out(r, baudbits[baud] | (t->c_cflag & CSTOPB) ? 0 : 0x80); - /* Assume we keep to IM1 */ if (baud <= B9600) out(r + 2, 0x8); else out(r + 2, 0x18); } -/* For the moment */ int tty_carrier(uint8_t minor) { used(minor); diff --git a/Kernel/platform-cromemco/devtty.h b/Kernel/platform-cromemco/devtty.h index ddb0f422..ef232c68 100644 --- a/Kernel/platform-cromemco/devtty.h +++ b/Kernel/platform-cromemco/devtty.h @@ -1 +1,25 @@ -extern void tty_irq(uint8_t minor); +extern void tty_drain(void); + +extern void tuart0_txqueue(uint8_t c) __z88dk_fastcall; +extern uint8_t tuart0_rx_get(void); +extern void tuart0_error_get(void); +extern void tuart0_txd(uint8_t unused); +extern void tuart0_rx_ring(uint8_t unused); + +extern uint8_t tuart0_txl, tuart0_rxl; + +extern void tuart1_txqueue(uint8_t c) __z88dk_fastcall; +extern uint8_t tuart1_rx_get(void); +extern void tuart1_error_get(void); +extern void tuart1_txd(uint8_t unused); +extern void tuart1_rx_ring(uint8_t unused); + +extern uint8_t tuart1_txl, tuart1_rxl; + +extern void tuart2_txqueue(uint8_t c) __z88dk_fastcall; +extern uint8_t tuart2_rx_get(void); +extern void tuart2_error_get(void); +extern void tuart2_txd(uint8_t unused); +extern void tuart2_rx_ring(uint8_t unused); + +extern uint8_t tuart2_txl, tuart2_rxl; diff --git a/Kernel/platform-cromemco/discard.c b/Kernel/platform-cromemco/discard.c index 17001282..c7680869 100644 --- a/Kernel/platform-cromemco/discard.c +++ b/Kernel/platform-cromemco/discard.c @@ -15,9 +15,9 @@ void pagemap_init(void) /* Nothing to do for the map of init but we do set our vectors up here */ void map_init(void) { - if (request_irq(0xE7, uart0a_rx) | - request_irq(0xEF, uart0a_txdone) | - request_irq(0xF7, uart0a_timer4)) + if (request_irq(0xE7, tuart0_rx_ring) | + request_irq(0xEF, tuart0_txd) | + request_irq(0xF7, tuart0_timer4)) panic("irqset"); /* We need to claim these in case we set one off as they are at odd vectors as the base tu_uart is strapped for 8080 mode */ @@ -29,6 +29,7 @@ void map_init(void) request_irq(0xFF, spurious) ) panic("irqset2"); + /* FIXME: request vectors for uart1 and 2 */ } uint8_t platform_param(char *p) diff --git a/Kernel/platform-cromemco/fuzix.lnk b/Kernel/platform-cromemco/fuzix.lnk index 04e26f2e..fd1b2c42 100644 --- a/Kernel/platform-cromemco/fuzix.lnk +++ b/Kernel/platform-cromemco/fuzix.lnk @@ -1,8 +1,10 @@ -mwxuy -i fuzix.ihx --b _CODE=0x0100 +-b _CODE=0x0280 -b _COMMONMEM=0xF200 -b _DISCARD=0xE000 +-b _SERIAL=0x0100 +-b _BOOT=0x0100 -l z80 platform-cromemco/crt0.rel platform-cromemco/commonmem.rel diff --git a/Kernel/platform-cromemco/irq.h b/Kernel/platform-cromemco/irq.h index 1dd00c7d..a9db31a6 100644 --- a/Kernel/platform-cromemco/irq.h +++ b/Kernel/platform-cromemco/irq.h @@ -6,8 +6,4 @@ extern uint8_t irqvec[]; extern void spurious(uint8_t vec); extern void set_irq(uint16_t vec, void (*func)(uint8_t)); -extern void uart0a_rx(uint8_t unused); -extern void uart0a_txdone(uint8_t unused); -extern void uart0a_timer4(uint8_t unused); - -extern uint8_t rx0a_int, tx0a_int, rx0a_char; +extern void tuart0_timer4(uint8_t unused); diff --git a/Kernel/platform-cromemco/main.c b/Kernel/platform-cromemco/main.c index 8b5834ad..f975f4e5 100644 --- a/Kernel/platform-cromemco/main.c +++ b/Kernel/platform-cromemco/main.c @@ -18,10 +18,8 @@ __sfr __at 0x08 timer4; void platform_interrupt(void) { timer4 = 156; - tty_irq(1); + tty_drain(); timer_interrupt(); -// tty_irq(2); -// tty_irq(3); } /* This points to the last buffer in the disk buffers. There must be at least diff --git a/Kernel/platform-cromemco/tuart.s b/Kernel/platform-cromemco/tuart.s new file mode 100644 index 00000000..20cf3ddc --- /dev/null +++ b/Kernel/platform-cromemco/tuart.s @@ -0,0 +1,226 @@ +; +; Based on the SIO driver but for the Cromemco TU-ART +; +; The big difference is that we have to do a minimal task switch +; in order to access our data as we don't have a true common and also +; common space is precious +; +; For speed we flip bank but not stack so once we flip we must not +; use the stack until we flip back. +; + +.macro tuart_ports X + + .globl _tuart'X'_rxl + .globl _tuart'X'_txl + +; +; Must be aligned. Need not be common +; + + .area _SERIAL +tuart'X'_rx: + .ds 64 +tuart'X'_tx: + .ds 64 + +; +; We flip maps so we don't need this in common +; + .area _INTDATA +tuart'X'_error: + .db 0 +tuart'X'_rxover: + .db 0 +tuart'X'_txp: + .dw tuart'X'_tx +tuart'X'_txe: + .dw tuart'X'_tx +tuart'X'_rxp: + .dw tuart'X'_rx +tuart'X'_rxe: + .dw tuart'X'_rx +_tuart'X'_rxl: + .db 0 +_tuart'X'_txl: + .db 0 +_tuart'X'_error: + .db 0 + +.endm + +.macro tuart_handler_im2 X P lh RET + +; +; C interface methods +; + .globl _tuart'X'_txqueue + .globl _tuart'X'_rx_get + .globl _tuart'X'_error_get + .globl _tuart'X'_txd + .globl _tuart'X'_rx_ring + + .area _COMMONMEM +; +; Interrupt vector handler for port A transmit empty +; +_tuart'X'_txd: + push af + push hl + push bc + ld bc,#0x4001 + in a,(c) + out (c),b + ld b,a + ld a,(_tuart'X'_txl) + or a + jr z, tx_'X'_none + dec a + ld (_tuart'X'_txl),a + ld hl,(tuart'X'_txp) + ld a,(hl) + out (P + 1),a + inc l + set 6,l + lh 7,l ; Force to upper or lower half + ld (tuart'X'_txp),hl +tx_'X'_none: + ; Do we need to mask the int off + ; if so need to cache 03 state (intmask) and add 0x20 (TBE) + out (c),b + pop bc + pop hl + pop af + ei + RET +; +; Interrupt vector handler for port A receive ready +; +_tuart'X'_rx_ring: + push af + push hl + push bc + ld bc,#0x4001 + in a,(c) + out (c),b + ld b,a +tuart'X'_rx_next: + in a,(P) ; status + and #7 + jp z, tuart'X'_no_error + ld hl,#_tuart'X'_error ; accumulate error bits + or (hl) + ld (hl),a +tuart'X'_no_error: + in a,(P + 1) ; read ASAP + ld l,a + ld a,(_tuart'X'_rxl) + inc a + bit 6,a + jp nz, tuart'X'_rx_over + ld (_tuart'X'_rxl),a + ld a,l + ld hl,(tuart'X'_rxp) + ld (hl),a + inc l + res 6,l + lh 7,l + ld (tuart'X'_rxp),hl + ; + ; This is bounded as worst case at high data rate and low + ; CPU speed we will overrun and bail out. + ; + in a,(P) ; RR 0 + bit 6,a ; RDA is high if there is more to read + jr c, tuart'X'_rx_next + out (c),b + pop bc + pop hl + pop af + ei + RET +tuart'X'_rx_over: + ld a,(tuart'X'_error) + or #0x04 ; Fake an RX overflow bit + ld (tuart'X'_error),a + out (c),b + pop bc + pop hl + pop af + ei + RET + + .area _CODE + +; +; Queue a byte to be sent (DI required) +; +; l = byte +; +; Need a way to halt processing somewhere here or a_tx ? +; (or can we use hardware ?) +; 128 byte ring buffer aligned to upper half (rx is in lower) +; +_tuart'X'_txqueue: + ld a,(_tuart'X'_txl) + or a + jr z, tuart'X'_direct_maybe ; if can tx now then do + inc a + bit 6,a + jp nz, tx'X'_overflow +tuart'X'_queue: + ld (_tuart'X'_txl),a + ld a,l + ld hl,(tuart'X'_txe) + ld (hl),a + inc l + set 6,l + lh 7,l + ld (tuart'X'_txe),hl + ld l,#0 + ret +tx'X'_overflow: + ; some kind of flag for error + ld l,#1 + ret +tuart'X'_direct_maybe: + ; check RR + in a,(P) + rla ; RX space ? + ; if space + ld a,#1 + jr nc, tuart'X'_queue + ; bypass the queue and kickstart the interrupt machine + ld a,l + out (P + 1),a + ld l,#0 + ret + + ; DI required + ; Returns char in L +_tuart'X'_rx_get: + ld a,(_tuart'X'_rxl) + or a + ret z + dec a + ld (_tuart'X'_rxl),a + ld hl,(tuart'X'_rxe) + ld a,(hl) + inc l + res 6,l + lh 7,l + ld (tuart'X'_rxe),hl + scf + ld l,a + ret + + ; DI required +_tuart'X'_error_get: + ld hl,#tuart'X'_error + ld a,(hl) + ld (hl),#0 + ld l,a + ret + + +.endm diff --git a/Kernel/platform-cromemco/vector.s b/Kernel/platform-cromemco/vector.s index 67d1e60c..0645ffbe 100644 --- a/Kernel/platform-cromemco/vector.s +++ b/Kernel/platform-cromemco/vector.s @@ -1,3 +1,5 @@ + + .include 'tuart.s' ; ; Interrupt handlers. These will get replaced by some proper FIFO ; logic @@ -6,55 +8,20 @@ .area _COMMONMEM .module vector - .globl _rx0a_char - .globl _rx0a_int - .globl _tx0a_int .globl interrupt_high + .globl _tuart0_timer4 - .globl _uart0a_rx - .globl _uart0a_txdone - .globl _uart0a_timer4 - -_uart0a_rx: ; 0xE7 - push af - push bc - push de - ld bc,#0x8140 - in d,(c) - out (c),b - in a,(1) - ld (_rx0a_char),a - ld a,#1 - ld (_rx0a_int),a - out (c),d - pop de - pop bc - pop af - ei - reti +_tuart0_timer4: + jp interrupt_high -_uart0a_txdone: ; 0xEF - push af - push bc - push de - ld bc,#0x8140 - in d,(c) - out (c),b - ld a,#1 - ld (_tx0a_int),a - out (c),d - pop de - pop bc - pop af - ei - reti +tuart_ports 0 +tuart_ports 1 +tuart_ports 2 -_uart0a_timer4: - jp interrupt_high + .area _COMMONMEM -_rx0a_char: - .byte 0 -_rx0a_int: - .byte 0 -_tx0a_int: - .byte 0 +; Console tuart +tuart_handler_im2 0 0 res reti +; Additional ports on cards +tuart_handler_im2 1 0x80 set reti +tuart_handler_im2 2 0x90 res reti -- 2.34.1