From: Alan Cox Date: Sat, 5 Jan 2019 21:06:25 +0000 (+0000) Subject: sc114: initial draft of an SC114 port using SCM monitor as much as possible X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=96b873cca283fb51646e23b4ece54b1b9453c613;p=FUZIX.git sc114: initial draft of an SC114 port using SCM monitor as much as possible It's also designed to work on the SC108, except we don't have a suitable bank aware monitor yet. --- diff --git a/Kernel/platform-sc114/Makefile b/Kernel/platform-sc114/Makefile new file mode 100644 index 00000000..eb58531c --- /dev/null +++ b/Kernel/platform-sc114/Makefile @@ -0,0 +1,52 @@ +ASRCS = crt0.s tricks.s commonmem.s sc114.s usermem.s +CSRCS = devices.c main.c devtty.c ide.c +DISCARD_CSRCS = discard.c +DISCARD_DSRCS = ../dev/devide_discard.c ../dev/ds1302_discard.c +DSRCS = ../dev/devide.c ../dev/mbr.c ../dev/blkdev.c +DSRCS += ../dev/ds1302.c +DASRCS = ../dev/ds1302_rc2014.s +NSRCS = + +AOBJS = $(ASRCS:.s=.rel) +COBJS = $(CSRCS:.c=.rel) +DISCARD_COBJS = $(DISCARD_CSRCS:.c=.rel) +DISCARD_DOBJS = $(patsubst ../dev/%.c,%.rel, $(DISCARD_DSRCS)) +DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS)) +DAOBJS = $(patsubst ../dev/%.s,%.rel, $(DASRCS)) +NOBJS = $(patsubst ../dev/net/%.c,%.rel, $(NSRCS)) + +OBJS = $(AOBJS) $(COBJS) $(DOBJS) $(DAOBJS) $(DISCARD_DOBJS) $(DISCARD_COBJS) $(NOBJS) + +CROSS_CCOPTS += -I../dev/ -I../dev/net/ + +JUNK = *.rel *.lst *.asm *.sym *.rst *.map *.ihx *.bin + +all: $(OBJS) + +$(AOBJS): %.rel: %.s + $(CROSS_AS) $(ASOPTS) $< + +$(COBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +$(DOBJS): %.rel: ../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +$(DAOBJS): %.rel: ../dev/%.s + $(CROSS_AS) $(ASOPTS) $@ $< + +$(DISCARD_COBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(DISCARD_DOBJS): %.rel: ../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(NOBJS): %.rel: ../dev/net/%.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +clean: + rm -f $(OBJS) $(JUNK) core *~ diskstrap fuzix.rom + +image: + sdasz80 -o loader.s + sdldz80 -i loader.rel diff --git a/Kernel/platform-sc114/README b/Kernel/platform-sc114/README new file mode 100644 index 00000000..0286940d --- /dev/null +++ b/Kernel/platform-sc114/README @@ -0,0 +1,77 @@ +Experimental port for the SC114 mainboard plus CF card plus RTC card. This +port tries to use the SCM ROM as far as possible and also is intended to +support the SC108 via the same port. It's probably possible to add SC101 if +anyone wanted to. + +The other SCM systems are different +- RC2014 has its own port and normally runs ROMWBW with 512K RAM/ROM +- The RC2014 64K RAM / 32K ROM system has a custom port and can't use the SCM + as it needs the ROM for the OS kernel core +- Linc has a single 16K banked window and would need to spectrum 128K like port + as well as banked kernel +- Tom's SBC has no banked RAM so would need a custom port with the kernel in + ROM like the small RC2014 build + +This is a design that happens to have 128K RAM but the 64K banks are flipped +directly. Fortunately there is a ROM which lives in both banks. + +For the moment we stick the OS in RAM (it would be entertaining to explore +putting the kernel in ROM but as it's not trivially reprogrammable flash +lets leave that for a while). The SCM ROM must have the new API's + +0x2A: + DE = address in secondary bank to read + + returns value in A. + +0x2B: + A = byte to write to secondary bank + DE = address in secondary bank to write + + +Memory map + +Kernel + +0000-00FF Vectors (present in both banks) +0100-EFFF Kernel (common above 0x8000 for ROM dodging) +F000-F1FF UDATA +F200-FCFF Common (mostly valid in both banks) +FD00-FFFF ROM monitor owned + +User + +0000-00FF Vectors (present in both banks) +0100-EFFF User space +F000-F1FF Kernel stack alternative (used during some bank switch ops) +F200-F2FF Istack alternate (we may take interrupts in either bank) +F300-FCFF Common (mostly valid in both banks) +FD00-FFFF ROM monitor owned + + +TO DO + +Write a proper bootloader that can use partition tables properly +Use the extra space to put back some kernel bits we binned from -tiny +Build a suitably high CP/M emuation and test it +CTC support and CTC no-rtc support +PPIDE ? +Finish SC108 compatibility + + + +Long term fun +- OS core in ROM, high memory holding rest (like -tiny) but with two user space +banks ?? +- Clean up the serial code (on all RC2014) + + +Boot Loader + +The standard loader in the ROM is the CP/M loader. It loads 24 sectors (12K) +from sector 0 moves them to the top 12K of RAM and jumps to the address held +in 0xFFFE/FFFF + +We subvert this loader and our own loader lives with a standard PC style +partition table and boots our OS. We ought to replace this with a nice loader +that can load CP/M from a partition as well as Fuzix from a file system. diff --git a/Kernel/platform-sc114/commonmem.s b/Kernel/platform-sc114/commonmem.s new file mode 100644 index 00000000..355bd53a --- /dev/null +++ b/Kernel/platform-sc114/commonmem.s @@ -0,0 +1,10 @@ +; +; Standard common. Nothing special here except to remember that writes +; to common space won't write through all banks +; + .module commonmem + + .area _COMMONMEM + + .include "../cpu-z80/std-commonmem.s" + diff --git a/Kernel/platform-sc114/config.h b/Kernel/platform-sc114/config.h new file mode 100644 index 00000000..0e6957db --- /dev/null +++ b/Kernel/platform-sc114/config.h @@ -0,0 +1,74 @@ +/* Enable to make ^Z dump the inode table for debug */ +#undef CONFIG_IDUMP +/* Enable to make ^A drop back into the monitor */ +#undef CONFIG_MONITOR +/* Profil syscall support (not yet complete) */ +#undef CONFIG_PROFIL +/* Multiple processes in memory at once */ +#undef CONFIG_MULTI +/* Swap based one process in RAM */ +#define CONFIG_SWAP_ONLY +#define CONFIG_PARENT_FIRST +#define MAXTICKS 20 +/* Permit large I/O requests to bypass cache and go direct to userspace */ +#define CONFIG_LARGE_IO_DIRECT +/* One memory bank */ +#define CONFIG_BANKS 1 +#define TICKSPERSEC 10 /* Ticks per second */ +#define PROGBASE 0x0000 /* also data base */ +#define PROGLOAD 0x0100 /* also data base */ +#define PROGTOP 0xF000 /* Top of program */ +#define KERNTOP 0xF000 /* Grow buffers to 0xF000 */ + +#define PROC_SIZE 61 /* Memory needed per process (inc udata) */ + +#define SWAPDEV (swap_dev) /* A variable for dynamic, or a device major/minor */ +extern unsigned int swap_dev; +#define SWAP_SIZE 0x7A /* 61K in blocks (prog + udata) */ +#define SWAPBASE 0x0000 /* start at the base of user mem */ +#define SWAPTOP 0xF000 /* Swap out program */ +#define CONFIG_SPLIT_UDATA /* Adjacent addresses but different bank! */ +#define UDATA_BLKS 1 /* One block of udata */ +#define UDATA_SIZE 512 /* 512 bytes of udata */ +#define MAX_SWAPS 16 /* We will size if from the partition */ +/* Swap will be set up when a suitably labelled partition is seen */ +#define CONFIG_DYNAMIC_SWAP + +/* Until we have a bootloader properly done */ +#define CONFIG_MBR_OFFSET 65536 +/* + * When the kernel swaps something it needs to map the right page into + * memory using map_for_swap and then turn the user address into a + * physical address. For a simple banked setup there is no conversion + * needed so identity map it. + */ +#define swap_map(x) ((uint8_t *)(x)) + +/* We need a tidier way to do this from the loader */ +#define CMDLINE NULL /* Location of root dev name */ +#define BOOTDEVICENAMES "hd#,fd,,rd" + +#define CONFIG_DYNAMIC_BUFPOOL /* we expand bufpool to overwrite the _DISCARD segment at boot */ +#define NBUFS 4 /* Number of block buffers, keep in line with space reserved in zeta-v2.s */ +#define NMOUNTS 2 /* Number of mounts at a time */ + +#define MAX_BLKDEV 4 /* 1 ROM disk, 1 RAM disk, 1 floppy, 1 IDE */ + +/* On-board DS1302, we can read the time of day from it */ +#define CONFIG_RTC +#define CONFIG_RTC_FULL +#define CONFIG_NO_CLOCK + +/* IDE/CF support */ +#define CONFIG_IDE + +/* Device parameters */ +#define NUM_DEV_TTY 2 + +/* UART0 as the console */ +#define BOOT_TTY (512 + 1) +#define TTY_INIT_BAUD B115200 /* Hardwired generally */ + +#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ + +#define platform_copyright() diff --git a/Kernel/platform-sc114/crt0.s b/Kernel/platform-sc114/crt0.s new file mode 100644 index 00000000..712e42f1 --- /dev/null +++ b/Kernel/platform-sc114/crt0.s @@ -0,0 +1,84 @@ +; 2015-02-20 Sergey Kiselev +; 2013-12-18 William R Sowerbutts + + .module crt0 + + ; Ordering of segments for the linker. + ; WRS: Note we list all our segments here, even though + ; we don't use them all, because their ordering is set + ; when they are first seen. + .area _CODE + .area _HOME ; compiler stores __mullong etc in here if you use them + .area _CODE2 + .area _CONST + .area _INITIALIZED + .area _DATA + .area _BSEG + .area _BSS + .area _HEAP + ; note that areas below here may be overwritten by the heap at runtime, so + ; put initialisation stuff in here + .area _BUFFERS ; _BUFFERS grows to consume all before it (up to KERNTOP) + .area _INITIALIZER ; binman copies this to the right place for us + .area _GSINIT ; unused + .area _GSFINAL ; unused + .area _DISCARD + .area _DISCARDL + .area _DISCARDH + .area _COMMONMEM + + ; exported symbols + .globl init + + ; imported symbols + .globl _fuzix_main + .globl init_hardware + .globl s__INITIALIZER + .globl s__COMMONMEM + .globl l__COMMONMEM + .globl s__DISCARD + .globl l__DISCARD + .globl s__DATA + .globl l__DATA + .globl kstack_top + + .include "kernel.def" + + ; startup code (0x100) + .area _CODE + + ; + ; Our loader will enter with A holding the platform ID + ; We preserve A carefully into init_hardware + ; +init: + di + ; switch to stack in high memory + ld sp, #kstack_top + + ; move the common memory where it belongs + ld hl, #s__DATA + ld de, #s__COMMONMEM + ld bc, #l__COMMONMEM + ldir + ; and the discard + ld de, #s__DISCARD + ld bc, #l__DISCARD + ldir + + ld hl, #s__DATA + ld de, #s__DATA + 1 + ld bc, #l__DATA - 1 + ld (hl), #0 + ldir + + ; Hardware setup + call init_hardware + + ; Call the C main routine + call _fuzix_main + + ; fuzix_main() shouldn't return, but if it does... + di +stop: halt + jr stop diff --git a/Kernel/platform-sc114/devices.c b/Kernel/platform-sc114/devices.c new file mode 100644 index 00000000..1a52d3e6 --- /dev/null +++ b/Kernel/platform-sc114/devices.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct devsw dev_tab[] = /* The device driver switch table */ +{ +/* open close read write ioctl */ + /* 0: /dev/hd - block device interface */ +#ifdef CONFIG_IDE + { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl}, +#else + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl}, +#endif + /* 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}, + /* 3: RAM disk */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl}, + /* 4: /dev/mem etc System devices (one offs) */ + { no_open, no_close, sys_read, sys_write, sys_ioctl}, +}; + +bool validdev(uint16_t dev) +{ + /* This is a bit uglier than needed but the right hand side is + a constant this way */ + if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255) + return false; + else + return true; +} diff --git a/Kernel/platform-sc114/devtty.c b/Kernel/platform-sc114/devtty.c new file mode 100644 index 00000000..759ed278 --- /dev/null +++ b/Kernel/platform-sc114/devtty.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * We drive the tty interface via the SCM monitor layer + * + * Right now that costs us RTS/CTS, char size and parity handling + */ + +static char tbuf1[TTYSIZ]; +static char tbuf2[TTYSIZ]; + +uint8_t numtty = 1; + +static tcflag_t uart0_mask[4] = { + _ISYS, + _OSYS, + CBAUD|_CSYS, + _LSYS +}; + +static tcflag_t uart1_mask[4] = { + _ISYS, + _OSYS, + CBAUD|CSYS, + _LSYS, +}; + +tcflag_t *termios_mask[NUM_DEV_TTY + 1] = { + NULL, + uart0_mask, + uart1_mask +}; + +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}, +}; + +static uint8_t baudmap[] = { + 0, 0, 0, 0, 0, 0, /* No map for < 300 baud */ + 0x0C, /* 300 */ + 0x0B, /* 600 */ + 0x0A, /* 1200 */ + 0x09, /* 2400 */ + 0x08, /* 4800 */ + 0x07, /* 9600 */ + 0x05, /* 19200 */ + 0x04, /* 38400 */ + 0x03, /* 57600 */ + 0x02, /* 115200 */ +}; + +/* FIXME: TODO set the initial value from the monitor - except it won't tell us */ +void tty_setup(uint8_t minor, uint8_t flags) +{ + tcflag_t *tptr = &ttydata[minor].termios.c_cflag; + uint8_t baud = *tptr & CBAUD; + if (baud < B300) { + baud = B300; + *tptr &= ~CBAUD; + *tptr |= B300; + } + scm_setbaud(((uint16_t)minor) << 8) |baudmap[baud]); +} + +int tty_carrier(uint8_t minor) +{ + return 1; +} + +/* We always DI before a ROM call so this is safe */ +void tty_pollirq(void) +{ + int16_t c = scm_input(1); + if (c >= 0) + tty_inproc(1, c); + if (numtty > 1) { + c = scm_input(2); + if (c >= 0) + tty_inproc(2, c); + } +} + +/* The SCM monitor doesn't have a check for ready for port n, we have to + fake stuff up a bit using the supplied 'try and send but might fail' */ + +static uint16_t ttyq[NUM_DEV_TTY + 1]; + +void tty_putc(uint8_t minor, unsigned char c) +{ + uint16_t val = ((uint16_t)minor << 8) | c; + if (scm_output(val)) + ttyq[minor] = val; /* Minor means it won't be 0 */ +} + +ttyready_t tty_writeready(uint8_t minor) +{ + /* If nothing is queued we are good */ + if (!ttyq[minor]) + return TTY_READY_NOW; + /* If we can't write the queued character we are busy */ + if (scm_output(ttyq[minor])) + return TTY_READY_SOON; + /* Wipe the queue and report good */ + ttyq[minor] = 0; + return TTY_READY_NOW; +} + +void tty_sleeping(uint8_t minor) +{ +} + +void tty_data_consumed(uint8_t minor) +{ +} + +/* kernel writes to system console -- never sleep! */ +void kputchar(char c) +{ + tty_putc(TTYDEV - 512, c); + if (c == '\n') + tty_putc(TTYDEV - 512, '\r'); +} diff --git a/Kernel/platform-sc114/devtty.h b/Kernel/platform-sc114/devtty.h new file mode 100644 index 00000000..669c287f --- /dev/null +++ b/Kernel/platform-sc114/devtty.h @@ -0,0 +1,9 @@ +#ifndef __DEVTTY_DOT_H__ +#define __DEVTTY_DOT_H__ + +void tty_putc(uint8_t minor, unsigned char c); +void tty_pollirq_sio(void); + +extern uint8_t numtty; + +#endif diff --git a/Kernel/platform-sc114/discard.c b/Kernel/platform-sc114/discard.c new file mode 100644 index 00000000..209544ae --- /dev/null +++ b/Kernel/platform-sc114/discard.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include +#include +#include "config.h" + +void map_init(void) +{ +} + +/* + * This function is called for partitioned devices if a partition is found + * and marked as swap type. The first one found will be used as swap. We + * only support one swap device. + */ +void platform_swap_found(uint8_t letter, uint8_t m) +{ + blkdev_t *blk = blk_op.blkdev; + uint16_t n; + if (swap_dev != 0xFFFF) + return; + letter -= 'a'; + kputs("(swap) "); + swap_dev = letter << 4 | m; + n = blk->lba_count[m - 1] / SWAP_SIZE; + if (n > MAX_SWAPS) + n = MAX_SWAPS; + while(n) + swapmap_init(n--); +} + +void system_init(void) +{ + scm_version(); + switch(scm_hw_info >> 8) { + case SCM_SC114: + kputs("Small Computer 114.\n"); + /* RAM switch port 30 bit 0, ROM port 38 bit 0 */ + /* Second ACIA or an SIO/2 means two serial */ + if (scm_hw_info & 6) + numtty = 2; + break; + case SCM_SC108: + kputs("Small Computer 108.\n"); + /* RAM switch port 38 bit 7, ROM port 38 bit 0 */ + /* Want to merge in this port */ + /* And the SCM SC101 uses outs to port 18 to select ram bank 0 , + 19 1, and 1c/1d for ROM in and out. However there's only one of it + so probably not worth the trouble ! */ + default: + /* Unsupported */ + panic("Unsupported platform.\n"); + } +} + +void device_init(void) +{ + system_init(); + ds1302_init(); +#ifdef CONFIG_IDE + devide_init(); +#endif +} diff --git a/Kernel/platform-sc114/fuzix.lnk b/Kernel/platform-sc114/fuzix.lnk new file mode 100644 index 00000000..a168196a --- /dev/null +++ b/Kernel/platform-sc114/fuzix.lnk @@ -0,0 +1,44 @@ +-mwxuy +-i fuzix.ihx +-b _CODE=0x0100 +-b _COMMONMEM=0xF000 +-l z80 +platform-sc114/crt0.rel +platform-sc114/commonmem.rel +platform-sc114/sc114.rel +start.rel +version.rel +lowlevel-z80-thunked.rel +platform-sc114/tricks.rel +platform-sc114/main.rel +timer.rel +kdata.rel +platform-sc114/devices.rel +devio.rel +filesys.rel +process.rel +inode.rel +syscall_exec16.rel +syscall_fs.rel +syscall_proc.rel +syscall_fs2.rel +syscall_fs3.rel +syscall_other.rel +mm.rel +swap.rel +simple.rel +tty.rel +devsys.rel +usermem.rel +platform-sc114/discard.rel +platform-sc114/devtty.rel +platform-sc114/mbr.rel +platform-sc114/blkdev.rel +platform-sc114/devide.rel +platform-sc114/devide_discard.rel +platform-sc114/ide.rel +platform-sc114/usermem.rel +platform-sc114/ds1302.rel +platform-sc114/ds1302_discard.rel +platform-sc114/ds1302_rc2014.rel +-e diff --git a/Kernel/platform-sc114/ide.c b/Kernel/platform-sc114/ide.c new file mode 100644 index 00000000..e76d678c --- /dev/null +++ b/Kernel/platform-sc114/ide.c @@ -0,0 +1,71 @@ +#define _IDE_PRIVATE + +#include +#include +#include +#include +#include +#include +#include + +/* + * We need slightly custom transfer routines for the IDE controller + * as we have no common stack to use. + */ + +COMMON_MEMORY + +void devide_read_data(void) __naked +{ + __asm + ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user + ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr + ld bc, #IDE_REG_DATA ; setup port number + ; and count + cp #2 + jr nz, not_swapin + ld a, (_blk_op+BLKPARAM_SWAP_PAGE) ; blkparam.swap_page +not_swapin: + ; At this point A = 0 kernel, A = 1, user + rrca + or #1 + out (0x38),a + rlca + out (0x30),a + inir ; transfer first 256 bytes + inir ; transfer second 256 bytes + ld a,#0x01 + out (0x38),a + rlca + out (0x30),a + ret + __endasm; +} + +void devide_write_data(void) __naked +{ + __asm + ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user + ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr + ld bc, #IDE_REG_DATA ; setup port number + ; and count + cp #2 + jr nz, not_swapout + ld a, (_blk_op+BLKPARAM_SWAP_PAGE) ; blkparam.swap_page +not_swapout: + ; At this point A = 0 kernel, A = 1 user + rrca + or #1 + out (0x38),a + rlca + out (0x30),a + otir ; transfer first 256 bytes + otir ; transfer second 256 bytes + xor a + ld a,#0x01 + out (0x38),a + rlca + out (0x30),a + ret + __endasm; +} diff --git a/Kernel/platform-sc114/kernel.def b/Kernel/platform-sc114/kernel.def new file mode 100644 index 00000000..37a5db61 --- /dev/null +++ b/Kernel/platform-sc114/kernel.def @@ -0,0 +1,49 @@ +; FUZIX mnemonics for memory addresses etc + +U_DATA .equ 0xF000 ; (this is struct u_data from kernel.h) +U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes. +Z80_TYPE .equ 0 ; CMOS + +Z80_MMU_HOOKS .equ 0 + +CONFIG_SWAP .equ 1 + +PROGBASE .equ 0x0000 +PROGLOAD .equ 0x0100 + +; Mnemonics for I/O ports etc + +CONSOLE_RATE .equ 115200 + +CPU_CLOCK_KHZ .equ 7372 + +; 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 +; Z80 CTC ports +CTC_CH0 .equ 0x88 ; CTC channel 0 and interrupt vector +CTC_CH1 .equ 0x89 ; CTC channel 1 (periodic interrupts) +CTC_CH2 .equ 0x8A ; CTC channel 2 +CTC_CH3 .equ 0x8B ; CTC channel 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 + +NBUFS .equ 4 + +; +; Select where to put the high code - in our case we need this +; in common +; +.macro HIGH + .area _COMMONMEM +.endm + +HIGHPAGE .equ 0 ; We only have 1 page byte and the low page + ; isn't used diff --git a/Kernel/platform-sc114/main.c b/Kernel/platform-sc114/main.c new file mode 100644 index 00000000..7faac946 --- /dev/null +++ b/Kernel/platform-sc114/main.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#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 = 0xF000; +uint8_t need_resched = 0; + + +void platform_discard(void) +{ + while (bufpool_end < (struct blkbuf *) (KERNTOP - sizeof(struct blkbuf))) { + memset(bufpool_end, 0, sizeof(struct blkbuf)); +#if BF_FREE != 0 + bufpool_end->bf_busy = BF_FREE; /* redundant when BF_FREE == 0 */ +#endif + bufpool_end->bf_dev = NO_DEVICE; + bufpool_end++; + } + kprintf("Buffers available: %d\n", bufpool_end - bufpool); +} + +static uint8_t idlect; + +void platform_idle(void) +{ + /* 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 to see what 1/10th sec looks like in poll loops */ + if (!++idlect) + sync_clock(); +} + +uint8_t platform_param(unsigned char *p) +{ + used(p); + return 0; +} + +void platform_interrupt(void) +{ + tty_pollirq(); +} + +/* + * Logic for tickless system. If you have an RTC you can ignore this. + */ + +static uint8_t newticks = 0xFF; +static uint8_t oldticks; + +static uint8_t re_enter; + +/* + * Hardware specific logic to get the seconds. We really ought to enhance + * this to check minutes as well just in case something gets stuck for + * ages. + */ +static void sync_clock_read(void) +{ + uint8_t s; + oldticks = newticks; + ds1302_read_clock(&s, 1); + s = (s & 0x0F) + (((s & 0xF0) >> 4) * 10); + newticks = s; +} + +/* + * The OS core will invoke this routine when idle (via platform_idle) but + * also after a system call and in certain other spots to ensure the clock + * is roughly valid. It may be called from interrupts, without interrupts + * or even recursively so it must protect itself using the framework + * below. + * + * Having worked out how much time has passed in 1/10ths of a second it + * performs that may timer_interrupt events in order to advance the clock. + * The core kernel logic ensures that we won't do anything silly from a + * jump forward of many seconds. + * + * We also choose to poll the ttys here so the user has some chance of + * getting control back on a messed up process. + */ +void sync_clock(void) +{ + irqflags_t irq = di(); + int16_t tmp; + if (!re_enter++) { + sync_clock_read(); + if (oldticks != 0xFF) { + tmp = newticks - oldticks; + if (tmp < 0) + tmp += 60; + tmp *= 10; + while(tmp--) { + timer_interrupt(); + } + } + } + re_enter--; + if (re_enter > 1) + kputs("oops"); + irqrestore(irq); +} + +/* + * This method is called if the kernel has changed the system clock. We + * don't work out how much work we need to do by using it as a reference + * so we don't care. + */ +void update_sync_clock(void) +{ +} diff --git a/Kernel/platform-sc114/platform_ide.h b/Kernel/platform-sc114/platform_ide.h new file mode 100644 index 00000000..d2313d31 --- /dev/null +++ b/Kernel/platform-sc114/platform_ide.h @@ -0,0 +1,9 @@ +#define ide_select(x) +#define ide_deselect() + +/* 8bit, no altstatus/control */ +#define IDE_8BIT_ONLY +#define IDE_REG_CS1_BASE 0x10 + +/* Due to our strange banking needs */ +#define IDE_NONSTANDARD_XFER diff --git a/Kernel/platform-sc114/rc2014.h b/Kernel/platform-sc114/rc2014.h new file mode 100644 index 00000000..21ee491c --- /dev/null +++ b/Kernel/platform-sc114/rc2014.h @@ -0,0 +1,25 @@ +#ifndef __RC2014_SIO_DOT_H__ +#define __RC2014_SIO_DOT_H__ + +/* Needs generalizing and tidying up across the RC2014 systems */ + +#include "config.h" + +#define SIO0_IVT 8 + +/* 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; + +/* ACIA is at same address as SIO but we autodetect */ + +#define ACIA_BASE 0x80 +__sfr __at (ACIA_BASE + 0) ACIA_C; +__sfr __at (ACIA_BASE + 1) ACIA_D; + +extern void sio2_otir(uint8_t port) __z88dk_fastcall; + +#endif diff --git a/Kernel/platform-sc114/rules.mk b/Kernel/platform-sc114/rules.mk new file mode 100644 index 00000000..c3701b05 --- /dev/null +++ b/Kernel/platform-sc114/rules.mk @@ -0,0 +1 @@ +export BANKED=-thunked diff --git a/Kernel/platform-sc114/sc114.s b/Kernel/platform-sc114/sc114.s new file mode 100644 index 00000000..e06981a3 --- /dev/null +++ b/Kernel/platform-sc114/sc114.s @@ -0,0 +1,499 @@ +; +; SC114 Initial Support +; + + .module sc114 + + ; exported symbols + .globl init_hardware + .globl interrupt_handler + .globl _program_vectors + .globl _kernel_flag + .globl map_page_low + .globl map_kernel_low + .globl map_user_low + .globl map_save_low + .globl map_restore_low + .globl _platform_doexec + .globl _platform_reboot + .globl _int_disabled + .globl _scm_romcall + + ; exported debugging tools + .globl _platform_monitor + .globl outchar + + ; imported symbols + .globl _ramsize + .globl _procmem + .globl istack_top + .globl istack_switched_sp + .globl kstack_top + .globl unix_syscall_entry + .globl outcharhex + + .globl _scm_reset + .globl _scm_conout + .globl scm_farput + + .globl s__COMMONMEM + .globl l__COMMONMEM + + .include "kernel.def" + .include "../kernel.def" + + +; +; Buffers (we use asm to set this up as we need them in a special segment +; so we can recover the discard memory into the buffer pool +; + + .globl _bufpool + .area _BUFFERS + +_bufpool: + .ds BUFSIZE * NBUFS + +; ----------------------------------------------------------------------------- +; +; Because of the weird memory model this is a bit different to the +; usual Z80 setup. +; +; ----------------------------------------------------------------------------- + .area _COMMONMEM + +_int_disabled: + .db 1 + +; +; Ugly but the API happens to use A B and C so all the nice ways to +; ROM page are not available. On the SC108 this will stick us back +; in the kernel mapping but that's fine - we were there anyway +; +; Interrupts must be off for a ROM call +; +_scm_romcall: + di + ex af,af' + xor a + out (0x38),a ; ROM in + ex af,af' + rst 0x30 + ex af,af' + inc a + out (0x38),a + ex af,af' + ld a,(_int_disabled) + or a + ret nz + ei + ret +_platform_monitor: + ; Reboot ends up back in the monitor +_platform_reboot: + jp _scm_reset + + +; ----------------------------------------------------------------------------- +; +; Our MMU is write only, but if we put a value in each bank in a fixed +; place we have a fast way to see which bank is which +; +; ----------------------------------------------------------------------------- + +banknum: + .byte 0x81 ; copied into far bank then set to 1 + +; ----------------------------------------------------------------------------- +; All of discard gets reclaimed when init is run +; +; Discard must be above 0x8000 as we need some of it when the ROM +; is paged in during init_hardware +; ----------------------------------------------------------------------------- + .area _DISCARD + +init_hardware: + ; A holds the platform + cp #0x08 ; SC114 + jr z, is_sc114 + ; Nope - must be an SC108 + ; Reprogram the mapping logic + ld de,#0x0181 + ld (_bank0to1),de + ld de,#0x8101 + ld (_bank1to0),de + ld a,#0x38 + ld (_bankport),a +is_sc114: + ld hl, #128 + ld (_ramsize), hl + ld hl,#64 + ld (_procmem), hl + + ld de,#s__COMMONMEM + ld bc,#l__COMMONMEM + + ; + ; Use the ROM helper to get our common into the secondary + ; memory bank. + ; +install_common: + ld a,(de) + push bc + push de + call scm_farput + pop de + pop bc + inc de + dec bc + ld a,b + or c + jr nz, install_common + ; A = 0 + ; SC108 is already back in the right RAM bank, make sure SC114 is + out (0x30),a + ld a,#0x01 + ld (banknum),a ; and correct page + + ; We now have our common in place. We can do the rest ourselves + + ; Put the low stubs into place in the kernel + ld hl,#stubs_low + ld de,#0 + ld bc,#0x68 + ldir + ld hl,#stubs_low + ld ix,#0 + ld bc,#0x68 + call ldir_to_user + + im 1 ; set CPU interrupt mode + ret + +; +; Our memory setup is weird and common is kind of meaningless here +; + .area _CODE + +_kernel_flag: + .db 1 ; We start in kernel mode +map_save_low: +map_kernel_low: +map_restore_low: +map_user_low: +map_page_low: + ret + +_program_vectors: + ret + +; +; outchar: Wait for UART TX idle, then print the char in A +; destroys: AF +; +outchar: + push bc + push de + push hl + call _scm_conout + pop hl + pop de + pop bc + ret + + +; Don't be tempted to put the symbol in the code below ..it's relocated +; to zero. Instead define where it ends up. + +_platform_doexec .equ 0x18 + + .area _DISCARD + + .globl rst38 + .globl stubs_low +; This exists at the bottom of each page. We move these into place +; from discard. +; +stubs_low: + .byte 0 +stub0: .word 0 ; cp/m emu changes this + .byte 0 ; cp/m emu I/O byte + .byte 0 ; cp/m emu drive and user + jp 0 ; cp/m emu bdos entry point +rst8: + ret + nop + nop + nop + nop + nop + nop + nop +rst10: + ret + nop + nop + nop + nop + nop + nop + nop +rst18: ; execve entry to user space + ld a,#0x01 + out (0x30),a + ei + jp (hl) + nop + nop +rst20: ret + nop + nop + nop + nop + nop + nop + nop +rst28: ret + nop + nop + nop + nop + nop + nop + nop +rst30: jp syscall_high + nop + nop + nop + nop + nop +; +; We only have 38-4F available for this in low space +; +rst38: jp interrupt_high ; Interrupt handling stub + nop + nop + nop + nop + nop + .ds 0x26 +nmi_handler: ; Should be at 0x66 + retn + +; +; This stuff needs to live somewhere, anywhere out of the way (so we +; use common). We need to copy it to the same address on both banks +; so place it in common as we will put common into both banks +; + + .area _COMMONMEM + + .globl ldir_to_user + .globl ldir_from_user + +; FIXME: save a byte and some setup by makign byte 2 of 0to1 the first +; of 1to0 +_bank0to1: + .dw 0x0001 ; 0x0181 for SC108 +_bank1to0: + .dw 0x0100 ; 0x8101 for SC108 +_bankport: + .db 0x30 ; 0x38 for SC108 +; +; This needs some properly optimized versions! +; +ldir_to_user: + ld de,(_bank0to1) ; from bank 0 to bank 1 +ldir_far: + push bc + ld bc,(_bankport) + exx + pop bc ; get BC into alt bank +far_ldir_1: + exx + out (c),d + ld a,(hl) + inc hl + out (c),e + ld (ix),a + inc ix + exx + dec bc + ld a,b + or c + jr nz, far_ldir_1 + ld a,(_bank0to1) ; gets us the bank 0 value */ + exx + out (c),a + exx + ret +ldir_from_user: + ld de,#0x0100 + jr ldir_far +; +; High stubs. Present in each bank in the top 256 bytes +; +interrupt_high: + push af + push de + push hl + ex af,af' + push af + push bc + exx + push bc + push de + push hl + push ix + push iy + ld a,(banknum) + ld c,a + ; + ; The trick going on here is that an SC108 will respond to + ; 0x38 bit 7 = RAM bank 0 = ROM and ignore 0x30 + ; but the SC114 will respond to 0x38 bit 0 only (ROM) and + ; to 0x30 bit 0 for RAM. + ; + ld a,#0x01 + out (0x38),a ; Bank 0 + rlca + out (0x30),a + ld (istack_switched_sp),sp ; istack is positioned to be valid + ld sp,#istack_top ; in both banks. We just have to + ; + ; interrupt_handler may come back on a different stack in + ; which case bc is junk. Fortuntely we never pre-empt in + ; kernel so the case we care about bc is always safe. This is + ; not a good way to write code and should be fixed! FIXME + ; + push bc + call interrupt_handler ; switch on the right SP + pop bc + ; Restore stack pointer to user. This leaves us with an invalid + ; stack pointer if called from user but interrupts are off anyway + ld sp,(istack_switched_sp) + ; On return HL = signal vector E= signal (if any) A = page for + ; high + or a + jr z, kernout + ; Returning to user space + ld a,#0x81 ; User bank + out (0x38),a + rlca + out (0x30),a + ; User stack is now valid + ; back on user stack + xor a + cp e + call nz, sigpath +pops: + ex af,af' + exx + pop iy + pop ix + pop hl + pop de + pop bc + exx + pop bc + pop af + ex af,af' + pop hl + pop de + pop af + ei + ret +kernout: + ; restore bank - if we interrupt mid user copy or similar we + ; have to put the right bank back + ld a,c + out (0x38),a + rlca + out (0x30),a + jr pops + +sigpath: + push de ; signal number + ld de,#irqsigret + push de ; clean up + jp (hl) +irqsigret: + inc sp ; drop signal number + inc sp + ret + +syscall_high: + push ix + ld ix,#0 + add ix,sp + push de ; the syscall if must preserve de for now + ; needs fixing when we change the syscall + ; API for Z80 to something less sucky + ld a,4(ix) + ld c,6(ix) + ld b,7(ix) + ld e,8(ix) + ld d,9(ix) + ld l,10(ix) + ld h,11(ix) + push hl + ld l,12(ix) + ld h,13(ix) + pop ix + di + ; BUG: syscall corrupts AF' - should we just define some + ; alt register corruptors for new API - would be sanest fix + ex af, af' ; Ick - find a better way to do this bit ! + ld a,#0x01 + out (0x38),a + rlca + out (0x30),a + ex af,af' + ; Stack now invalid + ld (U_DATA__U_SYSCALL_SP),sp + ld sp,#kstack_top + call unix_syscall_entry + ; FIXME check di rules + ; stack now invalid. Grab the new sp before we unbank the + ; memory holding it + ld sp,(U_DATA__U_SYSCALL_SP) + ld a,#0x81 ; back to the user page + out (0x38),a + rlca + out (0x30),a + xor a + cp h + call nz, syscall_sigret + ; FIXME for now do the grungy C flag HL DE stuff from + ; lowlevel-z80 until we fix the ABI + ld bc,#0 + ld a,h + or l + jr nz, error + ex de,hl + pop de + pop ix + ei + ret +error: scf + pop de + pop ix + ei + ret +syscall_sigret: + ld a,l ; DEBUG + push hl ; save errno + push de ; save retval + ld l,h + ld h,#0 + push hl ; signal + ld hl,#syscall_sighelp + push hl ; vector + ret +syscall_sighelp: + pop de ; discard signal + pop de ; recover error info + pop hl + ld h,#0 ; clear signal bit + ret + diff --git a/Kernel/platform-sc114/target.mk b/Kernel/platform-sc114/target.mk new file mode 100644 index 00000000..3bffcde0 --- /dev/null +++ b/Kernel/platform-sc114/target.mk @@ -0,0 +1 @@ +export CPU = z80 diff --git a/Kernel/platform-sc114/tricks.s b/Kernel/platform-sc114/tricks.s new file mode 100644 index 00000000..0113d75b --- /dev/null +++ b/Kernel/platform-sc114/tricks.s @@ -0,0 +1,5 @@ + .include "kernel.def" + .include "../kernel.def" + + .include "../lib/z80single.s" + diff --git a/Kernel/platform-sc114/usermem.s b/Kernel/platform-sc114/usermem.s new file mode 100644 index 00000000..81c92eeb --- /dev/null +++ b/Kernel/platform-sc114/usermem.s @@ -0,0 +1,156 @@ +; +; We have a custom implementation of usermem. We really need +; to optimize ldir_to/from_user. +; + .module usermem + + .include "kernel.def" + .include "../kernel.def" + + ; exported symbols + .globl __uget + .globl __ugetc + .globl __ugetw + + .globl __uput + .globl __uputc + .globl __uputw + .globl __uzero + + .globl ldir_from_user + .globl ldir_to_user + + .area _COMMONMEM + + +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 + ld e, 6(ix) + ld d, 7(ix) + ret + +__uget: + push ix + ld ix,#0 + add ix, sp + call uputget + jr z, uget_out + push de + pop ix + call ldir_from_user +uget_out: + pop ix + ld hl,#0 + ret + +__uput: + push ix + ld ix,#0 + add ix,sp + call uputget + jr z, uget_out + push de + pop ix + call ldir_to_user + jr uget_out + +; +; The kernel IRQ code will restore the bank if it interrupts this +; logic +; +__uzero: + pop de + pop hl + pop bc + push bc + push hl + push de + ld a,b + or c + ret z + ld a,#0x81 + out (0x38),a ; user bank + rlca + out (0x30),a + ld (hl),#0 + dec bc + ld a,b + or c + jr z, uout + ld e,l + ld d,h + inc de + ldir +uout: + ld a,#1 + out (0x38),a + rlca + out (0x30),a + ret + +__ugetc: + pop bc + pop hl + push hl + push bc + ld a,#0x81 + out (0x38),a + rlca + out (0x30),a + ld l,(hl) + ld h,#0 + jr uout + +__ugetw: + pop bc + pop hl + push hl + push bc + ld a,#0x81 + out (0x38),a + rlca + out (0x30),a + ld a,(hl) + inc hl + ld h,(hl) + ld l,a + jr uout + +__uputc: + pop bc + pop de + pop hl + push hl + push de + push bc + ld a,#0x81 + out (0x38),a + rlca + out (0x30),a + ld (hl),e + jr uout + +__uputw: + pop bc + pop de + pop hl + push hl + push de + push bc + ld a,#0x81 + out (0x38),a + rlca + out (0x30),a + ld (hl),e + inc hl + ld (hl),d + jr uout