From: Alan Cox Date: Sun, 27 Jan 2019 15:34:55 +0000 (+0000) Subject: rc2014-sbc64: Further work X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=a160ac528b0c57c5399c554cb7343cde0b3ecfe5;p=FUZIX.git rc2014-sbc64: Further work - 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 --- diff --git a/Kernel/platform-rc2014-sbc64/Makefile b/Kernel/platform-rc2014-sbc64/Makefile index a5da4a0b..d27ae68e 100644 --- a/Kernel/platform-rc2014-sbc64/Makefile +++ b/Kernel/platform-rc2014-sbc64/Makefile @@ -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 diff --git a/Kernel/platform-rc2014-sbc64/README b/Kernel/platform-rc2014-sbc64/README index e1439feb..df3ab7d3 100644 --- a/Kernel/platform-rc2014-sbc64/README +++ b/Kernel/platform-rc2014-sbc64/README @@ -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) diff --git a/Kernel/platform-rc2014-sbc64/config.h b/Kernel/platform-rc2014-sbc64/config.h index b6f8b8d8..39660528 100644 --- a/Kernel/platform-rc2014-sbc64/config.h +++ b/Kernel/platform-rc2014-sbc64/config.h @@ -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 diff --git a/Kernel/platform-rc2014-sbc64/devices.c b/Kernel/platform-rc2014-sbc64/devices.c index f3f1029c..d849f36a 100644 --- a/Kernel/platform-rc2014-sbc64/devices.c +++ b/Kernel/platform-rc2014-sbc64/devices.c @@ -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) */ diff --git a/Kernel/platform-rc2014-sbc64/devtty.c b/Kernel/platform-rc2014-sbc64/devtty.c index d566c9a9..33070d8e 100644 --- a/Kernel/platform-rc2014-sbc64/devtty.c +++ b/Kernel/platform-rc2014-sbc64/devtty.c @@ -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); +} diff --git a/Kernel/platform-rc2014-sbc64/devtty.h b/Kernel/platform-rc2014-sbc64/devtty.h index 22427d5b..fd42a603 100644 --- a/Kernel/platform-rc2014-sbc64/devtty.h +++ b/Kernel/platform-rc2014-sbc64/devtty.h @@ -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; diff --git a/Kernel/platform-rc2014-sbc64/discard.c b/Kernel/platform-rc2014-sbc64/discard.c index e48753af..5d998f24 100644 --- a/Kernel/platform-rc2014-sbc64/discard.c +++ b/Kernel/platform-rc2014-sbc64/discard.c @@ -5,6 +5,7 @@ #include #include #include +#include #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"); } /* diff --git a/Kernel/platform-rc2014-sbc64/fuzix.lnk b/Kernel/platform-rc2014-sbc64/fuzix.lnk index a4884a62..3cd9d8e4 100644 --- a/Kernel/platform-rc2014-sbc64/fuzix.lnk +++ b/Kernel/platform-rc2014-sbc64/fuzix.lnk @@ -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 diff --git a/Kernel/platform-rc2014-sbc64/main.c b/Kernel/platform-rc2014-sbc64/main.c index 955e0df5..376d3f7d 100644 --- a/Kernel/platform-rc2014-sbc64/main.c +++ b/Kernel/platform-rc2014-sbc64/main.c @@ -6,12 +6,14 @@ #include #include #include +#include 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(); } /* diff --git a/Kernel/platform-rc2014-sbc64/rc2014.h b/Kernel/platform-rc2014-sbc64/rc2014.h index f4baac3b..b98a7846 100644 --- a/Kernel/platform-rc2014-sbc64/rc2014.h +++ b/Kernel/platform-rc2014-sbc64/rc2014.h @@ -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 diff --git a/Kernel/platform-rc2014-sbc64/rc2014.s b/Kernel/platform-rc2014-sbc64/rc2014.s index cfb4386d..b1047949 100644 --- a/Kernel/platform-rc2014-sbc64/rc2014.s +++ b/Kernel/platform-rc2014-sbc64/rc2014.s @@ -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 @@ -32,6 +33,10 @@ .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 diff --git a/Kernel/platform-rc2014-sbc64/tricks.s b/Kernel/platform-rc2014-sbc64/tricks.s index 015ba064..b63a607b 100644 --- a/Kernel/platform-rc2014-sbc64/tricks.s +++ b/Kernel/platform-rc2014-sbc64/tricks.s @@ -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