#define CONFIG_IDE
/* Device parameters */
-#define NUM_DEV_TTY 2
+#define NUM_DEV_TTY 4
/* UART0 as the console */
#define BOOT_TTY (512 + 1)
/* 1: /dev/fd - Floppy disk block devices */
{ no_open, no_close, no_rdwr, no_rdwr, no_ioctl},
/* 2: /dev/tty -- serial ports */
- { tty_open, tty_close, tty_read, tty_write, tty_ioctl},
+ { rctty_open, tty_close, tty_read, tty_write, tty_ioctl},
/* 3: RAM disk */
{ no_open, no_close, no_rdwr, no_rdwr, no_ioctl},
/* 4: /dev/mem etc System devices (one offs) */
static char tbuf1[TTYSIZ];
static char tbuf2[TTYSIZ];
+static char tbuf3[TTYSIZ];
+static char tbuf4[TTYSIZ];
static uint8_t sleeping;
-uint8_t ser_type = 2;
+struct s_queue ttyinq[NUM_DEV_TTY + 1] = { /* ttyinq[0] is never used */
+ {NULL, NULL, NULL, 0, 0, 0},
+ {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2},
+ {tbuf2, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ / 2},
+ {tbuf3, tbuf3, tbuf3, TTYSIZ, 0, TTYSIZ / 2},
+ {tbuf4, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ / 2},
+};
static tcflag_t uart0_mask[4] = {
_ISYS,
tcflag_t *termios_mask[NUM_DEV_TTY + 1] = {
NULL,
uart0_mask,
- uart1_mask
-};
-
-struct s_queue ttyinq[NUM_DEV_TTY + 1] = { /* ttyinq[0] is never used */
- {NULL, NULL, NULL, 0, 0, 0},
- {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2},
- {tbuf2, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ / 2},
+ uart1_mask,
+ uart0_mask,
+ uart0_mask
};
uint8_t sio_r[] = {
0x05, 0xEA
};
+static uint16_t siobaud[] = {
+ 0xC0, /* 0 */
+ 0, /* 50 */
+ 0, /* 75 */
+ 0, /* 110 */
+ 0, /* 134 */
+ 0, /* 150 */
+ 0xC0, /* 300 */
+ 0x60, /* 600 */
+ 0xC0, /* 1200 */
+ 0x60, /* 2400 */
+ 0x30, /* 4800 */
+ 0x18, /* 9600 */
+ 0x0C, /* 19200 */
+ 0x06, /* 38400 */
+ 0x04, /* 57600 */
+ 0x02 /* 115200 */
+};
+
static void sio2_setup(uint8_t minor, uint8_t flags)
{
struct termios *t = &ttydata[minor].termios;
uint8_t r;
+ uint8_t baud;
+
+ used(flags);
+
+ baud = t->c_cflag & CBAUD;
+ if (baud < B300)
+ baud = B300;
+
/* Set bits per character */
sio_r[1] = 0x01 | ((t->c_cflag & CSIZE) << 2);
+
r = 0xC4;
+ if (ctc_present && minor == 3) {
+ CTC_CH1 = 0x55;
+ CTC_CH1 = siobaud[baud];
+ if (baud > B600) /* Use x16 clock and CTC divider */
+ r = 0x44;
+ } else
+ baud = B115200;
+
+ t->c_cflag &= CBAUD;
+ t->c_cflag |= baud;
+
if (t->c_cflag & CSTOPB)
r |= 0x08;
if (t->c_cflag & PARENB)
void tty_setup(uint8_t minor, uint8_t flags)
{
- if (ser_type == 1) {
+ if (sio_present || sio1_present) {
sio2_setup(minor, flags);
sio2_otir(SIO0_BASE + 2 * (minor - 1));
/* We need to do CTS/RTS support and baud setting on channel 2
yet */
}
- if (ser_type == 2) {
+ if (acia_present) {
struct termios *t = &ttydata[1].termios;
uint8_t r = t->c_cflag & CSIZE;
/* No CS5/CS6 CS7 must have parity enabled */
int tty_carrier(uint8_t minor)
{
uint8_t c;
- if (ser_type == 1) {
- if (minor == 1) {
- SIOA_C = 0;
- c = SIOA_C;
- } else {
- SIOB_C = 0;
- c = SIOB_C;
- }
- if (c & 0x8)
- return 1;
- return 0;
- } else /* ACIA isn't wired for carrier on any board */
+ uint8_t port;
+
+ /* No carrier on ACIA */
+ if (sio_present == 0)
+ return 1;
+
+ port = SIO0_BASE + 2 * (minor - 1);
+ out(port, 0);
+ c = in(port);
+ if (c & 0x08)
return 1;
+ return 0;
}
-void tty_pollirq_sio(void)
+void tty_pollirq_sio0(void)
{
static uint8_t old_ca, old_cb;
uint8_t ca, cb;
SIOA_C = 2 << 5;
/* Output pending */
if ((ca & 4) && (sleeping & 2)) {
- tty_outproc(1);
+ tty_outproc(2);
sleeping &= ~2;
SIOA_C = 5 << 3; // reg 0 CMD 5 - reset transmit interrupt pending
}
tty_inproc(2, SIOB_D);
progress = 1;
}
- if ((cb & 4) && (sleeping & 4)) {
- tty_outproc(2);
- sleeping &= ~4;
+ if ((cb & 4) && (sleeping & 8)) {
+ tty_outproc(3);
+ sleeping &= ~8;
SIOB_C = 5 << 3; // reg 0 CMD 5 - reset transmit interrupt pending
}
if ((cb ^ old_cb) & 8) {
} while(progress);
}
+void tty_pollirq_sio1(void)
+{
+ static uint8_t old_ca, old_cb;
+ uint8_t ca, cb;
+ uint8_t progress;
+
+ /* Check for an interrupt */
+ SIOC_C = 0;
+ if (!(SIOC_C & 2))
+ return;
+
+ /* FIXME: need to process error/event interrupts as we can get
+ spurious characters or lines on an unused SIO floating */
+ do {
+ progress = 0;
+ SIOC_C = 0; // read register 0
+ ca = SIOC_C;
+ /* Input pending */
+ if ((ca & 1) && !fullq(&ttyinq[3])) {
+ progress = 1;
+ tty_inproc(3, SIOC_D);
+ }
+ /* Break */
+ if (ca & 2)
+ SIOC_C = 2 << 5;
+ /* Output pending */
+ if ((ca & 4) && (sleeping & 8)) {
+ tty_outproc(3);
+ sleeping &= ~8;
+ SIOC_C = 5 << 3; // reg 0 CMD 5 - reset transmit interrupt pending
+ }
+ /* Carrier changed */
+ if ((ca ^ old_ca) & 8) {
+ if (ca & 8)
+ tty_carrier_raise(3);
+ else
+ tty_carrier_drop(3);
+ }
+ SIOD_C = 0; // read register 0
+ cb = SIOD_C;
+ if ((cb & 1) && !fullq(&ttyinq[4])) {
+ tty_inproc(4, SIOD_D);
+ progress = 1;
+ }
+ if ((cb & 4) && (sleeping & 16)) {
+ tty_outproc(4);
+ sleeping &= ~16;
+ SIOD_C = 5 << 3; // reg 0 CMD 5 - reset transmit interrupt pending
+ }
+ if ((cb ^ old_cb) & 8) {
+ if (cb & 8)
+ tty_carrier_raise(4);
+ else
+ tty_carrier_drop(4);
+ }
+ } while(progress);
+}
+
void tty_pollirq_acia(void)
{
uint8_t ca;
ca = ACIA_C;
if (ca & 1) {
- ca = ACIA_D;
- tty_inproc(1, ca);
+ tty_inproc(1, ACIA_D);
}
if ((ca & 2) && sleeping) {
tty_outproc(1);
}
}
-static char hex[] = { "0123456789ABCDEF" };
-
void tty_putc(uint8_t minor, unsigned char c)
{
- if (ser_type == 1) {
- if (minor == 1) {
- SIOA_D = c;
- } else if (minor == 2)
- SIOB_D = c;
- } else if (minor == 1)
- ACIA_D = c;
- else if (minor = 3) {
- /* FIXME: implement */
+ if (acia_present)
+ SIOA_D = c;
+ else {
+ uint8_t port = SIO0_BASE + 1 + 2 * (minor - 1);
+ out(port, c);
}
}
-/* We will need this for SIO once we implement flow control signals */
void tty_sleeping(uint8_t minor)
{
+ sleeping |= (1 << minor);
}
/* Be careful here. We need to peek at RR but we must be sure nobody else
Need to review this we should be ok as the IRQ handler always leaves
us pointing at RR0 */
-
ttyready_t tty_writeready(uint8_t minor)
{
irqflags_t irq;
uint8_t c;
- if (ser_type == 1) {
- irq = di();
- if (minor == 1) {
- SIOA_C = 0; /* read register 0 */
- c = SIOA_C;
- irqrestore(irq);
- if (c & 0x04) /* THRE? */
- return TTY_READY_NOW;
- return TTY_READY_SOON;
- } else if (minor == 2) {
- SIOB_C = 0; /* read register 0 */
- c = SIOB_C;
- irqrestore(irq);
- if (c & 0x04) /* THRE? */
- return TTY_READY_NOW;
- return TTY_READY_SOON;
- }
- irqrestore(irq);
- } else if (ser_type == 2 && minor == 1) {
+ uint8_t port;
+
+ if (acia_present) {
c = ACIA_C;
if (c & 0x02) /* THRE? */
return TTY_READY_NOW;
return TTY_READY_SOON;
}
- return TTY_READY_NOW;
+
+ irq = di();
+ port = SIO0_BASE+ 2 * (minor - 1);
+ out(port, 0);
+ c = in(port);
+ irqrestore(irq);
+
+ if (c & 0x04) /* THRE? */
+ return TTY_READY_NOW;
+ return TTY_READY_SOON;
}
void tty_data_consumed(uint8_t minor)
{
+ used(minor);
}
/* kernel writes to system console -- never sleep! */
while(tty_writeready(TTYDEV - 512) != TTY_READY_NOW);
tty_putc(TTYDEV - 512, c);
}
+
+int rctty_open(uint8_t minor, uint16_t flag)
+{
+ if (acia_present && minor != 1) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ if ((minor == 1 || minor == 2) && !sio_present) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ if ((minor == 3 || minor == 4) && !sio1_present) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return tty_open(minor, flag);
+}
#define __DEVTTY_DOT_H__
void tty_putc(uint8_t minor, unsigned char c);
-void tty_pollirq_sio(void);
+void tty_pollirq_sio0(void);
+void tty_pollirq_sio1(void);
void tty_pollirq_acia(void);
-
-extern uint8_t ser_type;
+int rctty_open(uint8_t minor, uint16_t flag);
#endif
#include <ds1302.h>
#include <devide.h>
#include <blkdev.h>
+#include <rc2014.h>
#include "config.h"
void map_init(void)
{
+ if (acia_present)
+ kputs("6850 ACIA detected at 0x80.\n");
+ if (sio_present)
+ kputs("Z80 SIO detected at 0x80.\n");
+ if (sio1_present)
+ kputs("Z80 SIO detected at 0x84.\n");
+ if (ctc_present)
+ kputs("Z80 CTC detected at 0x88.\n");
}
/*
CPU_CLOCK_KHZ .equ 7372
-; Base address of SIO/2 chip 0x80
-; For the Scott Baker SIO card adjust the order to match rc2014.h
-SIOA_C .EQU 0x80
-SIOA_D .EQU SIOA_D+1
-SIOB_C .EQU SIOA_D+2
-SIOB_D .EQU SIOA_D+3
; Z80 CTC ports
CTC_CH0 .equ 0x88 ; CTC channel 0 and interrupt vector
CTC_CH1 .equ 0x89 ; CTC channel 1 (periodic interrupts)
#include <devfd.h>
#include <rtc.h>
#include <ds1302.h>
+#include <rc2014.h>
extern unsigned char irqvector;
struct blkbuf *bufpool_end = bufpool + NBUFS; /* minimal for boot -- expanded after we're done with _DISCARD */
uint16_t ramtop = 0xF000;
uint8_t need_resched = 0;
+uint8_t acia_present;
+uint8_t ctc_present;
+uint8_t sio_present;
+uint8_t sio1_present;
void platform_discard(void)
{
kprintf("Buffers available: %d\n", bufpool_end - bufpool);
}
-static uint8_t idlect;
-
void platform_idle(void)
{
- /* Check the clock. We try and reduce the impact of the clock on
- latency by not doing it so often. 256 may be too small a divide
- need t see what 1/10th sec looks like in poll loops */
- if (!++idlect)
+ if (ctc_present)
+ __asm halt __endasm;
+ else {
+ irqflags_t irq = di();
sync_clock();
+ irqrestore(irq);
+ }
}
uint8_t platform_param(unsigned char *p)
return 0;
}
+static int16_t timerct;
+
+/* Call timer_interrupt at 10Hz */
+static void timer_tick(uint8_t n)
+{
+ timerct += n;
+ while (timerct >= 20) {
+ timer_interrupt();
+ timerct -= 20;
+ }
+}
+
void platform_interrupt(void)
{
- if (ser_type == 1)
- tty_pollirq_sio();
- else
+ if (acia_present)
tty_pollirq_acia();
- return;
+ if (sio_present)
+ tty_pollirq_sio0();
+ if (sio1_present)
+ tty_pollirq_sio1();
+ if (ctc_present) {
+ uint8_t n = 255 - CTC_CH3;
+ CTC_CH3 = 0x47;
+ CTC_CH3 = 255;
+ timer_tick(n);
+ }
}
/*
*/
void sync_clock(void)
{
- irqflags_t irq = di();
- int16_t tmp;
- if (!re_enter++) {
- sync_clock_read();
- if (oldticks != 0xFF) {
- tmp = newticks - oldticks;
- if (tmp < 0)
- tmp += 60;
- tmp *= 10;
- while(tmp--) {
- timer_interrupt();
+ if (!ctc_present) {
+ irqflags_t irq = di();
+ int16_t tmp;
+ if (!re_enter++) {
+ sync_clock_read();
+ if (oldticks != 0xFF) {
+ tmp = newticks - oldticks;
+ if (tmp < 0)
+ tmp += 60;
+ tmp *= 10;
+ while(tmp--) {
+ timer_interrupt();
+ }
}
}
+ re_enter--;
+ irqrestore(irq);
}
- re_enter--;
- if (re_enter > 1)
- kputs("oops");
- irqrestore(irq);
}
/*
__sfr __at (SIO0_BASE + 2) SIOB_C;
__sfr __at (SIO0_BASE + 3) SIOB_D;
+#define SIO1_BASE 0x84
+__sfr __at (SIO1_BASE + 0) SIOC_C;
+__sfr __at (SIO1_BASE + 1) SIOC_D;
+__sfr __at (SIO1_BASE + 2) SIOD_C;
+__sfr __at (SIO1_BASE + 3) SIOD_D;
+
/* ACIA is at same address as SIO but we autodetect */
#define ACIA_BASE 0x80
__sfr __at (ACIA_BASE + 0) ACIA_C;
__sfr __at (ACIA_BASE + 1) ACIA_D;
+__sfr __at 0x88 CTC_CH0;
+__sfr __at 0x89 CTC_CH1;
+__sfr __at 0x8A CTC_CH2;
+__sfr __at 0x8B CTC_CH3;
+
extern void sio2_otir(uint8_t port) __z88dk_fastcall;
+extern uint8_t acia_present;
+extern uint8_t ctc_present;
+extern uint8_t sio_present;
+extern uint8_t sio1_present;
+
#endif
.globl kstack_top
.globl unix_syscall_entry
.globl outcharhex
- .globl _ser_type
+ .globl _acia_present
+ .globl _ctc_present
+ .globl _sio_present
+ .globl _sio1_present
.globl s__COMMONMEM
.globl l__COMMONMEM
.include "../kernel.def"
+; Base address of SIO/2 chip 0x80
+
+SIOA_C .EQU 0x80
+SIOA_D .EQU SIOA_C+1
+SIOB_C .EQU SIOA_C+2
+SIOB_D .EQU SIOA_C+3
+
+SIOC_C .EQU 0x84
+SIOC_D .EQU SIOC_C+1
+SIOD_C .EQU SIOC_C+2
+SIOD_D .EQU SIOC_C+3
+
+ACIA_C .EQU 0x80
+ACIA_D .EQU 0x81
+ACIA_RESET .EQU 0x03
+ACIA_RTS_HIGH_A .EQU 0xD6 ; rts high, xmit interrupt disabled
+ACIA_RTS_LOW_A .EQU 0x96 ; rts low, xmit interrupt disabled
+;ACIA_RTS_LOW_A .EQU 0xB6 ; rts low, xmit interrupt enabled
+
+
;
; Buffers (we use asm to set this up as we need them in a special segment
; so we can recover the discard memory into the buffer pool
call ldir_to_user
; Play guess the serial port
- ; This needs doing better. We might be fooled by floating flow
- ; control lines as the SC104 does expose flow control. FIXME
- in a,(SIOA_C)
- and #0x2C
- cp #0x2C
- ; CTS and DCD should be high as they are not wired
- jr nz, try_acia
+
+ ;
+ ; We are booted under ROMWBW, therefore use the same algorithm as
+ ; ROMWBW so if the probe fails we at least expect it to have failed
+ ; before we run.
+ ;
+ ; FIXME: see if we can cleanly ask ROMWBW for the device type
+ ;
+
+ ;
+ ; This could be the ACIA control port. If so we mash the settings
+ ; up but that is ok as we will port them back in the ACIA probe
+ ;
+
+ xor a
+ ld c,#SIOA_C
+ out (c),a ; RR0
+ in b,(c) ; Save RR0 value
+ inc a
+ out (c),a ; RR1
+ in a,(c)
+ cp b ; Same from both reads - not an SIO
+
+ jr z, try_acia
; Repeat the check on SIO B
- in a,(SIOB_C)
- and #0x2C
- cp #0x2C
- jr z, is_sio
+
+ xor a
+ ld c,#SIOB_C
+ out (c),a ; RR0
+ in b,(c) ; Save RR0 value
+ inc a
+ out (c),a ; RR1
+ in a,(c)
+ cp b ; Same from both reads - not an SIO
+
+ jr nz, is_sio
+
try_acia:
;
; Look for an ACIA
ld a, #ACIA_RTS_LOW_A
out (ACIA_C),a ; Initialise ACIA
- ld a,#2
- ld (_ser_type),a
+ ld a,#1
+ ld (_acia_present),a
jp serial_up
;
; At least until RC2014 grows a nice keyboard/display card!
;
not_acia_either:
- xor a
- ld (_ser_type),a
jp serial_up
;
; We have an SIO so do the required SIO hdance
;
-is_sio: ld a,b
- ld (_ser_type),a
+is_sio: ld a,#1
+ ld (_sio_present),a
ld hl,#sio_setup
ld bc,#0xA00 + SIOA_C ; 10 bytes to SIOA_C
ld bc,#0x0A00 + SIOB_C ; and to SIOB_C
otir
+ xor a
+ ld c,#SIOC_C
+ out (c),a ; RR0
+ in b,(c) ; Save RR0 value
+ inc a
+ out (c),a ; RR1
+ in a,(c)
+ cp b ; Same from both reads - not an SIO
+
+ jr z, serial_up
+
+ ; Repeat the check on SIO B
+
+ xor a
+ ld c,#SIOD_C
+ out (c),a ; RR0
+ in b,(c) ; Save RR0 value
+ inc a
+ out (c),a ; RR1
+ in a,(c)
+ cp b ; Same from both reads - not an SIO
+
+ jr z, serial_up
+
+ ld a,#0x01
+ ld (_sio1_present),a
+
+ ld hl,#sio_setup
+ ld bc,#0xA00 + SIOC_C ; 10 bytes to SIOC_C
+ otir
+ ld hl,#sio_setup
+ ld bc,#0x0A00 + SIOD_C ; and to SIOD_C
+ otir
+
+
serial_up:
- im 1 ; set CPU interrupt mode
- ret
+
+ ; ---------------------------------------------------------------------
+ ; Initialize CTC
+ ;
+ ; Need to do autodetect on this
+ ;
+ ; We must initialize all channels of the CTC. The documentation
+ ; states that the initial CTC state is undefined and we don't want
+ ; random interrupt surprises
+ ;
+ ; ---------------------------------------------------------------------
+
+ ;
+ ; Defense in depth - shut everything up first
+ ;
+
+ ld a,#0x43
+ out (CTC_CH0),a ; set CH0 mode
+ out (CTC_CH1),a ; set CH1 mode
+ out (CTC_CH2),a ; set CH2 mode
+ out (CTC_CH3),a ; set CH3 mode
+
+ ;
+ ; Probe for a CTC
+ ;
+
+ ld a,#0x47 ; CTC 2 as counter
+ out (CTC_CH2),a
+ ld a,#0xAA ; Set a count
+ out (CTC_CH2),a
+ in a,(CTC_CH2)
+ cp #0xAA ; Should not have changed
+ jr nz, no_ctc
+
+ ld a,#0x07
+ out (CTC_CH2),a
+ ld a,#2
+ out (CTC_CH2),a
+
+ ; We are now counting down from 2 very fast, so should only see
+ ; those values on the bus
+
+ ld b,#0
+ctc_check:
+ in a,(CTC_CH2)
+ and #0xFC
+ jr nz, no_ctc
+ djnz ctc_check
+
+ ;
+ ; Looks like we have a CTC
+ ;
+
+have_ctc:
+ ld a,#1
+ ld (_ctc_present),a
+
+ ;
+ ; Set up timer for 200Hz
+ ;
+
+ ld a,#0xB5
+ out (CTC_CH2),a
+ ld a,#144
+ out (CTC_CH2),a ; 200 Hz
+
+ ;
+ ; Set up counter CH3 for official SIO (the SC110 sadly can't be
+ ; used this way).
+
+ ld a,#0x47
+ out (CTC_CH3),a
+ ld a,#255
+ out (CTC_CH3),a
+
+no_ctc:
+ ; Done CTC Stuff
+ ; ---------------------------------------------------------------------
+
+ im 1 ; set Z80 CPU interrupt mode 1
+ ret
RTS_LOW .EQU 0xEA