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.
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
* 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 ?
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
#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)
#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
{ 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) */
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] = {
tcflag_t *termios_mask[NUM_DEV_TTY + 1] = {
NULL,
uart0_mask,
- uart1_mask
+ 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;
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 */
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;
}
}
-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);
}
}
{
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 <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 */
/* 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)
#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)
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;
}
/*
*/
void sync_clock(void)
{
- if (!timermsr) {
+ if (!ctc_present) {
irqflags_t irq = di();
int16_t tmp;
if (!re_enter++) {
while(tmp--) {
timer_interrupt();
}
- /* FIXME: need to cover ACIA option.. */
- tty_pollirq_sio();
}
}
re_enter--;
#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 */
__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
.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"
; 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
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:
+
; ---------------------------------------------------------------------
; 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
; ---------------------------------------------------------------------
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:
; 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