ASRCS = crt0.s tricks.s commonmem.s rc2014.s
-CSRCS = devices.c main.c devtty.c
+CSRCS = devices.c main.c devtty.c platform_ide.c
DISCARD_CSRCS = discard.c
DISCARD_DSRCS = ../dev/devide_discard.c ../dev/ds1302_discard.c ../dev/mbr.c
DSRCS = ../dev/devide.c ../dev/blkdev.c
Supported Hardware
RC2014 SBC64 mainboard or MB64 (see notes)
- DS1302 RTC card (required)
-
- (CTC is not yet supported but will be a far faster/nicer option once
- it is. At that point you will need CTC or RTC or both)
+ DS1302 RTC card (need RTC or CTC or both)
+ CTC card at 0x88 for SIO timing and for system clock. The CTC card
+ must be jumpered for CTC 1 to drive CLK2 and CTC2 zero crossing into
+ CTC3. (A CTC is strongly recommended)
Options
-
- RC2014 SIO/2 board (or compatible). ACIA is not supported.
RC2014 Joystick
In Progress
- CTC at 0x88 for SIO timing and for system clock
+
+ RC2014 SIO/2 board (or compatible). ACIA is not supported.
Most other RC2014 cards supported by other platforms should also be
possible to add if anyone needs them.
- Suspend to RAM/Resume
+ Suspend to RAM/Resume (basics should work)
+
+Unsupported
+ SC110 (no way to run one timer into another which is needed for
+ proper timer use without IM2)
Things That Don't Work (yet)
- Flow control
- SIO speed changing
+ SIO/2 flow control
Notes
The Z80MB64 is 22MHz which means there are not yet RTC or CTC for it!
Apart from remembering the emulated hard disk has a 1K header all is the same
- ./rc2014 -m z80sbc64 -r sbc64.ram -i sbc64.ide -R
+ ./rc2014 -m z80sbc64 -r sbc64.ram -i sbc64.ide -R -c
(and to bootstrap initially copy Z80SBCLD.BIN to sbc64.ram)
#define CONFIG_RTC_FULL
#define CONFIG_NO_CLOCK
+/* We can suspend to RAM */
+#define CONFIG_PLATFORM_SUSPEND
+
/* IDE/CF support */
#define CONFIG_IDE
/* 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) */
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;
+
+ baud = t->c_cflag & CBAUD;
+ if (baud < B300)
+ baud = B300;
used(flags);
+
/* 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)
if (t->c_cflag & PARODD)
r |= 0x02;
sio_r[3] = r;
+
sio_r[5] = 0x8A | ((t->c_cflag & CSIZE) << 1);
}
if (minor == 1)
return;
sio2_setup(minor, flags);
- sio2_otir(SIO0_BASE + 2 * (minor - 1));
+ sio2_otir(SIO0_BASE + 2 * (minor - 2));
/* We need to do CTS/RTS support and baud setting on channel 2
yet */
}
+void tty_resume(void)
+{
+ tty_setup(1, 0);
+ tty_setup(2, 0);
+ tty_setup(3, 0);
+}
+
int tty_carrier(uint8_t minor)
{
uint8_t c;
void tty_putc(uint8_t minor, unsigned char c)
{
- if (minor == 1)
+ if (minor == 1) {
+ irqflags_t irq = di();
cpld_bitbang(c);
+ irqrestore(irq);
+ }
if (minor == 2)
SIOA_D = c;
else if (minor == 2)
tty_putc(TTYDEV - 512, '\r');
tty_putc(TTYDEV - 512, c);
}
+
+int rctty_open(uint8_t minor, uint16_t flag)
+{
+ if (minor > 1 && !sio_present) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return tty_open(minor, flag);
+}
void tty_putc(uint8_t minor, unsigned char c);
void tty_pollirq_sio(void);
void tty_poll_cpld(void);
+int rctty_open(uint8_t minor, uint16_t flag);
extern uint8_t ser_type;
#include <ds1302.h>
#include <devide.h>
#include <blkdev.h>
+#include <rc2014.h>
#include "config.h"
void map_init(void)
/* The high bits do nothing but it's a cheap way to avoid 0x00 */
pagemap_add(0x12);
pagemap_add(0x10);
+
+ if (ctc_present)
+ kputs("Z80 CTC detected.\n");
}
/*
tty.rel
devsys.rel
usermem.rel
-usermem_std-z80.rel
platform-rc2014-sbc64/discard.rel
platform-rc2014-sbc64/devtty.rel
platform-rc2014-sbc64/mbr.rel
platform-rc2014-sbc64/blkdev.rel
platform-rc2014-sbc64/devide.rel
platform-rc2014-sbc64/devide_discard.rel
+platform-rc2014-sbc64/platform_ide.rel
platform-rc2014-sbc64/ds1302.rel
platform-rc2014-sbc64/ds1302_discard.rel
platform-rc2014-sbc64/ds1302_rc2014.rel
#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 swap_dev = 0xFFFF;
uint16_t ramtop = 0x7E00;
-uint8_t ctc = 0;
+uint8_t ctc_present;
+uint8_t sio_present;
void platform_discard(void)
{
void platform_idle(void)
{
+ irqflags_t irq = di();
/* 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 */
+ need to see what 1/10th sec looks like in poll loops */
tty_poll_cpld();
+ irqrestore(irq);
+
if (!idlect++)
sync_clock();
}
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)
{
- tty_pollirq_sio();
+ static uint8_t last_ctc_ch2;
+
+ tty_poll_cpld();
+ if (sio_present)
+ tty_pollirq_sio();
+ if (ctc_present) {
+ uint8_t n = 255 - CTC_CH3;
+ CTC_CH3 = 0x47;
+ CTC_CH3 = 255;
+ timer_tick(n);
+ }
}
/*
*/
void sync_clock(void)
{
- if (!ctc) {
+ if (!ctc_present) {
irqflags_t irq = di();
int16_t tmp;
if (!re_enter++) {
re_enter--;
irqrestore(irq);
}
+ tty_poll_cpld();
}
/*
__sfr __at (SIO0_BASE + 2) SIOB_C;
__sfr __at (SIO0_BASE + 3) SIOB_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 void cpld_bitbang(uint8_t c) __z88dk_fastcall;
+extern uint8_t ctc_present;
+extern uint8_t sio_present;
+
#endif
.globl platform_interrupt_all
.globl _platform_reboot
.globl _platform_monitor
+ .globl _platform_suspend
.globl _bufpool
.globl _int_disabled
.globl _cpld_bitbang
.globl interrupt_handler
.globl unix_syscall_entry
.globl nmi_handler
+ .globl _suspend
+ .globl _ctc_present
+ .globl _tty_resume
+ .globl _ide_resume
; exported debugging tools
.globl outchar
SIOB_C .EQU SIOA_D+2
SIOB_D .EQU SIOA_D+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
;=========================================================================
ld hl,#64
ld (_procmem), hl
- ; FIXME: autodetect SIO
+ call program_kvectors
+
+resume_hardware:
+ ; FIXME: autodetect SIO and check for second SIO
ld hl,#sio_setup
ld bc,#0xA00 + SIOA_C ; 10 bytes to SIOA_C
; 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
; ---------------------------------------------------------------------
out (0x1f),a
rst 0
+_platform_suspend:
+ call _suspend
+ ; Re-initialize the CTC and basic SIO setup
+ call resume_hardware
+ ; Restore the live SIO configuration
+ call _tty_resume
+ ; Now fix the IDE controller
+ ; We can't use the generic code for probing and setup because we
+ ; discarded it at boot!
+ call _ide_resume
+ ret
+
_int_disabled:
.db 1
pagereg:
ld (hl),#0x00
ldir
- ; now install the interrupt vector at 0x0038
+program_kvectors:
+ ; This is ok as for the bank 3 it's already JP
+
ld a,#0xC3 ; JP instruction
+ ld (0x0000),a ; Must be present for NULL checker
+
+ ; now install the interrupt vector at 0x0038
ld (0x0038),a
ld hl,#interrupt_handler
ld (0x0039),hl
.include "kernel.def"
.include "../kernel.def"
- .include "../lib/z80fixedbank.s"
+ .include "../lib/z80fixedbank-core.s"
+;
+; This is related so we will keep it here. Copy the process memory
+; for a fork. a is the page base of the parent, c of the child
+;
+; We violate all the rules of good programming for speed here.
+;
+; Interrupts are off so I guess the stack pointer is spare (Watch
+; out for NMI if you ever need it!)
+;
+bankfork:
+ ld (cpatch0 + 1),a ; patch parent into loop
+ ld a,c
+ ld (cpatch1 + 1),a ; patch child into loop
+ ;
+ ; Set up ready for the copy
+ ;
+ ld (spcache),sp
+ ; 32256 bytes to copy.
+ ; Stack pointer at the target buffer
+ ld sp,#PROGBASE ; Base of memory to fork
+ ; 8 outer loops
+ ld a,#8
+ ld (copyct),a
+ ld a,#252 ; Count 252 * 16 byte copies
+copyloop:
+ ex af,af' ; Save A as we need an A for ioports
+cpatch0:
+ ld a,#0 ; parent bank (patched in for speed)
+bankpatch1:
+ out (0x1f),a
+ pop bc ; copy 16 bytes out of parent
+ pop de
+ pop hl
+ exx
+ pop bc
+ pop de
+ pop hl
+ pop ix
+ pop iy
+ ld (sp_patch+1),sp
+cpatch1:
+ ld a,#0 ; child bank (also patched in for speed)
+bankpatch2:
+ out (0x1f),a
+ push iy ; and put them back into the child
+ push ix
+ push hl
+ push de
+ push bc
+ exx
+ push hl
+ push de
+ push bc
+ ex af,af' ; Get counter back
+ dec a
+ jr z, setdone ; 252 loops ?
+copy_cont:
+sp_patch:
+ ld sp,#0
+ jp copyloop
+;
+; This outer loop only runs 8 times so isn't quite so performance
+; critical
+;
+setdone:
+ ld hl,#copyct
+ dec (hl)
+ jr z, copy_over
+ ld a,#252
+ jr copy_cont
+copy_over:
+ ;
+ ; Get the stack back
+ ;
+ ld sp,(spcache)
+ ;
+ ; And the correct kernel bank.
+ ;
+ jp map_kernel
+
+spcache:
+ .word 0
+copyct:
+ .byte 0
+
+
+;
+; The second set of very performance sensitive routines are accesses
+; to user space. We thus provide our own modified versions of these
+; for speed
+;
+; This works because user space occupies 0000-7FFF and we carefully
+; pack the kernel up so that only kernel code and vectors live below
+; 8000. In other words there is no case where we need to copy between
+; the low 32K of user and the low 32K of kernel. This in turn means
+; we know we can always map it all and ldir.
+;
+
+ ; exported symbols
+ .globl __uget
+ .globl __ugetc
+ .globl __ugetw
+
+ .globl outcharhex
+ .globl outhl
+
+ .globl __uput
+ .globl __uputc
+ .globl __uputw
+ .globl __uzero
+
+ .globl map_process_always
+ .globl map_kernel
+;
+; We need these in common as they bank switch
+;
+ .area _COMMONMEM
+
+;
+; The basic operations are copied from the standard one. Only the
+; blk transfers are different. uputget is a bit different as we are
+; not doing 8bit loop pairs.
+;
+uputget:
+ ; load DE with the byte count
+ ld c, 8(ix) ; byte count
+ ld b, 9(ix)
+ ld a, b
+ or c
+ ret z ; no work
+ ; load HL with the source address
+ ld l, 4(ix) ; src address
+ ld h, 5(ix)
+ ; load DE with destination address (in userspace)
+ ld e, 6(ix)
+ ld d, 7(ix)
+ ret ; Z is still false
+
+__uputc:
+ pop bc ; return
+ pop de ; char
+ pop hl ; dest
+ push hl
+ push de
+ push bc
+ call map_process_always
+ ld (hl), e
+uputc_out:
+ jp map_kernel ; map the kernel back below common
+
+__uputw:
+ pop bc ; return
+ pop de ; word
+ pop hl ; dest
+ push hl
+ push de
+ push bc
+ call map_process_always
+ ld (hl), e
+ inc hl
+ ld (hl), d
+ jp map_kernel
+
+__ugetc:
+ pop bc ; return
+ pop hl ; address
+ push hl
+ push bc
+ call map_process_always
+ ld l, (hl)
+ ld h, #0
+ jp map_kernel
+
+__ugetw:
+ pop bc ; return
+ pop hl ; address
+ push hl
+ push bc
+ call map_process_always
+ ld a, (hl)
+ inc hl
+ ld h, (hl)
+ ld l, a
+ jp map_kernel
+
+__uput:
+ push ix
+ ld ix, #0
+ add ix, sp
+ call uputget ; source in HL dest in DE, count in BC
+ jr z, uput_out ; but count is at this point magic
+ call map_process_always
+ ldir
+uput_out:
+ call map_kernel
+ pop ix
+ ld hl, #0
+ ret
+
+__uget:
+ push ix
+ ld ix, #0
+ add ix, sp
+ call uputget ; source in HL dest in DE, count in BC
+ jr z, uput_out ; but count is at this point magic
+ call map_process_always
+ ldir
+ jr uput_out
+
+;
+__uzero:
+ pop de ; return
+ pop hl ; address
+ pop bc ; size
+ push bc
+ push hl
+ push de
+ ld a, b ; check for 0 copy
+ or c
+ ret z
+ call map_process_always
+ ld (hl), #0
+ dec bc
+ ld a, b
+ or c
+ jp z, uputc_out
+ ld e, l
+ ld d, h
+ inc de
+ ldir
+ jp uputc_out