From 9e594533e2c40f26c19f6d1686a805fa0e8754e6 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 16 Sep 2018 00:46:20 +0100 Subject: [PATCH] sc108: Testing the thunking code on a machine with no common RAM Instead the banks are pure RAM with a 64K switch but there is a ROM helper to bootstrap stuff from bank 0 to bank 1. --- Kernel/platform-sc108/Makefile | 51 +++ Kernel/platform-sc108/README | 96 ++++++ Kernel/platform-sc108/commonmem.s | 10 + Kernel/platform-sc108/config.h | 70 ++++ Kernel/platform-sc108/crt0.s | 80 +++++ Kernel/platform-sc108/devices.c | 39 +++ Kernel/platform-sc108/devtty.c | 143 ++++++++ Kernel/platform-sc108/devtty.h | 10 + Kernel/platform-sc108/discard.c | 41 +++ Kernel/platform-sc108/fuzix.lnk | 44 +++ Kernel/platform-sc108/ide.c | 62 ++++ Kernel/platform-sc108/kernel.def | 46 +++ Kernel/platform-sc108/main.c | 124 +++++++ Kernel/platform-sc108/platform_ide.h | 9 + Kernel/platform-sc108/rc2014.h | 23 ++ Kernel/platform-sc108/rules.mk | 1 + Kernel/platform-sc108/sc108.s | 494 +++++++++++++++++++++++++++ Kernel/platform-sc108/target.mk | 1 + Kernel/platform-sc108/tricks.s | 5 + Kernel/platform-sc108/usermem.s | 144 ++++++++ 20 files changed, 1493 insertions(+) create mode 100644 Kernel/platform-sc108/Makefile create mode 100644 Kernel/platform-sc108/README create mode 100644 Kernel/platform-sc108/commonmem.s create mode 100644 Kernel/platform-sc108/config.h create mode 100644 Kernel/platform-sc108/crt0.s create mode 100644 Kernel/platform-sc108/devices.c create mode 100644 Kernel/platform-sc108/devtty.c create mode 100644 Kernel/platform-sc108/devtty.h create mode 100644 Kernel/platform-sc108/discard.c create mode 100644 Kernel/platform-sc108/fuzix.lnk create mode 100644 Kernel/platform-sc108/ide.c create mode 100644 Kernel/platform-sc108/kernel.def create mode 100644 Kernel/platform-sc108/main.c create mode 100644 Kernel/platform-sc108/platform_ide.h create mode 100644 Kernel/platform-sc108/rc2014.h create mode 100644 Kernel/platform-sc108/rules.mk create mode 100644 Kernel/platform-sc108/sc108.s create mode 100644 Kernel/platform-sc108/target.mk create mode 100644 Kernel/platform-sc108/tricks.s create mode 100644 Kernel/platform-sc108/usermem.s diff --git a/Kernel/platform-sc108/Makefile b/Kernel/platform-sc108/Makefile new file mode 100644 index 00000000..acb9775a --- /dev/null +++ b/Kernel/platform-sc108/Makefile @@ -0,0 +1,51 @@ +ASRCS = crt0.s tricks.s commonmem.s sc108.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: + diff --git a/Kernel/platform-sc108/README b/Kernel/platform-sc108/README new file mode 100644 index 00000000..1cd6250e --- /dev/null +++ b/Kernel/platform-sc108/README @@ -0,0 +1,96 @@ +Experimental port for the proposed SC108 mainboard plus CF card plus RTC card + +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 ROM has to have the following routines + +0x7FFD JP ldir_far + +To provide the following functionality + +copy_far +HL = source +IX = destination +BC = count +A = return bank bits +D = dest bank bits +E = source bank bits + +where source/dest must be in the upper 32K (clear of ROM) and interrupts off + +Something like this patched into the ROM should work + +; +; ROM patch for SC108 +; + .area CODE(ABS) + +.org 0x7FE2 + +; HL = SOURCE +; IX = DESTINATION +; BC = COUNT +; D = destination bank +; E = source bank +; +; +; +ldir_far: + push bc + ld c,#0x38 + exx + pop bc ; get BC into alt bank +far_ldir_1: + exx + out (c),e + ld a,(hl) + inc hl + out (c),d + ld (ix),a + inc ix + exx + dec bc + ld a,b + or c + jr nz, far_ldir_1 + xor a + out (0x38),a + ret + + jp ldir_far + + + +Memory map + +Kernel + +0000-00FF Vectors (present in both banks) +0100-EFFF Kernel (common above 0x8000 for ROM dodging) +F000-F1FF UDATA +F200-FFFF Common (mostly valid in both banks) + +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-FFFF Common (mostly valid in both banks) + + +TO DO + +Write a proper bootloader that can use partition tables properly +Fix hardcoded use of ACIA +Use the extra space to put back some kernel bits we binned from -tiny +Build a suitably high CP/M emuation and test it + + +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) diff --git a/Kernel/platform-sc108/commonmem.s b/Kernel/platform-sc108/commonmem.s new file mode 100644 index 00000000..355bd53a --- /dev/null +++ b/Kernel/platform-sc108/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-sc108/config.h b/Kernel/platform-sc108/config.h new file mode 100644 index 00000000..a6776483 --- /dev/null +++ b/Kernel/platform-sc108/config.h @@ -0,0 +1,70 @@ +/* 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 +/* 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 */ diff --git a/Kernel/platform-sc108/crt0.s b/Kernel/platform-sc108/crt0.s new file mode 100644 index 00000000..398acba8 --- /dev/null +++ b/Kernel/platform-sc108/crt0.s @@ -0,0 +1,80 @@ +; 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 + +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-sc108/devices.c b/Kernel/platform-sc108/devices.c new file mode 100644 index 00000000..1a52d3e6 --- /dev/null +++ b/Kernel/platform-sc108/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-sc108/devtty.c b/Kernel/platform-sc108/devtty.c new file mode 100644 index 00000000..d3353c74 --- /dev/null +++ b/Kernel/platform-sc108/devtty.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include +#include + +char tbuf1[TTYSIZ]; +char tbuf2[TTYSIZ]; + +uint8_t ser_type = 2; + +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}, +}; + +void tty_setup(uint8_t minor) +{ + if (minor == 1) { + } +} + +int tty_carrier(uint8_t minor) +{ +// uint8_t c; + if (minor == 1) { +// c = UART0_MSR; +// return (c & 0x80) ? 1 : 0; /* test DCD */ + } + return 1; +} + +void tty_pollirq_sio(void) +{ + uint8_t ca, cb; + + /* FIXME: need to process error/event interrupts as we can get + spurious characters or lines on an unused SIO floating */ + do { + SIOA_C = 0; // read register 0 + ca = SIOA_C; + if (ca & 1) + tty_inproc(1, SIOA_D); + if (ca & 4) { + tty_outproc(1); + SIOA_C = 5 << 3; // reg 0 CMD 5 - reset transmit interrupt pending + } + SIOB_C = 0; // read register 0 + cb = SIOB_C; + if (cb & 1) + tty_inproc(2, SIOB_D); + if (cb & 4) { + tty_outproc(2); + SIOB_C = 5 << 3; // reg 0 CMD 5 - reset transmit interrupt pending + } + } while((ca | cb) & 1); +} + +void tty_pollirq_acia(void) +{ + uint8_t ca; + + ca = ACIA_C; + if (ca & 1) { + ca = ACIA_D; + tty_inproc(1, ca); + } + if (ca & 2) { +// We don't use IRQ driven tty_outproc(1); + } +} + +static char hex[] = { "0123456789ABCDEF" }; + +void tty_putc(uint8_t minor, unsigned char c) +{ + if (ser_type == 1) { + if (minor == 1) { + SIOA_D = c; + } else if (minor == 2) + SIOB_D = c; + } else if (minor == 1) + ACIA_D = c; + else if (minor = 3) { + /* FIXME: implement */ + } +} + +/* We will need this for SIO once we implement flow control signals */ +void tty_sleeping(uint8_t minor) +{ +} + +/* Be careful here. We need to peek at RR but we must be sure nobody else + interrupts as we do this. Really we want to switch to irq driven tx ints + on this platform I think. Need to time it and see + + An asm common level tty driver might be a better idea */ +ttyready_t tty_writeready(uint8_t minor) +{ + 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) { + c = ACIA_C; + if (c & 0x02) /* THRE? */ + return TTY_READY_NOW; + return TTY_READY_SOON; + } + return TTY_READY_NOW; +} + +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-sc108/devtty.h b/Kernel/platform-sc108/devtty.h new file mode 100644 index 00000000..518450e6 --- /dev/null +++ b/Kernel/platform-sc108/devtty.h @@ -0,0 +1,10 @@ +#ifndef __DEVTTY_DOT_H__ +#define __DEVTTY_DOT_H__ + +void tty_putc(uint8_t minor, unsigned char c); +void tty_pollirq_sio(void); +void tty_pollirq_acia(void); + +extern uint8_t ser_type; + +#endif diff --git a/Kernel/platform-sc108/discard.c b/Kernel/platform-sc108/discard.c new file mode 100644 index 00000000..5766275b --- /dev/null +++ b/Kernel/platform-sc108/discard.c @@ -0,0 +1,41 @@ +#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_add(n--); +} + +void device_init(void) +{ + ds1302_init(); +#ifdef CONFIG_IDE + devide_init(); +#endif +} diff --git a/Kernel/platform-sc108/fuzix.lnk b/Kernel/platform-sc108/fuzix.lnk new file mode 100644 index 00000000..7969881a --- /dev/null +++ b/Kernel/platform-sc108/fuzix.lnk @@ -0,0 +1,44 @@ +-mwxuy +-i fuzix.ihx +-b _CODE=0x0100 +-b _COMMONMEM=0xF000 +-l z80 +platform-sc108/crt0.rel +platform-sc108/commonmem.rel +platform-sc108/sc108.rel +start.rel +version.rel +lowlevel-z80-thunked.rel +platform-sc108/tricks.rel +platform-sc108/main.rel +timer.rel +kdata.rel +platform-sc108/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-sc108/discard.rel +platform-sc108/devtty.rel +platform-sc108/mbr.rel +platform-sc108/blkdev.rel +platform-sc108/devide.rel +platform-sc108/devide_discard.rel +platform-sc108/ide.rel +platform-sc108/usermem.rel +platform-sc108/ds1302.rel +platform-sc108/ds1302_discard.rel +platform-sc108/ds1302_rc2014.rel +-e diff --git a/Kernel/platform-sc108/ide.c b/Kernel/platform-sc108/ide.c new file mode 100644 index 00000000..7c13a293 --- /dev/null +++ b/Kernel/platform-sc108/ide.c @@ -0,0 +1,62 @@ +#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, kernel + rrca + or #1 ; 0x81 or 0x01 + out (0x38),a + inir ; transfer first 256 bytes + inir ; transfer second 256 bytes + ld a,#0x01 + out (0x38),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, kernel + rrca + or #1 ; 0x81 or 0x01 + out (0x38),a + otir ; transfer first 256 bytes + otir ; transfer second 256 bytes + ld a,#0x01 + out (0x38),a + ret + __endasm; +} diff --git a/Kernel/platform-sc108/kernel.def b/Kernel/platform-sc108/kernel.def new file mode 100644 index 00000000..4ce84ccb --- /dev/null +++ b/Kernel/platform-sc108/kernel.def @@ -0,0 +1,46 @@ +; 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 1 ; 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 diff --git a/Kernel/platform-sc108/main.c b/Kernel/platform-sc108/main.c new file mode 100644 index 00000000..a27ea784 --- /dev/null +++ b/Kernel/platform-sc108/main.c @@ -0,0 +1,124 @@ +#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 */ +uint8_t timermsr = 0; +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 t 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) +{ + if (ser_type == 1) + tty_pollirq_sio(); + else + tty_pollirq_acia(); + return; +} + +/* + * 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) +{ + if (!timermsr) { + 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--; + } + 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-sc108/platform_ide.h b/Kernel/platform-sc108/platform_ide.h new file mode 100644 index 00000000..d2313d31 --- /dev/null +++ b/Kernel/platform-sc108/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-sc108/rc2014.h b/Kernel/platform-sc108/rc2014.h new file mode 100644 index 00000000..fffce867 --- /dev/null +++ b/Kernel/platform-sc108/rc2014.h @@ -0,0 +1,23 @@ +#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; + +#endif diff --git a/Kernel/platform-sc108/rules.mk b/Kernel/platform-sc108/rules.mk new file mode 100644 index 00000000..c3701b05 --- /dev/null +++ b/Kernel/platform-sc108/rules.mk @@ -0,0 +1 @@ +export BANKED=-thunked diff --git a/Kernel/platform-sc108/sc108.s b/Kernel/platform-sc108/sc108.s new file mode 100644 index 00000000..f9ead528 --- /dev/null +++ b/Kernel/platform-sc108/sc108.s @@ -0,0 +1,494 @@ +; +; SC108 Initial Support +; + + .module sc108 + + ; 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 + + ; 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 _ser_type + + .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 + +_platform_monitor: + ; Reboot ends up back in the monitor +_platform_reboot: + xor a + out (0x38), a ; ROM appears low + rst 0 ; bang + +; ----------------------------------------------------------------------------- +; +; 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: + ld hl, #128 + ld (_ramsize), hl + ld hl,#64 + ld (_procmem), hl + + ld hl,#s__COMMONMEM + ld ix,#s__COMMONMEM + ld bc,#l__COMMONMEM + xor a ; Kernel + ROM + out (0x38),a ; + + ld de,#0x8000 ; bank 0 to bank 1 (ROM in) + xor a ; return to bank 0 + call 0x7FFD ; ROM helper vector + + ld a,#0x01 ; bank 0 ROM out + out (0x38),a + 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 + + ; Play guess the serial port + ld bc,#0x80 + ; If writing to 0x80 changes the data we see on an input then + ; it's most likely an SIO and not the 68B50 + out (c),b + in d,(c) + inc b + out (c),b + in a,(c) + sub d +;;FIXME jr z, is_sio + + ; We have however pooped on the 68B50 setup so put it back into + ; a sensible state. + ld a,#0x03 + out (c),a + in a,(c) + or a + jr z, not_acia_either + ld a, #ACIA_RTS_LOW_A + out (c),a ; Initialise ACIA + ld a,#2 + ld (_ser_type),a + im 1 + ret + + ; + ; Doomed I say .... doomed, we're all doomed + ; + ; At least until RC2014 grows a nice keyboard/display card! + ; +not_acia_either: + xor a + ld (_ser_type),a + ret + +is_sio: ld a,b + ld (_ser_type),a + +init_partial_uart: + ld a,#0x00 + out (SIOA_C),a + ld a,#0x18 + out (SIOA_C),a + + ld a,#0x04 + out (SIOA_C),a + ld a,#0xC4 + out (SIOA_C),a + + ld a,#0x01 + out (SIOA_C),a + ld a,#0x18;A? ; Receive int mode 11, tx int enable (was $18) + out (SIOA_C),a + + ld a,#0x03 + out (SIOA_C),a + ld a,#0xE1 + out (SIOA_C),a + +RTS_LOW .EQU 0xEA + + ld a,#0x05 + out (SIOA_C),a + ld a,#RTS_LOW + out (SIOA_C),a + + ld a,#0x00 + out (SIOB_C),a + ld a,#0x18 + out (SIOB_C),a + + ld a,#0x04 + out (SIOB_C),a + ld a,#0xC4 + out (SIOB_C),a + + ld a,#0x01 + out (SIOB_C),a + ld a, #0x18;A? ; Receive int mode 11, tx int enable (was $18) + out (SIOB_C),a + + ld a,#0x03 + out (SIOB_C),a + ld a,#0xE1 + out (SIOB_C),a + + ld a,#0x05 + out (SIOB_C),a + ld a,#RTS_LOW + out (SIOB_C),a + + 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 +; +; We use the MIDI port for debug - it's not useful for much else after all. +; +outchar: + push af + ; wait for transmitter to be idle +ocloop_sio: + xor a ; read register 0 + out (SIOA_C), a + in a,(SIOA_C) ; read Line Status Register + and #0x04 ; get THRE bit + jr z,ocloop_sio + ; now output the char to serial port + pop af + out (SIOA_D),a + 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: + ld a,#0x81 + out (0x38),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 +; +; This needs some properly optimized versions! +; +ldir_to_user: + ld de,#0x0181 ; from bank 0 to bank 1 +ldir_far: + push bc + ld c,#0x38 + 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,#1 + out (0x38),a + ret +ldir_from_user: + ld de,#0x8101 + jr ldir_far +; +; High stubs. Present in each bank in the top 256 bytes +; +interrupt_high: + push af + push de + push hl + ld a,(banknum) + ld l,a + ld a,#0x01 + out (0x38),a ; Bank 0, no ROM + ld (istack_switched_sp),sp ; istack is positioned to be valid + ld sp,#istack_top ; in both banks. We just have to + push hl ; save return bank + call interrupt_handler ; switch on the right SP + pop hl + ; 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 + ; User stack is now valid + ; back on user stack + xor a + cp e + call nz, sigpath + 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,l + out (0x38),a + pop hl + pop de + pop af + ei + ret + +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 + ex af, af' ; Ick - find a better way to do this bit ! + push af + ld a,#1 + out (0x38),a + pop af + 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 + 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 bc ; 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-sc108/target.mk b/Kernel/platform-sc108/target.mk new file mode 100644 index 00000000..3bffcde0 --- /dev/null +++ b/Kernel/platform-sc108/target.mk @@ -0,0 +1 @@ +export CPU = z80 diff --git a/Kernel/platform-sc108/tricks.s b/Kernel/platform-sc108/tricks.s new file mode 100644 index 00000000..0113d75b --- /dev/null +++ b/Kernel/platform-sc108/tricks.s @@ -0,0 +1,5 @@ + .include "kernel.def" + .include "../kernel.def" + + .include "../lib/z80single.s" + diff --git a/Kernel/platform-sc108/usermem.s b/Kernel/platform-sc108/usermem.s new file mode 100644 index 00000000..ff9cc95f --- /dev/null +++ b/Kernel/platform-sc108/usermem.s @@ -0,0 +1,144 @@ +; +; 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 + 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 + ret + +__ugetc: + pop bc + pop hl + push hl + push bc + ld a,#0x81 + out (0x38),a + ld l,(hl) + ld h,#0 + jr uout + +__ugetw: + pop bc + pop hl + push hl + push bc + ld a,#0x81 + out (0x38),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 + ld (hl),e + jr uout + +__uputw: + pop bc + pop de + pop hl + push hl + push de + push bc + ld a,#0x81 + out (0x38),a + ld (hl),e + inc hl + ld (hl),d + jr uout -- 2.34.1