rc2014: Support for CTC, fix a load of serial related bugs
authorAlan Cox <alan@linux.intel.com>
Sun, 27 Jan 2019 17:55:31 +0000 (17:55 +0000)
committerAlan Cox <alan@linux.intel.com>
Sun, 27 Jan 2019 17:55:31 +0000 (17:55 +0000)
Also add support for a second SIO2 card

Kernel/platform-rc2014/README
Kernel/platform-rc2014/config.h
Kernel/platform-rc2014/devices.c
Kernel/platform-rc2014/devtty.c
Kernel/platform-rc2014/devtty.h
Kernel/platform-rc2014/discard.c
Kernel/platform-rc2014/main.c
Kernel/platform-rc2014/rc2014.h
Kernel/platform-rc2014/rc2014.s

index 0f262c6..5bb10b0 100644 (file)
@@ -17,14 +17,12 @@ Supported Hardware
        default ROM and RAM boards.
 
        A serial IO board. Either an RC2014 SIO/2 board or a 68B50 ACIA board.
-       If you have a Scott Baker SIO/2 card you'll need to see config.h
 
-       DS1302 RTC at 0xC0 (eventually CTC and no RTC will be allowed too)
+       DS1302 RTC at 0xC0      } You must have one or both
+       RC2014 CTC at 0x88      } CTC strongly recommended
 
        Options:
 
-       CTC board at 0x88
-
        VFD Display. If config.h:CONFIG_VFD_TERM is defined, then the VFD
        Terminal will be supported. This will display all output to the serial
        port on the VFD.
@@ -35,22 +33,19 @@ Supported Hardware
 
        RC2014 Joystick
 
-In Progress
-
        PPIDE
 
 Unsupported Hardware
 
-       SC108/SC114 or other CPU boards with their own banking instead of the
-       512K ROM/RAM
+       SC108/SC114/Z80SBC64 or other CPU boards with their own banking instead
+       of the 512K ROM/RAM
 
        SC114 bit bang serial
 
        Z80 PIO cards (really because I've no idea how to present them)
 
-       SC110 CTC/serial (it uses CTC 1 for the serial baud but we try and
-       use it for other things). Will rework the core code to use the CTCs
-       differently to fix this at some point
+       SC110 CTC/serial (does not appear to be able to chain CTC pairs, as
+       is neeed for IM1 mode).
 
 
 Things To Note When Modifying
@@ -67,26 +62,9 @@ Things that don't work
 
   * Flow control isn't yet enabled for the serial port.
 
-  * PPIDE
-
 Stuff To Do
 
-  * Rework the CTCs if we can so we use CTC2 as a timer for the CTC clock and
-    use CTC3 to count CTC0 overflows. That way we can use the CTC2 interrupt
-    and the CTC3 value together in order to a) spot missed events and b) allow
-    us to run without using IM2 given the poor IM2 support on RC2014. That
-    then still allows us to use CTC1 for UART speed, and hopefully CTC0 for
-    single step debugging. Note that we are forced to use CTC1 for serial
-    for the SC110 card, and we want adjacent CTCs for jumpering convenience
-    for the timer. Also there is no ZT3 so it's the one timer that can't
-    chain so is best for the tick counter.
-
-  * Detect and optimize code paths in CTC mode - the extra DS1302 accesses we
-    have to do are really expensive.
-
-  * Can we detect which kind of SIO and CTC we have and where
-
-  * Other tty options - can we autodetect 16550A at 0x80. Detect SIO at 0x84.
+  * Other tty options - can we autodetect 16550A at 0x80.
 
   * Probe both 0x10 and 0xE0 for CF ?
 
@@ -107,7 +85,7 @@ Stuff To Do
     do some of the combinations we have today with the TMS9918A as well unless
     we go to banked syscall (Which is doable)
 
-  * Push the tty buffers into the banked space to give us room for four ttys
+  * Push the tty buffers into the banked space to give us room for more ttys
 
   * Go the soft IRQ route with fast SIO serial handling for rx interrupts and
     flip buffers. Also raises space issues as we'll need 256 bytes per SIO
index 6de7c58..506bfb9 100644 (file)
@@ -99,7 +99,7 @@ extern unsigned int swap_dev;
 #define CONFIG_INPUT                   /* Input device for joystick */
 #define CONFIG_INPUT_GRABMAX   0       /* No keyboard to grab */
 
-#define NUM_DEV_TTY 2
+#define NUM_DEV_TTY 4
 
 /* UART0 as the console */
 #define BOOT_TTY (512 + 1)
@@ -107,9 +107,4 @@ extern unsigned int swap_dev;
 
 #define TTYDEV   BOOT_TTY /* Device used by kernel for messages, panics */
 
-/* The Scott Baker SIO has a non-standard layout (it predates the official one) */
-/* You'll need to define this if you have a Scott Baker SIO2 card, or submit
-   a fancier autodetect! Also you'll need to change rc2014.s */
-#undef CONFIG_SIO_BAKER
-
 #define platform_copyright()           // for now
index 7b5a8fc..0e919f7 100644 (file)
@@ -25,7 +25,7 @@ struct devsw dev_tab[] =  /* The device driver switch table */
   {  no_open,      no_close,   no_rdwr,        no_rdwr,        no_ioctl},
 #endif
   /* 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) */
index ae96538..fe2761d 100644 (file)
 
 static char tbuf1[TTYSIZ];
 static char tbuf2[TTYSIZ];
+static char tbuf3[TTYSIZ];
+static char tbuf4[TTYSIZ];
 
 static uint8_t sleeping;
 
-uint8_t ser_type = 1;
-
 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] = {
@@ -40,7 +42,9 @@ static tcflag_t uart1_mask[4] = {
 tcflag_t *termios_mask[NUM_DEV_TTY + 1] = {
        NULL,
        uart0_mask,
-       uart1_mask
+       uart1_mask,
+       uart0_mask,
+       uart0_mask
 };
 
 uint8_t sio_r[] = {
@@ -49,13 +53,52 @@ 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)
@@ -68,13 +111,13 @@ static void sio2_setup(uint8_t minor, uint8_t flags)
 
 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 */
@@ -117,31 +160,30 @@ void tty_setup(uint8_t minor, uint8_t flags)
 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;
        uint8_t progress;
 
        /* Check for an interrupt */
-//     SIOA_C = 0;
-//     if (!(SIOA_C & 2))
-//             return;
+       SIOA_C = 0;
+       if (!(SIOA_C & 2))
+               return;
 
        /* FIXME: need to process error/event interrupts as we can get
           spurious characters or lines on an unused SIO floating */
@@ -159,7 +201,7 @@ void tty_pollirq_sio(void)
                        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
                }
@@ -176,9 +218,9 @@ void tty_pollirq_sio(void)
                        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) {
@@ -190,6 +232,64 @@ void tty_pollirq_sio(void)
        } 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;
@@ -204,22 +304,17 @@ void tty_pollirq_acia(void)
        }
 }
 
-static char hex[] = { "0123456789ABCDEF" };
-
 void tty_putc(uint8_t minor, unsigned char c)
 {
-       if (ser_type == 1) {
-               if (minor == 1) {
-                       SIOA_D = c;
 #ifdef CONFIG_VFD_TERM
-                       vfd_term_write(c);
+       if (minor == 1)
+               vfd_term_write(c);
 #endif
-               } 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);
        }
 }
 
@@ -240,35 +335,29 @@ 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! */
@@ -280,3 +369,20 @@ void kputchar(char c)
        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);
+}
index 518450e..6bc732c 100644 (file)
@@ -2,9 +2,9 @@
 #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
index c4016de..7245ea3 100644 (file)
@@ -6,8 +6,8 @@
 #include <devide.h>
 #include <blkdev.h>
 #include <ppide.h>
+#include <rc2014.h>
 #include "config.h"
-#include "devrd.h"
 #include "vfd-term.h"
 
 /* Everything in here is discarded after init starts */
@@ -36,6 +36,15 @@ void pagemap_init(void)
 
        /* finally add the common area */
        pagemap_add(32 + 3);
+
+       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");
 }
 
 void map_init(void)
index 9740df1..d5e9ed1 100644 (file)
@@ -7,27 +7,29 @@
 #include <devinput.h>
 #include <rtc.h>
 #include <ds1302.h>
+#include <rc2014.h>
 
 extern unsigned char irqvector;
-uint8_t timermsr = 0;
 uint16_t swap_dev = 0xFFFF;
 
+uint8_t acia_present;
+uint8_t ctc_present;
+uint8_t sio_present;
+uint8_t sio1_present;
+
 void platform_discard(void)
 {
 }
 
 void platform_idle(void)
 {
-       /* FIXME: for the non CTC case we should poll the DS1302 here and
-          fake up appopriate timer events */
-       /* Let's go to sleep while we wait for something to interrupt us;
-        * Makes the HALT LED go yellow, which amuses me greatly. */
-//     __asm halt __endasm;
-       irqflags_t irq = di();
-       sync_clock();
-       /* FIXME: need to cover ACIA option.. */
-       tty_pollirq_sio();
-       irqrestore(irq);
+       if (ctc_present)
+               __asm halt __endasm;
+       else {
+               irqflags_t irq = di();
+               sync_clock();
+               irqrestore(irq);
+       }
 }
 
 uint8_t platform_param(unsigned char *p)
@@ -36,18 +38,32 @@ 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 (1) {        /* CTC == 0 when we do the work */
-#ifdef CONFIG_FLOPPY
-               fd_tick();
-#endif
-//             poll_input();
-//             timer_interrupt();
+       if (acia_present)
+               tty_pollirq_acia();
+       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);
        }
-       /* FIXME: need to cover ACIA option.. */
-       tty_pollirq_sio();
-       return;
 }
 
 /*
@@ -90,7 +106,7 @@ static void sync_clock_read(void)
  */
 void sync_clock(void)
 {
-       if (!timermsr) {
+       if (!ctc_present) {
                irqflags_t irq = di();
                int16_t tmp;
                if (!re_enter++) {
@@ -103,8 +119,6 @@ void sync_clock(void)
                                while(tmp--) {
                                        timer_interrupt();
                                }
-                               /* FIXME: need to cover ACIA option.. */
-                               tty_pollirq_sio();
                        }
                }
                re_enter--;
index 0dcf1a8..aaf0cd4 100644 (file)
@@ -3,22 +3,18 @@
 
 #include "config.h"
 
-#define SIO0_IVT 8
-
-#ifdef CONFIG_SIO_BAKER
-#define SIO0_BASE 0x80
-__sfr __at (SIO0_BASE + 2) SIOA_C;
-__sfr __at (SIO0_BASE + 0) SIOA_D;
-__sfr __at (SIO0_BASE + 3) SIOB_C;
-__sfr __at (SIO0_BASE + 1) SIOB_D;
-#else
 /* Standard RC2014 */
 #define SIO0_BASE 0x80
 __sfr __at (SIO0_BASE + 0) SIOA_C;
 __sfr __at (SIO0_BASE + 1) SIOA_D;
 __sfr __at (SIO0_BASE + 2) SIOB_C;
 __sfr __at (SIO0_BASE + 3) SIOB_D;
-#endif
+
+#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 */
 
@@ -26,8 +22,16 @@ __sfr __at (SIO0_BASE + 3) SIOB_D;
 __sfr __at (ACIA_BASE + 0) ACIA_C;
 __sfr __at (ACIA_BASE + 1) ACIA_D;
 
-extern bool boot_from_rom;
+__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
index 1dbf0d9..7e8439f 100644 (file)
        .globl unix_syscall_entry
        .globl nmi_handler
        .globl null_handler
-       .globl _ser_type
+       .globl _acia_present
+       .globl _ctc_present
+       .globl _sio_present
+       .globl _sio1_present
 
        ; exported debugging tools
-       .globl inchar
        .globl outchar
+       .globl inchar
 
         .include "kernel.def"
         .include "../kernel.def"
@@ -57,12 +60,16 @@ RTS_LOW             .EQU    0xEA
 
 ; 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
+SIOA_D         .EQU    SIOA_C+1
+SIOB_C         .EQU    SIOA_C+2
+SIOB_D         .EQU    SIOA_C+3
 
-SIO_IV          .EQU    8               ; Interrupt vector table entry to use
+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
@@ -139,8 +146,8 @@ try_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
 
        ;
@@ -149,14 +156,12 @@ try_acia:
        ; 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
@@ -165,28 +170,118 @@ is_sio:  ld a,b
        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:
+
         ; ---------------------------------------------------------------------
        ; 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
+       ;
        ; ---------------------------------------------------------------------
 
-       ld a,#0x57                      ; counter mode, disable interrupts
+       ;
+       ; Defense in depth - shut everything up first
+       ;
+
+       ld a,#0x43
        out (CTC_CH0),a                 ; set CH0 mode
-       ld a,#0                         ; time constant = 256
-       out (CTC_CH0),a                 ; set CH0 time constant
-       ld a,#0x57                      ; counter mode, FIXME C7 enable interrupts
        out (CTC_CH1),a                 ; set CH1 mode
-       ld a,#180                       ; time constant = 180
-       out (CTC_CH1),a                 ; set CH1 time constant
-       ld a,#0x57                      ; counter mode, disable interrupts
        out (CTC_CH2),a                 ; set CH2 mode
-       ld a,#0x57                      ; counter mode, disable interrupts
        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
         ; ---------------------------------------------------------------------
 
@@ -469,9 +564,9 @@ _sio2_otir:
 outchar:
 
        push af
-       ld a,(_ser_type)
-       cp #2
-       jr z, ocloop_acia
+       ld a, (_acia_present)
+       or a
+       jr nz, ocloop_acia
 
        ; wait for transmitter to be idle
 ocloop_sio:
@@ -503,9 +598,9 @@ out_done:
 ; Outputs: A - received character, F destroyed
 ;=========================================================================
 inchar:
-       ld a,(_ser_type)
-       cp #2
-       jr z,inchar_acia
+       ld a,(_acia_present)
+       or a
+       jr nz,inchar_acia
 inchar_s:
         xor a                           ; read register 0
         out (SIOA_C), a