r |= 0x02;
sio_r[3] = r;
sio_r[5] = 0x8A | ((t->c_cflag & CSIZE) << 1);
+ if (minor == 1)
+ sio2a_wr5 = sio_r[5];
+ else
+ sio2b_wr5 = sio_r[5];
}
void tty_setup(uint8_t minor, uint8_t flags)
return 0;
}
-void tty_pollirq_sio(void)
+void tty_drain_sio(void)
{
+ uint8_t c;
+#if 1
+ while(sio2b_rxl) {
+ c = sio2b_rx_get();
+ tty_inproc(1, c);
+ }
+ while(sio2a_rxl) {
+ c = sio2a_rx_get();
+ tty_inproc(2, c);
+ }
+ /* TODO: error, tx flow, modem change etc */
+#else
static uint8_t old_ca, old_cb;
uint8_t ca, cb;
uint8_t progress;
tty_carrier_drop(1);
}
} while(progress);
+#endif
}
void tty_putc(uint8_t minor, unsigned char c)
{
+#if 1
+ if (minor == 1)
+ sio2b_txqueue(c);
+ else
+ sio2a_txqueue(c);
+#else
if (minor == 2) {
SIOA_D = c;
} else if (minor == 1)
SIOB_D = c;
+#endif
}
/* We will need this for SIO once we implement flow control signals */
ttyready_t tty_writeready(uint8_t minor)
{
- irqflags_t irq;
- uint8_t c;
-
- irq = di();
- if (minor == 2) {
- SIOA_C = 0; /* read register 0 */
- c = SIOA_C;
- irqrestore(irq);
- if (c & 0x04) /* THRE? */
- return TTY_READY_NOW;
+ if (minor == 1 && sio2b_txl == 255)
return TTY_READY_SOON;
- } else if (minor == 1) {
- SIOB_C = 0; /* read register 0 */
- c = SIOB_C;
- irqrestore(irq);
- if (c & 0x04) /* THRE? */
- return TTY_READY_NOW;
+ if (minor == 2 && sio2a_txl == 255)
return TTY_READY_SOON;
- }
- irqrestore(irq);
return TTY_READY_NOW;
}
/* kernel writes to system console -- never sleep! */
void kputchar(char c)
{
- tty_putc(TTYDEV - 512, c);
+ /* Can't use the normal paths as we must survive interrupts off */
+ /* FIXME: would be nicer to just disable tx int and re-enable it ? */
+ irqflags_t irq = di();
+ while(!(SIOA_C & 0x04));
+ SIOA_D = c;
if (c == '\n')
- tty_putc(TTYDEV - 512, '\r');
+ kputchar('\r');
+ irqrestore(irq);
}
.globl null_handler
.globl nmi_handler
.globl outcharhex
+ .globl init
.globl s__COMMONMEM
.globl l__COMMONMEM
.globl spurious ; so we can debug trap on it
spurious:
+ ei
reti
mapreg:
ld (0x88),hl ; PIO A
ld (0x8A),hl ; PIO B
- ld hl,#spurious ; For now
+ ld hl,#sio2b_txd
ld (0x90),hl ; SIO B TX empty
+ ld hl,#sio2b_status
ld (0x92),hl ; SIO B External status
+ ld hl,#sio2b_rx_ring
ld (0x94),hl ; SIO B Receive
+ ld hl,#sio2b_special
ld (0x96),hl ; SIO B Special
+ ld hl,#sio2a_txd
ld (0x98),hl ; SIO A TX empty
+ ld hl,#sio2a_status
ld (0x9A),hl ; SIO A External status
+ ld hl,#sio2a_rx_ring
ld (0x9C),hl ; SIO A Received
+ ld hl,#sio2a_special
ld (0x9E),hl ; SIO A Special
ld hl, #80
ld (_ramsize), hl
.byte 0x00
.byte 0x18 ; Reset
.byte 0x04
- .byte 0xC4
+ .byte 0xC4 ; x64 async 1 stop no parity
.byte 0x01
- .byte 0x18
+ .byte 0x1F ; status affects vector, ti, ei, int all
.byte 0x03
- .byte 0xE1
+ .byte 0xE1 ; 8bit, autoen, rx en
.byte 0x05
- .byte RTS_LOW
- .byte 0x02 ; IRQ vector (B only)
- .byte 0x90
+ .byte RTS_LOW ; dtr, 8bit, tx en, rts
+ .byte 0x02
+ .byte 0x90 ; IRQ vector (B only)
.area _CODE
out (SIOA_D),a
ret
+;
+; SIO2 IM2 console driver
+;
+; The buffers are 256 bytes per channel and page aligned. The lower
+; half is used for receive the upper for transmit. Both are rings.
+
+;
+; Transmit data from the queue. We need a stop system for this but
+; actually the logical one might be to just turn that IRQ off, so long
+; as we remember to kickstart it again properly. Check if it's enough
+; to just unmask the IRQ bit ?
+;
+; All of this lives in common space so we don't bank switch so much.
+
+ .area _CONSOLE
+
+sio2a_rx:
+ jp init ; for boot only
+ .ds 128-3 ; we wrote a JP init in the first 3
+sio2a_tx: ; that will be recycled
+ .ds 128
+sio2b_rx:
+ .ds 128
+sio2b_tx:
+ .ds 128
+
+ .area _COMMONMEM
+_sio2a_txl:
+ .db 0
+sio2a_error:
+ .db 0
+sio2a_rxover:
+ .db 0
+sio2a_stat:
+ .db 0
+sio2a_txp:
+ .dw sio2a_tx
+sio2a_txe:
+ .dw sio2a_tx
+_sio2a_rxl:
+ .db 0
+sio2a_rxp:
+ .dw sio2a_rx
+sio2a_rxe:
+ .dw sio2a_rx
+
+_sio2b_txl:
+ .db 0
+sio2b_error:
+ .db 0
+sio2b_rxover:
+ .db 0
+sio2b_stat:
+ .db 0
+sio2b_txp:
+ .dw sio2a_tx
+sio2b_txe:
+ .dw sio2a_tx
+_sio2b_rxl:
+ .db 0
+sio2b_rxp:
+ .dw sio2a_rx
+sio2b_rxe:
+ .dw sio2a_rx
+
+;
+; Interrupt vector handler for port A transmit empty
+;
+sio2a_txd:
+ push af
+ ld a,(_sio2a_txl)
+ or a
+ jr z, tx_a_none
+ push hl
+ dec a
+ ld (_sio2a_txl),a
+ ld hl,(sio2a_txp)
+ ld a,(hl)
+ out (SIOA_D),a
+ inc hl
+ set 7,l
+ ld (sio2a_txp),hl
+ pop hl
+tx_a_none:
+ ld a,#0x28
+ out (SIOA_C),a ; silence tx interrupt
+ pop af
+ ei
+ reti
+;
+; Interrupt vector handler for port A receive ready
+;
+sio2a_rx_ring:
+ push af
+ push hl
+sio2a_rx_next:
+ in a,(SIOA_D) ; read ASAP
+ ld l,a
+ ld a,(_sio2a_rxl)
+ inc a
+ jp m, a_rx_over
+ ld (_sio2a_rxl),a
+ ; should we check bit 5/6 and if appropriate flow control on bit 5/6
+ ; high ?
+;sio2a_flow_patch: ; patch with fastest 5 byte nop
+; cp #0x60 ; flow control threshold
+; call z, _sio2a_flow_control_on
+ ld a,l
+ ld hl,(sio2a_rxp)
+ ld (hl),a
+ inc l
+ res 7,l
+ ld (sio2a_rxp),hl
+ ;
+ ; The chip has a small FIFO and bytes can also arrive as we
+ ; read. To maximise performance try and empty it each time.
+ ;
+ ; This is bounded as worst case at high data rate and low
+ ; CPU speed we will overrun and bail out.
+ ;
+ in a,(SIOA_C) ; RR 0
+ rra
+ jr c, sio2a_rx_next
+ pop hl
+ pop af
+ ei
+ reti
+a_rx_over:
+ ld a,(sio2a_error)
+ or #0x20 ; Fake an RX overflow bit
+ ld (sio2a_rxover),a
+ pop af
+ ei
+ reti
+;
+; Interrupt vector for a port A status change
+;
+; FIXME:
+; log the or of changes seem (dcd down, up) and last state
+; of both CTS and DCD. The CTS last state is sufficient for flow
+; and we can use thw two edges of DCD to work out if we had a hangup
+; and if we are now open.
+;
+sio2a_status:
+ ; CTS or DCD change
+ push af
+ push hl
+ ; RR0
+ in a,(SIOA_C)
+ ; Clear the latched values
+ ld a,#0x10
+ out (SIOA_C),a
+ pop hl
+ pop af
+ ei
+ reti
+
+;
+; Interrupt vector for a port A error
+;
+sio2a_special:
+ ; Parity, RX Overrun, Framing
+ ; Probably want to record them, but we at least must clean up
+ push af
+ ld a,#1
+ out (SIOA_C),a ; RR1 please
+ in a,(SIOA_C) ; clear events
+ ld (sio2a_error),a ; Save error bits
+ ; Clear the latched values
+ ld a,#0x30
+ out (SIOA_C),a
+ pop af
+ ei
+ reti
+
+;
+; Interrupt vector handler for port B transmit empty
+;
+sio2b_txd:
+ push af
+ ld a,(_sio2b_txl)
+ or a
+ jr z, tx_b_none
+ push hl
+ dec a
+ ld (_sio2b_txl),a
+ ld hl,(sio2b_txp)
+ ld a,(hl)
+ out (SIOB_D),a
+ inc hl
+ set 7,l
+ ld (sio2b_txp),hl
+ pop hl
+tx_b_none:
+ ld a,#0x28
+ out (SIOB_C),a ; silence tx interrupt
+ pop af
+ ei
+ reti
+;
+; Interrupt vector handler for port B receive ready
+;
+sio2b_rx_ring:
+ push af
+ push hl
+sio2b_rx_next:
+ in a,(SIOB_D) ; read ASAP
+ ld l,a
+ ld a,(_sio2b_rxl)
+ inc a
+ jp m, b_rx_over
+ ld (_sio2b_rxl),a
+ ; should we check bit 5/6 and if appropriate flow control on bit 5/6
+ ; high ?
+;sio2b_flow_patch: ; patch with fastest 5 byte nop
+; cp #0x60 ; flow control threshold
+; call z, _sio2b_flow_control_on
+ ld a,l
+ ld hl,(sio2b_rxp)
+ ld (hl),a
+ inc l
+ res 7,l
+ ld (sio2b_rxp),hl
+ ;
+ ; The chip has a small FIFO and bytes can also arrive as we
+ ; read. To maximise performance try and empty it each time.
+ ;
+ ; This is bounded as worst case at high data rate and low
+ ; CPU speed we will overrun and bail out.
+ ;
+ in a,(SIOB_C) ; RR 0
+ rra
+ jr c, sio2b_rx_next
+ pop hl
+ pop af
+ ei
+ reti
+b_rx_over:
+ ld a,(sio2b_error)
+ or #0x20 ; Fake an RX overflow bit
+ ld (sio2b_rxover),a
+ pop af
+ ei
+ reti
+;
+; Interrupt vector for a port B status change
+;
+; FIXME:
+; log the or of changes seem (dcd down, up) and last state
+; of both CTS and DCD. The CTS last state is sufficient for flow
+; and we can use thw two edges of DCD to work out if we had a hangup
+; and if we are now open.
+;
+sio2b_status:
+ ; CTS or DCD change
+ push af
+ push hl
+ ; RR0
+ in a,(SIOB_C)
+ ; Clear the latched values
+ ld a,#0x10
+ out (SIOB_C),a
+ pop hl
+ pop af
+ ei
+ reti
+
+;
+; Interrupt vector for a port B error
+;
+sio2b_special:
+ ; Parity, RX Overrun, Framing
+ ; Probably want to record them, but we at least must clean up
+ push af
+ ld a,#1
+ out (SIOB_C),a ; RR1 please
+ in a,(SIOB_C) ; clear events
+ ld (sio2b_error),a ; Save error bits
+ ; Clear the latched values
+ ld a,#0x30
+ out (SIOB_C),a
+ pop af
+ ei
+ reti
+
+;
+; C interface methods
+;
+ .globl _sio2a_txqueue
+ .globl _sio2a_flow_control_on
+ .globl _sio2a_rx_get
+ .globl _sio2a_error_get
+
+ .globl _sio2a_rxl
+ .globl _sio2a_txl
+ .globl _sio2a_wr5
+
+ .globl _sio2b_txqueue
+ .globl _sio2b_flow_control_on
+ .globl _sio2b_rx_get
+ .globl _sio2b_error_get
+
+ .globl _sio2b_rxl
+ .globl _sio2b_txl
+ .globl _sio2b_wr5
+
+_sio2a_wr5:
+ .db 0xEA ; DTR, 8bit, tx enabled
+_sio2b_wr5:
+ .db 0xEA ; DTR, 8bit, tx enabled
+
+;
+; 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)
+;
+_sio2a_txqueue:
+ ld a,(_sio2a_txl)
+ or a
+ jr z, sio2a_direct_maybe ; if can tx now then do
+ inc a
+ jp m, txa_overflow
+sio2a_queue:
+ ld (_sio2a_txl),a
+ ld a,l
+ ld hl,(sio2a_txe)
+ ld (hl),a
+ inc l
+ set 7,l
+ ld (sio2a_txe),hl
+ ld l,#0
+ ret
+txa_overflow:
+ ; some kind of flag for error
+ ld l,#1
+ ret
+sio2a_direct_maybe:
+ ; check RR
+ in a,(SIOA_C)
+ and #0x04 ; RX space ?
+ ; if space
+ ld a,#1
+ jr nz, sio2a_queue
+ ; bypass the queue and kickstart the interrupt machine
+ ld a,l
+ out (SIOA_D),a
+ ld l,#0
+ ret
+ ; Call with DI
+
+_sio2a_flow_control_on:
+ ld a,#5
+ out(SIOA_C),a ; WR 5
+ ld a,(_sio2a_wr5)
+ or #0x02
+ out (SIOA_C),a ; Turn on RTS
+ ret
+
+ ; DI required
+ ; Returns char in L
+ ;
+ ; Caller responsible for making post buffer fetch decisions about
+ ; RTS
+_sio2a_rx_get:
+ ld a,(_sio2a_rxl)
+ or a
+ ret z
+ dec a
+ ld (_sio2a_rxl),a
+ ld hl,(sio2a_rxe)
+ ld a,(hl)
+ inc l
+ res 7,l
+ ld (sio2a_rxe),hl
+ scf
+ ld l,a
+ ret
+
+ ; DI required
+_sio2a_error_get:
+ ld hl,#sio2a_error
+ ld a,(hl)
+ ld (hl),#0
+ ld l,a
+ ret
+
+;
+; 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)
+;
+_sio2b_txqueue:
+ ld a,(_sio2b_txl)
+ or a
+ jr z, sio2b_direct_maybe ; if can tx now then do
+ inc a
+ jp m, txb_overflow
+sio2b_queue:
+ ld (_sio2b_txl),a
+ ld a,l
+ ld hl,(sio2b_txe)
+ ld (hl),a
+ inc l
+ set 7,l
+ ld (sio2b_txe),hl
+ ld l,#0
+ ret
+txb_overflow:
+ ; some kind of flag for error
+ ld l,#1
+ ret
+sio2b_direct_maybe:
+ ; check RR
+ in a,(SIOB_C)
+ and #0x04 ; RX space ?
+ ; if space
+ ld a,#1
+ jr z, sio2b_queue
+ ; bypass the queue and kickstart the interrupt machine
+ ld a,l
+ out (SIOB_D),a
+ ld l,#0
+ ret
+ ; Call with DI
+
+_sio2b_flow_control_on:
+ ld a,#5
+ out(SIOB_C),a ; WR 5
+ ld a,(_sio2b_wr5)
+ or #0x02
+ out (SIOB_C),a ; Turn on RTS
+ ret
+
+ ; DI required
+ ; Returns char in L
+ ;
+ ; Caller responsible for making post buffer fetch decisions about
+ ; RTS
+_sio2b_rx_get:
+ ld a,(_sio2b_rxl)
+ or a
+ ret z
+ dec a
+ ld (_sio2b_rxl),a
+ ld hl,(sio2b_rxe)
+ ld a,(hl)
+ inc l
+ res 7,l
+ ld (sio2b_rxe),hl
+ scf
+ ld l,a
+ ret
+
+ ; DI required
+_sio2b_error_get:
+ ld hl,#sio2b_error
+ ld a,(hl)
+ ld (hl),#0
+ ld l,a
+ ret
+