$(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
-;
-; Common on z80pack is at 0xF000 as defined by hardware.
-;
-
.module commonmem
.area _COMMONMEM
.area _CODE2
.area _CONST
.area _INITIALIZED
+ .area _INTDATA
.area _DATA
.area _BSEG
.area _BSS
.area _DISCARD
.area _INITIALIZER
.area _COMMONMEM
+ .area _BOOT
+ .area _SERIAL
; imported symbols
.globl _fuzix_main
.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:
{ 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,
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)
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,
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);
-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;
/* 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 */
request_irq(0xFF, spurious)
)
panic("irqset2");
+ /* FIXME: request vectors for uart1 and 2 */
}
uint8_t platform_param(char *p)
-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
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);
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
--- /dev/null
+;
+; 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
+
+ .include 'tuart.s'
;
; Interrupt handlers. These will get replaced by some proper FIFO
; logic
.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