From a3709add7129c2288b3c5c6b5a1967888268f378 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 11 Sep 2018 23:00:32 +0100 Subject: [PATCH] rc2014-tiny: quick debugging session This seems to kind of work. I'm sure it's not perfect. Anyway it's mostly intended as a bit of fun --- Kernel/platform-rc2014-tiny/Makefile | 56 +++ Kernel/platform-rc2014-tiny/README | 34 ++ Kernel/platform-rc2014-tiny/commonmem.s | 9 + Kernel/platform-rc2014-tiny/config.h | 65 +++ Kernel/platform-rc2014-tiny/crt0.s | 165 ++++++++ Kernel/platform-rc2014-tiny/devices.c | 37 ++ Kernel/platform-rc2014-tiny/devinput.c | 61 +++ Kernel/platform-rc2014-tiny/devinput.h | 1 + Kernel/platform-rc2014-tiny/devtty.c | 142 +++++++ Kernel/platform-rc2014-tiny/devtty.h | 10 + Kernel/platform-rc2014-tiny/discard.c | 41 ++ Kernel/platform-rc2014-tiny/fuzix.lnk | 45 +++ Kernel/platform-rc2014-tiny/kernel.def | 24 ++ Kernel/platform-rc2014-tiny/main.c | 122 ++++++ Kernel/platform-rc2014-tiny/platform_ide.h | 6 + Kernel/platform-rc2014-tiny/ppide.c | 121 ++++++ Kernel/platform-rc2014-tiny/rc2014.h | 21 + Kernel/platform-rc2014-tiny/rc2014.s | 437 +++++++++++++++++++++ Kernel/platform-rc2014-tiny/rules.mk | 15 + Kernel/platform-rc2014-tiny/target.mk | 1 + Kernel/platform-rc2014-tiny/tricks.s | 227 +++++++++++ 21 files changed, 1640 insertions(+) create mode 100644 Kernel/platform-rc2014-tiny/Makefile create mode 100644 Kernel/platform-rc2014-tiny/README create mode 100644 Kernel/platform-rc2014-tiny/commonmem.s create mode 100644 Kernel/platform-rc2014-tiny/config.h create mode 100644 Kernel/platform-rc2014-tiny/crt0.s create mode 100644 Kernel/platform-rc2014-tiny/devices.c create mode 100644 Kernel/platform-rc2014-tiny/devinput.c create mode 100644 Kernel/platform-rc2014-tiny/devinput.h create mode 100644 Kernel/platform-rc2014-tiny/devtty.c create mode 100644 Kernel/platform-rc2014-tiny/devtty.h create mode 100644 Kernel/platform-rc2014-tiny/discard.c create mode 100644 Kernel/platform-rc2014-tiny/fuzix.lnk create mode 100644 Kernel/platform-rc2014-tiny/kernel.def create mode 100644 Kernel/platform-rc2014-tiny/main.c create mode 100644 Kernel/platform-rc2014-tiny/platform_ide.h create mode 100644 Kernel/platform-rc2014-tiny/ppide.c create mode 100644 Kernel/platform-rc2014-tiny/rc2014.h create mode 100644 Kernel/platform-rc2014-tiny/rc2014.s create mode 100644 Kernel/platform-rc2014-tiny/rules.mk create mode 100644 Kernel/platform-rc2014-tiny/target.mk create mode 100644 Kernel/platform-rc2014-tiny/tricks.s diff --git a/Kernel/platform-rc2014-tiny/Makefile b/Kernel/platform-rc2014-tiny/Makefile new file mode 100644 index 00000000..4e44a3be --- /dev/null +++ b/Kernel/platform-rc2014-tiny/Makefile @@ -0,0 +1,56 @@ +ASRCS = crt0.s tricks.s commonmem.s rc2014.s +CSRCS = devices.c main.c devtty.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/ + +CROSS_CC_HIGH = --codeseg COMMONMEM + +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) $(CROSS_CC_HIGH) -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: + dd if=../fuzix.bin of=fuzix.rom bs=32768 count=1 + # We have a 512 byte hole we leave for the loader + dd if=../fuzix.bin of=diskstrap bs=33280 count=1 skip=1 + diff --git a/Kernel/platform-rc2014-tiny/README b/Kernel/platform-rc2014-tiny/README new file mode 100644 index 00000000..0f949248 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/README @@ -0,0 +1,34 @@ +This is a prototype Fuzix for the RC2014 with only the pageable ROM. Fuzix +occupies the ROM and loads its data and high space from disk on boot. + +This is btw not a good way to run Fuzix, it's more for the challenge! + +Memory map + +Kernel mode + +0000-7FFF Kernel (and bootstrap) +8000-BFFF Discardables / Userspace +C000-FFFF Kernel + +User mode (one process in memory at a time, swap mandatory) + +0000-BFFF User space +C000-FFFF Kernel + + +Things that don't work + + * Flow control isn't yet enabled for the serial port. + +Stuff To Do + + * SIO v ACIA detection is bust + + * It could be made a chunk smaller if we dumped the nice fancy IDE support + for simple block IDE without full partitions etc. + + * Set up SIO with some kind of fast IRQ path or we'll drop a lot of bytes + (perhaps in IRQ only do rtc check if fifo empty or something ?) + + * RETI fix on polled SIO path diff --git a/Kernel/platform-rc2014-tiny/commonmem.s b/Kernel/platform-rc2014-tiny/commonmem.s new file mode 100644 index 00000000..12884457 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/commonmem.s @@ -0,0 +1,9 @@ +; +; Common is placed at 0xF000 by fuzix.lnk +; + + .module commonmem + + .area _COMMONMEM + + .include "../cpu-z80/std-commonmem.s" diff --git a/Kernel/platform-rc2014-tiny/config.h b/Kernel/platform-rc2014-tiny/config.h new file mode 100644 index 00000000..184048cb --- /dev/null +++ b/Kernel/platform-rc2014-tiny/config.h @@ -0,0 +1,65 @@ +/* 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 0xC000 /* Top of program */ +#define KERNTOP 0xFFFF /* common grows from C000, buffers above*/ + +#define PROC_SIZE 48 /* Memory needed per process */ + +#define SWAPDEV (swap_dev) /* A variable for dynamic, or a device major/minor */ +extern unsigned int swap_dev; +#define SWAP_SIZE 0x61 /* 48.5K in blocks (prog + udata) */ +#define SWAPBASE 0x0000 /* start at the base of user mem */ +#define SWAPTOP 0xC200 /* Swap out udata and program */ +#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 + +/* + * 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-rc2014-tiny/crt0.s b/Kernel/platform-rc2014-tiny/crt0.s new file mode 100644 index 00000000..97574458 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/crt0.s @@ -0,0 +1,165 @@ +; 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. + + ; Start with the ROM area CODE-CODE2 + .area _CODE + .area _HOME ; compiler stores __mullong etc in here if you use them + .area _CODE2 + ; Discard is loaded where process memory wil blow it away + .area _DISCARD + ; The rest grows upwards from C000 starting with the udata so we can + ; swap in one block, ending with the buffers so they can expand up + .area _COMMONMEM + .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) + ; These get overwritten and don't matter + .area _INITIALIZER ; binman copies this to the right place for us + .area _GSINIT ; unused + .area _GSFINAL ; unused + + ; 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 + + .globl interrupt_handler + .globl nmi_handler + + .include "kernel.def" + + ; startup code + .area _PAGE0(ABS) + +.org 0 + +restart0: + jp init + .ds 5 +restart8: + .ds 8 +restart10: + .ds 8 +restart18: + .ds 8 +restart20: + .ds 8 +restart28: + .ds 8 +restart30: + .ds 8 +restart38: + jp interrupt_handler + .ds 5 +; 0x40 + .ds 26 +; 0x66 + jp nmi_handler +; +; And 0x69 onwards we could use for code +; + + ; Starts at 0x0080 at the moment + + .area _CODE + +init: + di + ld sp,#0x8200 ; safe spot + + ; Init the ATA CF + ; For now this is fairly dumb. + ; We ought to init the UART here first so we can say something + ; before loading. + ld a,#0xE0 + out (0x16),a + xor a + out (0x14),a + out (0x15),a + ; Set 8bit mode + call wait_ready + ld a, #1 ; 8bit PIO + out (0x11),a + ld a, #0xEF ; SET FEATURES (8bit PIO) + out (0x17),a + call wait_ready + + ; Load and map the rest of the image + ld d,#1 + ld bc,#0x10 ; c = data port b = 0 + ld hl,#0x8200 ; Load 8200-FFFF +loader: + inc d + call load_sector + bit 6,d ; load 64 sectors 2-66 + jr z, loader + + ; switch to stack in high memory + ld sp, #kstack_top + + ; Zero the data area + 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 + +; +; Load sector d from disk into HL and advance HL accordingly +; +load_sector: + ld a,d + out (0x13),a ; LBA + ld a,#1 + out (0x12),a ; 1 sector + ld a,#0x20 + out (0x17),a ; command + ; Wait +wait_drq: + in a,(0x17) + bit 3,a + jr z, wait_drq + ; Get data, leave HL pointing to next byte, leaves B as 0 again + inir + inir + ret +wait_ready: + in a,(0x17) + bit 6,a + jr z,wait_ready + ret + \ No newline at end of file diff --git a/Kernel/platform-rc2014-tiny/devices.c b/Kernel/platform-rc2014-tiny/devices.c new file mode 100644 index 00000000..f3f1029c --- /dev/null +++ b/Kernel/platform-rc2014-tiny/devices.c @@ -0,0 +1,37 @@ +#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-rc2014-tiny/devinput.c b/Kernel/platform-rc2014-tiny/devinput.c new file mode 100644 index 00000000..4e5eb54f --- /dev/null +++ b/Kernel/platform-rc2014-tiny/devinput.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include + +__sfr __at 0x01 js1; +__sfr __at 0x02 js2; + +static uint8_t js_data[2]= {255, 255}; + +uint8_t read_js(uint8_t *slot, uint8_t n, uint8_t r) +{ + uint8_t d = 0; + r &= 63; + if (r == js_data[n]) + return 0; + js_data[n] = r; + if (r & 1) + d = STICK_DIGITAL_U; + if (r & 2) + d |= STICK_DIGITAL_D; + if (r & 4) + d |= STICK_DIGITAL_L; + if (r & 8) + d |= STICK_DIGITAL_R; + if (r & 16) + d |= BUTTON(0); + if (r & 32) + d |= BUTTON(1); + *slot++ = STICK_DIGITAL | (n + 1); + *slot = d; + return 2; +} + +int platform_input_read(uint8_t *slot) +{ + if (read_js(slot, 0, js1)) + return 2; + if (read_js(slot, 1, js2)) + return 2; + return 0; +} + +void platform_input_wait(void) +{ + psleep(&js_data); +} + +void poll_input(void) +{ + if ((js1 & 63) != js_data[0] || + ((js2 & 63) != js_data[1])) + wakeup(&js_data); +} + +int platform_input_write(uint8_t flag) +{ + flag; + udata.u_error = EINVAL; + return -1; +} diff --git a/Kernel/platform-rc2014-tiny/devinput.h b/Kernel/platform-rc2014-tiny/devinput.h new file mode 100644 index 00000000..5e7922aa --- /dev/null +++ b/Kernel/platform-rc2014-tiny/devinput.h @@ -0,0 +1 @@ +extern void poll_input(void); diff --git a/Kernel/platform-rc2014-tiny/devtty.c b/Kernel/platform-rc2014-tiny/devtty.c new file mode 100644 index 00000000..4b3d955f --- /dev/null +++ b/Kernel/platform-rc2014-tiny/devtty.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include +#include + +char tbuf1[TTYSIZ]; +char tbuf2[TTYSIZ]; + +uint8_t ser_type = 1; + +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) { + tty_inproc(1, ACIA_D); + } + if (ca & 2) { + 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-rc2014-tiny/devtty.h b/Kernel/platform-rc2014-tiny/devtty.h new file mode 100644 index 00000000..518450e6 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/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-rc2014-tiny/discard.c b/Kernel/platform-rc2014-tiny/discard.c new file mode 100644 index 00000000..5766275b --- /dev/null +++ b/Kernel/platform-rc2014-tiny/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-rc2014-tiny/fuzix.lnk b/Kernel/platform-rc2014-tiny/fuzix.lnk new file mode 100644 index 00000000..ce72d6a1 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/fuzix.lnk @@ -0,0 +1,45 @@ +-mwxuy +-i fuzix.ihx +-b _CODE=0x0080 +-b _COMMONMEM=0xC000 +-b _DISCARD=0x8200 +-b _INITIALIZER=0xA000 +-l z80 +platform-rc2014-tiny/crt0.rel +platform-rc2014-tiny/commonmem.rel +platform-rc2014-tiny/rc2014.rel +start.rel +version.rel +lowlevel-z80.rel +platform-rc2014-tiny/tricks.rel +platform-rc2014-tiny/main.rel +timer.rel +kdata.rel +platform-rc2014-tiny/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 +usermem_std-z80.rel +platform-rc2014-tiny/discard.rel +platform-rc2014-tiny/devtty.rel +platform-rc2014-tiny/mbr.rel +platform-rc2014-tiny/blkdev.rel +platform-rc2014-tiny/devide.rel +platform-rc2014-tiny/devide_discard.rel +platform-rc2014-tiny/ds1302.rel +platform-rc2014-tiny/ds1302_discard.rel +platform-rc2014-tiny/ds1302_rc2014.rel +-e diff --git a/Kernel/platform-rc2014-tiny/kernel.def b/Kernel/platform-rc2014-tiny/kernel.def new file mode 100644 index 00000000..f7ca5ea9 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/kernel.def @@ -0,0 +1,24 @@ +; FUZIX mnemonics for memory addresses etc + +U_DATA .equ 0xC000 ; (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 + +; 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 diff --git a/Kernel/platform-rc2014-tiny/main.c b/Kernel/platform-rc2014-tiny/main.c new file mode 100644 index 00000000..1a010362 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/main.c @@ -0,0 +1,122 @@ +#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 */ +uint8_t timermsr = 0; +uint16_t swap_dev = 0xFFFF; +uint16_t ramtop = 0xC000; +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++; + } +} + +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) +{ + /* FIXME: need to cover ACIA option.. */ + tty_pollirq_sio(); + 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-rc2014-tiny/platform_ide.h b/Kernel/platform-rc2014-tiny/platform_ide.h new file mode 100644 index 00000000..8343e588 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/platform_ide.h @@ -0,0 +1,6 @@ +#define ide_select(x) +#define ide_deselect() + +/*8bit, no altstatus/control */ +#define IDE_8BIT_ONLY +#define IDE_REG_CS1_BASE 0x10 diff --git a/Kernel/platform-rc2014-tiny/ppide.c b/Kernel/platform-rc2014-tiny/ppide.c new file mode 100644 index 00000000..4e80bf1b --- /dev/null +++ b/Kernel/platform-rc2014-tiny/ppide.c @@ -0,0 +1,121 @@ +/* 2015-04-24 WRS: devide glue functions for PPIDE */ + +#include +#include +#include +#include +#include +#include +#include + +__sfr __at (PPIDE_BASE + 0x00) ppi_port_a; /* IDE bus LSB */ +__sfr __at (PPIDE_BASE + 0x01) ppi_port_b; /* IDE bus MSB */ +__sfr __at (PPIDE_BASE + 0x02) ppi_port_c; /* IDE bus control signals */ +__sfr __at (PPIDE_BASE + 0x03) ppi_control; /* 8255 command register */ + +void ppide_init(void) +{ + ppi_control = PPIDE_PPI_BUS_READ; + ppi_port_c = ide_reg_status; +} + +uint8_t devide_readb(uint8_t regaddr) +{ + uint8_t r; + + /* note: ppi_control should contain PPIDE_PPI_BUS_READ already */ + ppi_port_c = regaddr; + ppi_control = 1 | (PPIDE_RD_BIT << 1); /* begin /RD pulse */ + r = ppi_port_a; + ppi_control = 0 | (PPIDE_RD_BIT << 1); /* end /RD pulse */ + return r; +} + +void devide_writeb(uint8_t regaddr, uint8_t value) +{ + ppi_control = PPIDE_PPI_BUS_WRITE; + ppi_port_c = regaddr; + ppi_port_a = value; + ppi_port_b = 0; + ppi_control = 1 | (PPIDE_WR_BIT << 1); /* begin /WR pulse */ + ppi_control = 0 | (PPIDE_WR_BIT << 1); /* end /WR pulse */ + ppi_control = PPIDE_PPI_BUS_READ; +} + +/****************************************************************************/ +/* The innermost part of the transfer routines has to live in common memory */ +/* since it must be able to bank switch to the user memory bank. */ +/****************************************************************************/ +COMMON_MEMORY + +void devide_read_data(void) __naked +{ + __asm + ld a, #ide_reg_data + ld c, #PPIDE_BASE+2 ; select control lines + out (c), a ; select IDE data register + ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr + ld d, #ide_reg_data ; register address + ld e, #ide_reg_data | PPIDE_RD_LINE ; register address with /RD asserted + ld b, #0 ; setup count + ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user + or a ; test is_user + push af ; save flags + ld a, #PPIDE_BASE+0 ; I will be needing this later + call nz, map_process_always ; map user memory first if required +goread: ; now we do the transfer + out (c), e ; assert /RD + ld c, a ; PPIDE_BASE + ini ; read byte from LSB + inc c ; up to MSB + ini ; read byte from MSB + inc c ; control lines + out (c), d ; de-assert /RD + inc b ; (delay) counteract second ini instruction + jr nz, goread ; (delay) next word + ; read completed + pop af ; recover is_user test result + ret z ; done if kernel memory transfer + jp map_kernel ; else map kernel then return + __endasm; +} + +void devide_write_data(void) __naked +{ + __asm + ld c, #PPIDE_BASE+2 ; select control lines + ld a, #ide_reg_data + out (c), a ; select data register + ld a, #PPIDE_PPI_BUS_WRITE + inc c ; up to 8255A command register + out (c), a ; 8255A ports A, B to output mode + dec c ; back down to the control lines + ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr + ld d, #ide_reg_data ; register address + ld e, #ide_reg_data | PPIDE_WR_LINE ; register address with /WR asserted + ld b, #0 ; setup count + ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user + or a ; test is_user + push af ; save flags + ld a, #PPIDE_BASE+0 ; I will be needing this later + call nz, map_process_always ; map user memory first if required +gowrite: ; now we do the transfer + out (c), d ; de-assert /WR + ld c, a ; PPIDE_BASE + outi ; write byte to LSB + inc c ; up to MSB + outi ; write byte to MSB + inc c ; up to control lines + out (c), e ; assert /WR + inc b ; (delay) offset to counteract second outi instruction + jr nz, gowrite ; (delay) next word + ; write completed + out (c), d ; de-assert /WR + ld a, #PPIDE_PPI_BUS_READ + inc c ; up to 8255A command register + out (c), a ; 8255A ports A, B to read mode + pop af ; recover is_user test result + ret z ; done if kernel memory transfer + jp map_kernel ; else map kernel then return + __endasm; +} diff --git a/Kernel/platform-rc2014-tiny/rc2014.h b/Kernel/platform-rc2014-tiny/rc2014.h new file mode 100644 index 00000000..2cf84045 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/rc2014.h @@ -0,0 +1,21 @@ +#ifndef __RC2014_SIO_DOT_H__ +#define __RC2014_SIO_DOT_H__ + +#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-rc2014-tiny/rc2014.s b/Kernel/platform-rc2014-tiny/rc2014.s new file mode 100644 index 00000000..01656509 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/rc2014.s @@ -0,0 +1,437 @@ +; 2014-02-19 Sergey Kiselev +; RC2014 hardware specific code + + .module rc2014 + + ; exported symbols + .globl init_hardware + .globl _program_vectors + .globl map_kernel + .globl map_process + .globl map_process_always + .globl map_save + .globl map_restore + .globl map_for_swap + .globl platform_interrupt_all + .globl _platform_reboot + .globl _platform_monitor + .globl _bufpool + + ; imported symbols + .globl _ramsize + .globl _procmem + .globl outhl + .globl outnewline + .globl interrupt_handler + .globl unix_syscall_entry + .globl nmi_handler + .globl null_handler + .globl _ser_type + + ; exported debugging tools + .globl inchar + .globl outchar + + .include "kernel.def" + .include "../kernel.def" + +;========================================================================= +; Constants +;========================================================================= +CONSOLE_DIVISOR .equ (1843200 / (16 * CONSOLE_RATE)) +CONSOLE_DIVISOR_HIGH .equ (CONSOLE_DIVISOR >> 8) +CONSOLE_DIVISOR_LOW .equ (CONSOLE_DIVISOR & 0xFF) + +RTS_HIGH .EQU 0xE8 +RTS_LOW .EQU 0xEA + +; 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 + +SIO_IV .EQU 8 ; Interrupt vector table entry to use + +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 +;========================================================================= + .area _BUFFERS +_bufpool: + .ds (BUFSIZE * 4) ; adjust NBUFS in config.h in line with this + +;========================================================================= +; Initialization code +;========================================================================= + .area _DISCARD +init_hardware: + ld hl,#64 + ld (_ramsize), hl + ld hl,#48 + ld (_procmem), hl + ; 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 + jr 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 nz, not_acia_either + ld a, #ACIA_RTS_LOW_A + out (c),a ; Initialise ACIA + ld a,#2 + ld (_ser_type),a + 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 + + 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,#0x02 + out (SIOB_C),a + ld a,#SIO_IV ; INTERRUPT VECTOR ADDRESS (needs to go) + 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 + + ; --------------------------------------------------------------------- + ; Initialize CTC + ; + ; 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 + 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 + + ; Done CTC Stuff + ; --------------------------------------------------------------------- + + im 1 ; set Z80 CPU interrupt mode 1 + ret + +;========================================================================= +; Kernel code +;========================================================================= + .area _CODE + +_platform_monitor: + di + halt +_platform_reboot: + ; We need to map the ROM back in -- ideally into every page. + ; This little trick based on a clever suggestion from John Coffman. + call map_kernel + rst 0 + +;========================================================================= +; Common Memory (0xF000 upwards) +;========================================================================= + .area _COMMONMEM + +;========================================================================= + +platform_interrupt_all: + ret + +; install interrupt vectors +_program_vectors: + di + pop de ; temporarily store return address + pop hl ; function argument -- base page number + push hl ; put stack back as it was + push de + + ; At this point the common block has already been copied + call map_process + + ; write zeroes across all vectors + ld hl,#0 + ld de,#1 + ld bc,#0x007f ; program first 0x80 bytes only + ld (hl),#0x00 + ldir + + ; now install the interrupt vector at 0x0038 + ld a,#0xC3 ; JP instruction + ld (0x0038),a + ld hl,#interrupt_handler + ld (0x0039),hl + + ; set restart vector for UZI system calls + ld (0x0030),a ; rst 30h is unix function call vector + ld hl,#unix_syscall_entry + ld (0x0031),hl + + ld (0x0000),a + ld hl,#null_handler ; to Our Trap Handler + ld (0x0001),hl + + ld (0x0066),a ; Set vector for NMI + ld hl,#nmi_handler + ld (0x0067),hl + + jr map_kernel + +;========================================================================= +; Memory management +;========================================================================= + +; +; The ROM is a toggle. That makes it exciting if we get it wrong! +; +; We *must* have interrupts off here to avoid double toggles. +; + +rom_toggle: + .byte 0 ; ROM starts mapped +save_rom: + .byte 0 ; For map_save/restore + +; +; Centralize all control of the toggle in one place so we can debug it +; +rom_control: + push bc ; Messy - clean me up! + ld c,a + push hl + ld a,i + push af + ld a,c + di + ld hl,#rom_toggle + cp (hl) + jr z, no_work + ld (hl),a + out (0x38),a ; anything toggles +no_work: + pop af + jp po, was_di + ei +was_di: pop hl + pop bc + ret + +;========================================================================= +; map_process - map process or kernel pages +; Inputs: page table address in HL, map kernel if HL == 0 +; Outputs: none; A and HL destroyed +;========================================================================= +map_process: + ld a,h + or l ; HL == 0? + jr z,map_kernel ; HL == 0 - map the kernel + + ; fall through + +;========================================================================= +; map_for_swap - map a page into a bank for swap I/O +; Inputs: none +; Outputs: none +; +; The caller will later map_kernel to restore normality +; +;========================================================================= +map_for_swap: + + ; fall through + +;========================================================================= +; map_process_always - map process pages +; Inputs: page table address in #U_DATA__U_PAGE +; Outputs: none; all registers preserved +;========================================================================= +map_process_always: + push af + ld a,#1 + call rom_control + pop af + ret + +;========================================================================= +; map_kernel - map kernel pages +; Inputs: none +; Outputs: none; all registers preserved +;========================================================================= +map_kernel: + push af + xor a + call rom_control + pop af + ret + +;========================================================================= +; map_restore - restore a saved page mapping +; Inputs: none +; Outputs: none, all registers preserved +;========================================================================= +map_restore: + push af + ld a,(save_rom) + call rom_control + pop af + ret + +;========================================================================= +; map_save - save the current page mapping to map_savearea +; Inputs: none +; Outputs: none +;========================================================================= +map_save: + push af + ld a,(rom_toggle) + ld (save_rom),a + pop af + ret + +;========================================================================= +; Basic console I/O +;========================================================================= + +;========================================================================= +; outchar - Wait for UART TX idle, then print the char in A +; Inputs: A - character to print +; Outputs: none +;========================================================================= +outchar: + + push af + ld a,(_ser_type) + cp #2 + jr z, ocloop_acia + + ; 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 + jr out_done + + ; wait for transmitter to be idle +ocloop_acia: + in a,(ACIA_C) ; read Line Status Register + and #0x02 ; get THRE bit + jr z,ocloop_acia + ; now output the char to serial port + pop af + out (ACIA_D),a +out_done: + ret + +;========================================================================= +; inchar - Wait for character on UART, return in A +; Inputs: none +; Outputs: A - received character, F destroyed +;========================================================================= +inchar: + ld a,(_ser_type) + cp #2 + jr z,inchar_acia +inchar_s: + xor a ; read register 0 + out (SIOA_C), a + in a,(SIOA_C) ; read Line Status Register + and #0x01 ; test if data is in receive buffer + jr z,inchar_s ; no data, wait + in a,(SIOA_D) ; read the character from the UART + ret +inchar_acia: + in a,(ACIA_C) ; read Line Status Register + and #0x01 ; test if data is in receive buffer + jr z,inchar_acia ; no data, wait + in a,(ACIA_D) ; read the character from the UART + ret diff --git a/Kernel/platform-rc2014-tiny/rules.mk b/Kernel/platform-rc2014-tiny/rules.mk new file mode 100644 index 00000000..2c727a9f --- /dev/null +++ b/Kernel/platform-rc2014-tiny/rules.mk @@ -0,0 +1,15 @@ +# +# Push the execve code high to make room +# +export CROSS_CC_SEG1=--codeseg CODE +export CROSS_CC_SEG2=--codeseg CODE2 +export CROSS_CC_SEG3=--codeseg CODE +export CROSS_CC_VIDEO=--codeseg CODE2 +# +export CROSS_CC_SYS1=--codeseg CODE +export CROSS_CC_SYS2=--codeseg CODE +export CROSS_CC_SYS3=--codeseg CODE +export CROSS_CC_SYS4=--codeseg CODE2 +export CROSS_CC_SYS5=--codeseg COMMONMEM +export CROSS_CC_SEGDISC=--codeseg DISCARD + diff --git a/Kernel/platform-rc2014-tiny/target.mk b/Kernel/platform-rc2014-tiny/target.mk new file mode 100644 index 00000000..3bffcde0 --- /dev/null +++ b/Kernel/platform-rc2014-tiny/target.mk @@ -0,0 +1 @@ +export CPU = z80 diff --git a/Kernel/platform-rc2014-tiny/tricks.s b/Kernel/platform-rc2014-tiny/tricks.s new file mode 100644 index 00000000..03a841ba --- /dev/null +++ b/Kernel/platform-rc2014-tiny/tricks.s @@ -0,0 +1,227 @@ +; 2013-12-21 William R Sowerbutts + + .module tricks + + .globl _ptab_alloc + .globl _newproc + .globl _chksigs + .globl _getproc + .globl _platform_monitor + .globl trap_illegal + .globl _platform_switchout + .globl _switchin + .globl _doexec + .globl _dofork + .globl _runticks + .globl unix_syscall_entry + .globl interrupt_handler + .globl _swapper + .globl _swapout + + ; imported debug symbols + .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex + + .include "kernel.def" + .include "../kernel.def" + + .area _COMMONMEM + +; Switchout switches out the current process, finds another that is READY, +; possibly the same process, and switches it in. When a process is +; restarted after calling switchout, it thinks it has just returned +; from switchout(). +; +; This function can have no arguments or auto variables. +_platform_switchout: + di + ; save machine state + + ld hl, #0 ; return code set here is ignored, but _switchin can + ; return from either _switchout OR _dofork, so they must both write + ; U_DATA__U_SP with the following on the stack: + push hl ; return code + push ix + push iy + ld (U_DATA__U_SP), sp ; this is where the SP is restored in _switchin + + ; find another process to run (may select this one again) + call _getproc + + push hl + call _switchin + + ; we should never get here + call _platform_monitor + +badswitchmsg: .ascii "_switchin: FAIL" + .db 13, 10, 0 +swapped: .ascii "_switchin: SWAPPED" + .db 13, 10, 0 + +_switchin: + di + pop bc ; return address + pop de ; new process pointer +; +; FIXME: do we actually *need* to restore the stack ! +; + push de ; restore stack + push bc ; restore stack + + push de + ld hl, #P_TAB__P_PAGE_OFFSET + add hl, de ; process ptr + pop de + + ld a, (hl) + + or a + jr nz, not_swapped + ; + ; We are still on the departing processes stack, which is + ; fine for now. + ; + ld sp, #_swapstack + ; + ; The process we are switching from is not resident. In + ; practice that means it exited. We don't need to write + ; it back out as it's dead. + ; + ld ix,(U_DATA__U_PTAB) + ld a,P_TAB__P_PAGE_OFFSET(ix) + + or a + jr z, not_resident + ; + ; DE = process we are switching to + ; + push de ; save process + ; We will always swap out the current process + ld hl, (U_DATA__U_PTAB) + push hl + call _swapout + pop hl + pop de ; process to swap to +not_resident: + push de ; called function may modify on stack arg so make + push de ; two copies + call _swapper + pop de + pop de + +not_swapped: + ; check u_data->u_ptab matches what we wanted + ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab + or a ; clear carry flag + sbc hl, de ; subtract, result will be zero if DE==IX + jr nz, switchinfail + + ; wants optimising up a bit + ld ix, (U_DATA__U_PTAB) + ; next_process->p_status = P_RUNNING + ld P_TAB__P_STATUS_OFFSET(ix), #P_RUNNING + + ; Fix the moved page pointers + ; Just do one byte as that is all we use on this platform + ld a, P_TAB__P_PAGE_OFFSET(ix) + ld (U_DATA__U_PAGE), a + ; runticks = 0 + ld hl, #0 + ld (_runticks), hl + + ; restore machine state -- note we may be returning from either + ; _switchout or _dofork + ld sp, (U_DATA__U_SP) + + pop iy + pop ix + pop hl ; return code + + ; enable interrupts, if the ISR isn't already running + ld a, (U_DATA__U_ININTERRUPT) + or a + ret nz ; in ISR, leave interrupts off + ei + ret ; return with interrupts on + +switchinfail: + call outhl + ld hl, #badswitchmsg + call outstring + ; something went wrong and we didn't switch in what we asked for + jp _platform_monitor + +fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry + +; +; Called from _fork. We are in a syscall, the uarea is live as the +; parent uarea. The kernel is the mapped object. +; +_dofork: + ; always disconnect the vehicle battery before performing maintenance + di ; should already be the case ... belt and braces. + + pop de ; return address + pop hl ; new process p_tab* + push hl + push de + + ld (fork_proc_ptr), hl + + ; prepare return value in parent process -- HL = p->p_pid; + ld de, #P_TAB__P_PID_OFFSET + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + + ; Save the stack pointer and critical registers. + ; When this process (the parent) is switched back in, it will be as if + ; it returns with the value of the child's pid. + push hl ; HL still has p->p_pid from above, the return value in the parent + push ix + push iy + + ; save kernel stack pointer -- when it comes back in the parent we'll be in + ; _switchin which will immediately return (appearing to be _dofork() + ; returning) and with HL (ie return code) containing the child PID. + ; Hurray. + ld (U_DATA__U_SP), sp + + ; now we're in a safe state for _switchin to return in the parent + ; process. + + ld hl, (U_DATA__U_PTAB) + push hl + call _swapout + pop hl + + ; now the copy operation is complete we can get rid of the stuff + ; _switchin will be expecting from our copy of the stack. + pop bc + pop bc + pop bc + + ; Make a new process table entry, etc. + ld hl, (fork_proc_ptr) + push hl + call _newproc + pop bc + + ; runticks = 0; + ld hl, #0 + ld (_runticks), hl + ; in the child process, fork() returns zero. + ; + ; And we exit, with the kernel mapped, the child now being deemed + ; to be the live uarea. The parent is frozen in time and space as + ; if it had done a switchout(). + ret +; +; We can keep a stack in common because we will complete our +; use of it before we switch common block. In this case we have +; a true common so it's even easier. +; + .ds 128 +_swapstack: -- 2.34.1