rc2014-sbc64: Further work
authorAlan Cox <alan@linux.intel.com>
Sun, 27 Jan 2019 15:34:55 +0000 (15:34 +0000)
committerAlan Cox <alan@linux.intel.com>
Sun, 27 Jan 2019 15:34:55 +0000 (15:34 +0000)
- Disable interrupts during a bitbang serial then it works rather better
- Tie SIO to a presence check (not yet probed for)
- Autodetect CTC
- CTC programming and default to CTC if possible
- Fix interrupt vector setup
- Assorted cleanups
- First cut at suspend/resume properly

12 files changed:
Kernel/platform-rc2014-sbc64/Makefile
Kernel/platform-rc2014-sbc64/README
Kernel/platform-rc2014-sbc64/config.h
Kernel/platform-rc2014-sbc64/devices.c
Kernel/platform-rc2014-sbc64/devtty.c
Kernel/platform-rc2014-sbc64/devtty.h
Kernel/platform-rc2014-sbc64/discard.c
Kernel/platform-rc2014-sbc64/fuzix.lnk
Kernel/platform-rc2014-sbc64/main.c
Kernel/platform-rc2014-sbc64/rc2014.h
Kernel/platform-rc2014-sbc64/rc2014.s
Kernel/platform-rc2014-sbc64/tricks.s

index a5da4a0..d27ae68 100644 (file)
@@ -1,5 +1,5 @@
 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
index e1439fe..df3ab7d 100644 (file)
@@ -3,29 +3,31 @@ Fuzix for the Z80SBC64 and in theory Z80MB64 platforms
 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!
@@ -72,7 +74,7 @@ Emulation
 
 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)
 
index b6f8b8d..3966052 100644 (file)
@@ -54,6 +54,9 @@ extern unsigned int swap_dev;
 #define CONFIG_RTC_FULL
 #define CONFIG_NO_CLOCK
 
+/* We can suspend to RAM */
+#define CONFIG_PLATFORM_SUSPEND
+
 /* IDE/CF support */
 #define CONFIG_IDE
 
index f3f1029..d849f36 100644 (file)
@@ -19,7 +19,7 @@ struct devsw dev_tab[] =  /* The device driver switch table */
   /* 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) */
index d566c9a..33070d8 100644 (file)
@@ -58,16 +58,53 @@ 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;
+
+       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)
@@ -75,6 +112,7 @@ static void sio2_setup(uint8_t minor, uint8_t flags)
        if (t->c_cflag & PARODD)
                r |= 0x02;
        sio_r[3] = r;
+
        sio_r[5] = 0x8A | ((t->c_cflag & CSIZE) << 1);
 }
 
@@ -83,11 +121,18 @@ void tty_setup(uint8_t minor, uint8_t flags)
        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;
@@ -174,8 +219,11 @@ void tty_poll_cpld(void)
 
 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)
@@ -237,3 +285,12 @@ void kputchar(char c)
                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);
+}
index 22427d5..fd42a60 100644 (file)
@@ -4,6 +4,7 @@
 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;
 
index e48753a..5d998f2 100644 (file)
@@ -5,6 +5,7 @@
 #include <ds1302.h>
 #include <devide.h>
 #include <blkdev.h>
+#include <rc2014.h>
 #include "config.h"
 
 void map_init(void)
@@ -16,6 +17,9 @@ void pagemap_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");
 }
 
 /*
index a4884a6..3cd9d8e 100644 (file)
@@ -30,13 +30,13 @@ bankfixed.rel
 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
index 955e0df..376d3f7 100644 (file)
@@ -6,12 +6,14 @@
 #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)
 {
@@ -29,10 +31,13 @@ static uint8_t idlect;
 
 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();
 }
@@ -43,9 +48,31 @@ 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)
 {
-       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);
+       }
 }
 
 /*
@@ -88,7 +115,7 @@ static void sync_clock_read(void)
  */
 void sync_clock(void)
 {
-       if (!ctc) {
+       if (!ctc_present) {
                irqflags_t irq = di();
                int16_t tmp;
                if (!re_enter++) {
@@ -106,6 +133,7 @@ void sync_clock(void)
                re_enter--;
                irqrestore(irq);
        }
+       tty_poll_cpld();
 }
 
 /*
index f4baac3..b98a784 100644 (file)
@@ -12,7 +12,15 @@ __sfr __at (SIO0_BASE + 1) SIOA_D;
 __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
index cfb4386..b104794 100644 (file)
@@ -20,6 +20,7 @@
        .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
@@ -56,13 +61,6 @@ SIOA_D               .EQU    SIOA_D+1
 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
 ;=========================================================================
@@ -86,7 +84,10 @@ init_hardware:
        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
@@ -104,21 +105,73 @@ serial_up:
        ; 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
         ; ---------------------------------------------------------------------
 
@@ -145,6 +198,18 @@ _platform_reboot:
        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:
@@ -173,8 +238,13 @@ _program_vectors:
        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
index 015ba06..b63a607 100644 (file)
@@ -1,5 +1,236 @@
         .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