From: Alan Cox Date: Wed, 20 Feb 2019 23:05:38 +0000 (+0000) Subject: zxdiv48: Initial code for 48K ZX Spectrum with DivIDE plus or ZXCF X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=ef66604c118702a3a839bcd1a19b9e88e42b27df;p=FUZIX.git zxdiv48: Initial code for 48K ZX Spectrum with DivIDE plus or ZXCF Queueing this up for weekend testing. It seemed a shame that the classic squishy key machines were being left out so there was only one thing left to do - fix it. It's a curious combination of the 128K/DivIDE model and the single process in memory model. The kernel is banked four ways in the low 16K using memory from the DivIDE Plus or ZXCF card. The area between the screen and 0x8000 just about fits enough data for a minimal system providing the disk buffers are banked. From 0x800-0xFBFF is then used for the application space with FC00-FFFF holding the stacks, udata and a 256 byte space we may yet need. The rest of the 512/1024K of RAM on the interface is then used as swap. Task switching is slow (two x 32K ldir) but that is no worse than the disk load/save used by other single process in memory platforms. The biggest problem is the limited amount of unbanked kernel RAM. With the screen at 0x4000-0x5AFF we only have a shade over 8K for all our common, our data, constants and bank switching stubs. To get it to fit we end up limiting our user space to 31K (versus 31.5K on most other 32K switched bank platforms). --- diff --git a/Kernel/platform-zxdiv48/Makefile b/Kernel/platform-zxdiv48/Makefile new file mode 100644 index 00000000..6552a7de --- /dev/null +++ b/Kernel/platform-zxdiv48/Makefile @@ -0,0 +1,72 @@ +CSRCS = devtty.c devices.c main.c buffers.c divide.c zxcf.c devrd.c ide.c +CDSRCS = discard.c +DSRCS = ../dev/devide.c ../dev/blkdev.c +DDSRCS = ../dev/devide_discard.c ../dev/mbr.c +DZSRCS = ../dev/zx/zxkeyboard.c +DZSRCS += ../dev/zx/devinput.c +DDZSRCS = +ASRCS = crt0.s zx48.s zxvideo.s rd_divideplus.s +ASRCS += tricks.s commonmem.s + +COBJS = $(CSRCS:.c=.rel) +CDOBJS = $(CDSRCS:.c=.rel) +AOBJS = $(ASRCS:.s=.rel) +DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS)) +DDOBJS = $(patsubst ../dev/%.c,%.rel, $(DDSRCS)) +DZOBJS = $(patsubst ../dev/zx/%.c,%.rel, $(DZSRCS)) +DDZOBJS = $(patsubst ../dev/zx/%.c,%.rel, $(DDZSRCS)) +OBJS = $(COBJS) $(CDOBJS) $(AOBJS) $(DOBJS) $(DDOBJS) $(DZOBJS) $(DDZOBJS) + +CROSS_CCOPTS += -I../dev/ -I../dev/zx/ + +CROSS_CC_SEG3 = --codeseg CODE3 + +all: $(OBJS) + +$(COBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG3) -c $< + +$(CDOBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(DOBJS): %.rel: ../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG3) -c $< + +$(DDOBJS): %.rel: ../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(DZOBJS): %.rel: ../dev/zx/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG3) -c $< + +$(DDZOBJS): %.rel: ../dev/zx/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(AOBJS): %.rel: %.s + $(CROSS_AS) $(ASOPTS) $< + +clean: + rm -f $(OBJS) *.lst *.asm *.sym *.rst *.rel core *~ + rm -f *.ihx *.tmp loader loader-zxcf + rm -f FUZIX.BIN FUZIX + +# Re-order the image and snapshop it +image: + # Build a generic DivIDE plus loader + sdasz80 -o loader.s + sdldz80 -i loader.rel + makebin -s 57856 loader.ihx loader.tmp + # Build a generic ZXCF+ style loader + sdasz80 -o loader-zxcf.s + sdldz80 -i loader-zxcf.rel + makebin -s 57856 loader-zxcf.ihx loader-zxcf.tmp + # Generate the image file we need + dd if=loader.tmp of=FUZIX bs=8192 skip=1 + + # Now build a FUZIX.BIN to load + # in the order we need it + dd if=../bank1.bin bs=16384 >FUZIX.BIN conv=sync + dd if=../bank2.bin bs=16384 >>FUZIX.BIN conv=sync + dd if=../bank3.bin bs=16384 >>FUZIX.BIN conv=sync + dd if=../bank4.bin bs=16384 >>FUZIX.BIN conv=sync + dd if=../common.bin bs=16384 >>FUZIX.BIN skip=1 count=1 conv=sync + dd if=../common.bin bs=16384 >>FUZIX.BIN skip=2 count=1 conv=sync diff --git a/Kernel/platform-zxdiv48/README b/Kernel/platform-zxdiv48/README new file mode 100644 index 00000000..1b39dca0 --- /dev/null +++ b/Kernel/platform-zxdiv48/README @@ -0,0 +1,83 @@ +Just a mad experiment for now + +TODO +- Wrap the loader into something useful like a .tap or snapshot for FATware +- Maybe use ResiDOS for the ZXCF+ loader ? +- Test Loader +- Test Kernel +- tmpbuf and execve(). Not quite as simple as keeping the buffers in the + execve bank as we also use one right now in fork() and in IDE. + +Possible space savers + tmpbuf2 can go banked (we have ~300 bytes data free so this would + save us 512 more!) + +Fuzix for the 48K Spectrum and clones with DivIDE plus and thus 512K of RAM +that can only be banked in the low 16K, plus video pinned at 0x4000-5AFF. + +DivIDE plus + +0xE3: 7: conmem 6: mapram 0-5: bank + (may only be 4 banks) + + 7 set ROM at 0000-1FFF and RAM at 2000-3FFF (banked) + and pins it (overrides 6) + 6 is a one shot and write protects bank 3 and places it at + 0000-1FFF, + 5-0: are the bank at 2000-3FFF + +0x1F 000AAAAX Normal DivIDE mode AAAA is the 32K bank for the 4x8K + mapped + 001XXXXX Enable 128K mode + 01WAAAAA RAM mode. Disables DivIDE traps and behaviour. Maps + the given 16K bank (AAAAA) into the low 16K, writable + if W is set. + 10WAAAAA As above with ROM mapped (W means flash rewrite is + possible). We don't bother putting Fuzix in flash + 11XXXXXX Reset DivIDE plus next ROM access/refresh cycle + if I = 0-63 + +We only use the RAM mode. The RAM is divided up into banked chunks and +the 16K chunks hold the kernel plus buffers etc. + +We run with the following mapping + +0000-007F Reset stubs in each bank (interrupts etc) +0080-3FFF Code in each bank +4000-5AFF Spectrum screen memory and system variables +5B00-7FFF Kernel data + +8000-83FF Needed to create the exec of init +A000- _DISCARD area - blown away when we exec init + (tons of room in this space) + +ZXCF/ZXATASP + +Bank select is just a different port and different top bit flags, whilst +the IDE lives somewhere else. + +0x4278,RWBBBBBB + +R = read ROM/writesram if set +W = write allowed +BBBBBB = bank (64 x 16K) max 1MB + +IDE is at +0xRRBF data/error/sector etc.. as expected 00BF 01BF ... + +ZXCF+2 has a fast data buffer at xx9F (for ldir) +31 WO 0 - first interface 1 - second interface (ZXCF+2 only) + +We don't specifially support the ZXCF+2 yet. + +Bank 0 is used at reset (normally holds ResiDOS) so we leave 0/1 alone. + +The IDE is more problematic due to the 16bit port (no inir/otir). We also need +to size the RAM properly because the ZXCF+ and ZXATASP had options from 128K +(not much use - would need reworking of the model to allow secondary disk swap) +512K (just fine), and 1MB (not clear what to do with half of it). + +ResiDOS is not actually stunningly useful. We simply can't afford the cost of +extra calls and magic routines and dynamic page numbering with all the bank +flipping we do as we run. We do try and co-exist on disk providing PC partition +table are in use. diff --git a/Kernel/platform-zxdiv48/buffers.c b/Kernel/platform-zxdiv48/buffers.c new file mode 100644 index 00000000..0753b81e --- /dev/null +++ b/Kernel/platform-zxdiv48/buffers.c @@ -0,0 +1,104 @@ +#include +#include +#include + +/* + * Must live in CODE3. We share this with TRS80model 1 so we ought + * to extract it for Z80. + */ + +void blktok(void *kaddr, struct blkbuf *buf, uint16_t off, uint16_t len) +{ + __builtin_memcpy(kaddr, buf->__bf_data + off, len); +} + +void blkfromk(void *kaddr, struct blkbuf *buf, uint16_t off, uint16_t len) +{ + __builtin_memcpy(buf->__bf_data + off, kaddr, len); +} + +/* + * This works because our uput and uget (see trs80-bank.s) switch + * to kernel logical bank 2 when copying, as kernel bank 1 is only + * code so it knows that any copy must be to common or bank 2. Without + * that this would need a double buffer. + */ +void blktou(void *uaddr, struct blkbuf *buf, uint16_t off, uint16_t len) +{ + uput(buf->__bf_data + off, uaddr, len); +} + +void blkfromu(void *uaddr, struct blkbuf *buf, uint16_t off, uint16_t len) +{ + uget(uaddr, buf->__bf_data + off , len); +} + +static uint8_t scratchbuf[64]; + +/* Worst case is needing to copy over about 64 bytes */ +void *blkptr(struct blkbuf *buf, uint16_t offset, uint16_t len) +{ + if (len > 64) + panic("blkptr"); + __builtin_memcpy(scratchbuf, buf->__bf_data + offset, len); + return scratchbuf; +} + +void blkzero(struct blkbuf *buf) +{ + __builtin_memset(buf->__bf_data, 0, BLKSIZE); +} + +extern uint8_t bufdata[]; + +/* This is called at start up to assign data to the first buffers, and then + again to assign data to the extra allocated buffers */ + +static bufptr bnext = bufpool; +static uint8_t *bdnext = bufdata; + +void bufsetup(void) +{ + bufptr bp; + + for(bp = bnext; bp < bufpool_end; ++bp) { + bp->__bf_data = bdnext; + bdnext += BLKSIZE; + } + bnext = bp; +} + +/* + * Scratch buffers for syscall arguments - until we can rework + * execve and realloc to avoid this need. We can in theory put + * the second tmpbuf into bank3 private space + */ + +static uint8_t tmp[2][BLKSIZE]; +static uint8_t tfree = 3; + +void tmpfree(void *p) +{ + if (p == tmp[0]) { + tfree |= 1; + return; + } + if (p == tmp[1]) { + tfree |= 2; + return; + } + panic("tmpfree"); +} + +void *tmpbuf(void) +{ + if (tfree & 1) { + tfree &= ~1; + return tmp[0]; + } + if (tfree & 2) { + tfree &= ~2; + return tmp[1]; + } + panic("tmpbuf"); +} diff --git a/Kernel/platform-zxdiv48/commonmem.s b/Kernel/platform-zxdiv48/commonmem.s new file mode 100644 index 00000000..04b60bde --- /dev/null +++ b/Kernel/platform-zxdiv48/commonmem.s @@ -0,0 +1,8 @@ +; +; Multiple app sizes and the fact the kernel and apps share the same banks +; means we need to put this somewhere low +; + .module commonmem + .area _UDATA + + .include "../cpu-z80/std-commonmem.s" diff --git a/Kernel/platform-zxdiv48/config.h b/Kernel/platform-zxdiv48/config.h new file mode 100644 index 00000000..9892f910 --- /dev/null +++ b/Kernel/platform-zxdiv48/config.h @@ -0,0 +1,85 @@ +#define CONFIG_IDE +#define CONFIG_LARGE_IO_DIRECT(x) 1 /* We support direct to user I/O */ + +/* 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) */ +#define CONFIG_PROFIL +/* Multiple processes in memory at once */ +#undef CONFIG_MULTI +#define CONFIG_SWAP_ONLY +/* Single tasking */ +#undef CONFIG_SINGLETASK +/* CP/M emulation */ +#undef CONFIG_CPM_EMU + +/* Settings for swap only mode */ +#define CONFIG_SPLIT_UDATA /* Not really but need to fix simple.c */ +#define UDATA_SIZE 0x200 +#define UDATA_BLKS 1 + +/* Input layer support */ +#define CONFIG_INPUT +#define CONFIG_INPUT_GRABMAX 3 +/* Video terminal, not a serial tty */ +#define CONFIG_VT +/* Keyboard contains non-ascii symbols */ +#define CONFIG_UNIKEY +#define CONFIG_FONT8X8 +#define CONFIG_FONT8X8SMALL + +#define CONFIG_BLKBUF_EXTERNAL + +/* Custom banking */ + +/* We have one mapping from our working of memory */ +#define MAX_MAPS 1 +#define MAP_SIZE 0x8000U + +/* Banks as reported to user space */ +#define CONFIG_BANKS 1 + +/* Vt definitions */ +#define VT_WIDTH 32 +#define VT_HEIGHT 24 +#define VT_RIGHT 31 +#define VT_BOTTOM 23 + +#define TICKSPERSEC 50 /* Ticks per second */ +#define PROGBASE 0x8000 /* also data base */ +#define PROGLOAD 0x8000 /* also data base */ +#define PROGTOP 0xFC00 /* Top of program, base of U_DATA */ +#define PROC_SIZE 32 /* Memory needed per process */ +#define MAXTICKS 10 /* As our task switch is so expensive */ + +#define BOOT_TTY (513) /* Set this to default device for stdio, stderr */ + /* In this case, the default is the first TTY device */ + +/* We need a tidier way to do this from the loader */ +#define CMDLINE NULL /* Location of root dev name */ + +/* Device parameters */ +#define NUM_DEV_TTY 1 + +#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ +#define NBUFS 7 /* Number of block buffers */ +#define NMOUNTS 2 /* Number of mounts at a time */ +#define MAX_BLKDEV 2 /* 2 IDE drives, 2 SD drive */ + +#define SWAPBASE 0x8000 +#define SWAPTOP 0xFF00UL /* FFxx is the interrupt stack */ +#define SWAP_SIZE 0x40 +/* We need to set the swaps up dynamically. In theory the counts are + 14 for DivIDE plus (512K RAM, of which 64K is kernel banks), and + 13 for ZXCF+ 512K (512K RAM 64K kernel banks 32K ResiDOS) but there + is a 1MB version that would need 29.. which is silly. Set it to 16 + which is our process count and worst case */ +#define MAX_SWAPS 14 +#define SWAPDEV 0x800 /* Device for swapping (ram special). */ + +/* All our swap is to direct mapped space */ +#define swap_map(x) ((uint8_t *)(x)) + +#define BOOTDEVICENAMES "hd#" diff --git a/Kernel/platform-zxdiv48/crt0.s b/Kernel/platform-zxdiv48/crt0.s new file mode 100644 index 00000000..e23422b0 --- /dev/null +++ b/Kernel/platform-zxdiv48/crt0.s @@ -0,0 +1,117 @@ + .module crt0 + ; + ; Our common lives low + ; + .area _CONST + .area _COMMONMEM + .area _STUBS + .area _COMMONDATA + .area _INITIALIZED + ; + ; Beyond this point we just zero. + ; + .area _DATA + .area _BSEG + .area _BSS + .area _HEAP + .area _GSINIT + .area _GSFINAL + ; + ; All our code is banked at 0x0080 (with stubs at 0-7F) + ; + .area _CODE1 + .area _CODE2 + ; We start this bank with FONT so that we have it aligned + .area _FONT + .area _CODE3 + .area _VIDEO + .area _CODE4 + + ; Discard is dumped in at 0xA000 and will be blown away later. + .area _DISCARD + ; Somewhere to throw it out of the way + .area _INITIALIZER + + ; imported symbols + .globl _fuzix_main + .globl init_early + .globl init_hardware + .globl l__DATA + .globl s__DATA + .globl kstack_top + + .globl unix_syscall_entry + .globl nmi_handler + .globl interrupt_handler + + .include "kernel.def" + .include "../kernel-z80.def" + + ; + ; startup code. Called with most stuff indeterminate but CODE1 + ; mapped. On entry A is the machine type. + ; A = 0 DivIDE plus + ; A = 1 ZXCF 512K+ (we need to do sizing later) + ; + + .area _CODE1 + + .globl _go + +_go: + + di + + ; We need to wipe the BSS but the rest of the job is done. + + ld hl, #s__DATA + ld de, #s__DATA+1 + ld bc, #l__DATA-1 + ld (hl), #0 + ldir + + ld sp, #kstack_top + + ; Configure memory map + push af + call init_early + pop af + + ; Hardware setup + push af + call init_hardware + pop af + + ; Call the C main routine + push af + call _fuzix_main + pop af + + ; main shouldn't return, but if it does... + di +stop: halt + jr stop + + ; Boot marker at 0x2200 + + .area _COMMONDATA + .globl _marker +_marker: + .byte 'Z' ; marker + .byte 'B' + .word _go ; boot addresss + + .area _STUBS +stubs: + .ds 512 + + .area _CODE3 +; +; Buffers live in segment 3 where we have a little bit of room +; + + .globl _bufdata + +_bufdata: + .ds 512 * NBUFS + diff --git a/Kernel/platform-zxdiv48/devices.c b/Kernel/platform-zxdiv48/devices.c new file mode 100644 index 00000000..76b91283 --- /dev/null +++ b/Kernel/platform-zxdiv48/devices.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct devsw dev_tab[] = /* The device driver switch table */ +{ + /* 0: /dev/hd Hard disc block devices */ + { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl }, + /* 1: /dev/fd Floppy disc block devices: nope */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 2: /dev/tty TTY devices */ + { tty_open, tty_close, tty_read, tty_write, gfx_ioctl }, + /* 3: /dev/lpr Printer devices */ + { 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 }, + /* 5: Pack to 7 with nxio if adding private devices and start at 8 */ + /* 5: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 6: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 7: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 8: DivIDE plus RAM swap: TODO */ + { no_open, no_close, no_rdwr, no_rdwr, no_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) - 1) + return false; + else + return true; +} + +void device_init(void) +{ +#ifdef CONFIG_IDE + devide_init(); +#endif +} diff --git a/Kernel/platform-zxdiv48/devrd.c b/Kernel/platform-zxdiv48/devrd.c new file mode 100644 index 00000000..289dd072 --- /dev/null +++ b/Kernel/platform-zxdiv48/devrd.c @@ -0,0 +1,56 @@ +/* + * DivIDE Plus RAM bank ramdisc driver + * + */ + +#include +#include +#include +#include + +static int rd_transfer(uint8_t is_read, uint8_t rawflag) +{ + int ct = 0; + + /* It's a disk but only for swapping (and rd_io isn't general purpose) */ + if(rawflag != 2) { + udata.u_error = EIO; + return -1; + } + + rd_wr = is_read; + rd_dptr = udata.u_dptr; + + while (ct < udata.u_nblock) { + rd_page = (udata.u_block >> 5) + 4; /* 0-3 are kernel */ + rd_addr = (udata.u_block & 31) << 9; + rd_io(); + udata.u_block++; + rd_dptr += BLKSIZE; + ct++; + } + return ct << BLKSHIFT; +} + +int rd_open(uint8_t minor, uint16_t flag) +{ + flag; + if(minor != 0) { + udata.u_error = ENODEV; + return -1; + } + return 0; +} + +int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return rd_transfer(true, rawflag); +} + +int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return rd_transfer(false, rawflag); +} + diff --git a/Kernel/platform-zxdiv48/devtty.c b/Kernel/platform-zxdiv48/devtty.c new file mode 100644 index 00000000..61003df9 --- /dev/null +++ b/Kernel/platform-zxdiv48/devtty.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char tbuf1[TTYSIZ]; + +uint8_t vtattr_cap = VTA_INVERSE|VTA_FLASH|VTA_UNDERLINE; +uint8_t vtborder; +uint8_t curattr = 7; + +static tcflag_t console_mask[4] = { + _ISYS, + _OSYS, + _CSYS, + _LSYS +}; + +tcflag_t *termios_mask[NUM_DEV_TTY + 1] = { + NULL, + console_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}, +}; + +/* tty1 is the screen */ + +/* Output for the system console (kprintf etc) */ +void kputchar(char c) +{ + if (c == '\n') + tty_putc(0, '\r'); + tty_putc(0, c); +} + +/* Both console and debug port are always ready */ +ttyready_t tty_writeready(uint8_t minor) +{ + minor; + return TTY_READY_NOW; +} + +void tty_putc(uint8_t minor, unsigned char c) +{ + minor; + vtoutput(&c, 1); +} + +int tty_carrier(uint8_t minor) +{ + minor; + return 1; +} + +void tty_setup(uint8_t minor, uint8_t flags) +{ + minor; +} + +void tty_sleeping(uint8_t minor) +{ + minor; +} + +void tty_data_consumed(uint8_t minor) +{ +} + + +/* This is used by the vt asm code, but needs to live in the kernel */ +uint16_t cursorpos; + +static struct display specdisplay = { + 0, + 256, 192, + 256, 192, + 0xFF, 0xFF, + FMT_SPECTRUM, + HW_UNACCEL, + GFX_VBLANK|GFX_MAPPABLE|GFX_TEXT, + 0 +}; + +static struct videomap specmap = { + 0, + 0, + 0x4000, + 6912, + 0, + 0, + 0, + MAP_FBMEM|MAP_FBMEM_SIMPLE +}; + +/* + * Graphics ioctls. Very minimal for this platform. It's a single fixed + * mode with direct memory mapping. + */ +int gfx_ioctl(uint8_t minor, uarg_t arg, char *ptr) +{ + if (minor != 1 || arg >> 8 != 0x03) + return vt_ioctl(minor, arg, ptr); + switch(arg) { + case GFXIOC_GETINFO: + return uput(&specdisplay, ptr, sizeof(struct display)); + case GFXIOC_MAP: + return uput(&specmap, ptr, sizeof(struct videomap)); + case GFXIOC_UNMAP: + return 0; + case GFXIOC_WAITVB: + /* Our system clock is vblank */ + timer_wait++; + psleep(&timer_interrupt); + timer_wait--; + chksigs(); + if (udata.u_cursig) { + udata.u_error = EINTR; + return -1; + } + return 0; + } + return -1; +} + +void vtattr_notify(void) +{ + /* Attribute byte fixups: not hard as the colours map directly + to the spectrum ones */ + if (vtattr & VTA_INVERSE) + curattr = ((vtink & 7) << 3) | (vtpaper & 7); + else + curattr = (vtink & 7) | ((vtpaper & 7) << 3); + if (vtattr & VTA_FLASH) + curattr |= 0x80; + /* How to map the bright bit - we go by either */ + if ((vtink | vtpaper) & 0x10) + curattr |= 0x40; +} diff --git a/Kernel/platform-zxdiv48/devtty.h b/Kernel/platform-zxdiv48/devtty.h new file mode 100644 index 00000000..40a67f5c --- /dev/null +++ b/Kernel/platform-zxdiv48/devtty.h @@ -0,0 +1,19 @@ +#ifndef __DEVTTY_DOT_H__ +#define __DEVTTY_DOT_H__ + +void tty_pollirq(void); +static void keydecode(void); + +#define KEY_ROWS 8 +#define KEY_COLS 5 +extern uint8_t keymap[8]; +extern uint8_t keyboard[8][5]; +extern uint8_t shiftkeyboard[8][5]; + +extern uint8_t timer_wait; + +extern int gfx_ioctl(uint8_t minor, uarg_t arg, char *ptr); + +extern uint8_t vtborder; + +#endif diff --git a/Kernel/platform-zxdiv48/discard.c b/Kernel/platform-zxdiv48/discard.c new file mode 100644 index 00000000..fa5a472d --- /dev/null +++ b/Kernel/platform-zxdiv48/discard.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#include + +extern uint8_t fuller, kempston, kmouse, kempston_mbmask; + +/* string.c + * Copyright (C) 1995,1996 Robert de Bath + * This file is part of the Linux-8086 C library and is distributed + * under the GNU Library General Public License. + */ +static int strcmp(const char *d, const char *s) +{ + register char *s1 = (char *) d, *s2 = (char *) s, c1, c2; + + while ((c1 = *s1++) == (c2 = *s2++) && c1); + return c1 - c2; +} + +uint8_t platform_param(char *p) +{ + if (strcmp(p, "kempston") == 0) { + kempston = 1; + return 1; + } + if (strcmp(p, "kmouse") == 0) { + kmouse = 1; + return 1; + } + if (strcmp(p, "fuller") == 0) { + fuller = 1; + return 1; + } + if (strcmp(p, "kmouse3") == 0) { + kmouse = 1; + kempston_mbmask = 7; + return 1; + } + if (strcmp(p, "kmturbo") == 0) { + /* For now rely on the turbo detect - may want to change this */ + kmouse = 1; + return 1; + } + return 0; +} + +void map_init(void) +{ + /* Banks of 32K free minus one banks worth that is the upper RAM */ + uint8_t i = (procmem >> 4) - 1; + while(i--) + swapmap_init(i); +} + +void platform_copyright(void) +{ +} diff --git a/Kernel/platform-zxdiv48/divide.c b/Kernel/platform-zxdiv48/divide.c new file mode 100644 index 00000000..ec8f974b --- /dev/null +++ b/Kernel/platform-zxdiv48/divide.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include + +/* + * Might be worth unifying with the other versions but this one + * differs a little + */ +COMMON_MEMORY + +void divide_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, #0x00A3 ; setup port number + ; and count + cp #1 + push af + call z, map_buffers + ld a,#0x05 + out (0xfe),a + inir ; transfer first 256 bytes + ld a,#0x02 + out (0xfe),a + inir ; transfer second 256 bytes + ld a,(_vtborder) + out (0xfe),a + pop af + ret nz + jp map_kernel_restore ; else map kernel then return + __endasm; +} + +void divide_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, #0x00A3 ; setup port number + ; and count + cp #1 + push af + call z, map_buffers + ld a,#0x05 + out (0xfe),a + otir ; transfer first 256 bytes + ld a,#0x02 + out (0xfe),a + otir ; transfer second 256 bytes + ld a,(_vtborder) + out (0xfe),a + pop af + ret nz + jp map_kernel_restore ; else map kernel then return + __endasm; +} diff --git a/Kernel/platform-zxdiv48/divrd.h b/Kernel/platform-zxdiv48/divrd.h new file mode 100644 index 00000000..27bb208e --- /dev/null +++ b/Kernel/platform-zxdiv48/divrd.h @@ -0,0 +1,14 @@ +/* + * RAMdisc driver + */ + +int rd_open(uint8_t minor, uint16_t flags); +int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag); + +extern uint8_t rd_wr; +extern uint8_t *rd_dptr; +extern uint8_t rd_page; +extern uint16_t rd_addr; + +void rd_io(void); diff --git a/Kernel/platform-zxdiv48/fuzix.lnk b/Kernel/platform-zxdiv48/fuzix.lnk new file mode 100644 index 00000000..91f4208d --- /dev/null +++ b/Kernel/platform-zxdiv48/fuzix.lnk @@ -0,0 +1,57 @@ +-mwxuy +-r +-i fuzix.ihx +-b _CONST=0x5B00 +-b _CODE1=0x0080 +-b _CODE2=0x0080 +-b _FONT=0x0080 +-b _CODE4=0x0080 +-b _DISCARD=0xA000 +-b _UDATA=0xFD00 +-l z80 +platform-zxdiv48/crt0.rel +platform-zxdiv48/commonmem.rel +platform-zxdiv48/zx48.rel +platform-zxdiv48/zxvideo.rel +platform-zxdiv48/main.rel +platform-zxdiv48/buffers.rel +platform-zxdiv48/discard.rel +start.rel +version.rel +lowlevel-z80-banked.rel +platform-zxdiv48/tricks.rel +simple.rel +timer.rel +kdata.rel +usermem.rel +platform-zxdiv48/devices.rel +devio.rel +filesys.rel +process.rel +inode.rel +syscall_exec16.rel +syscall_fs.rel +syscall_fs2.rel +syscall_fs3.rel +syscall_proc.rel +syscall_other.rel +tty.rel +vt.rel +font8x8.rel +mm.rel +swap.rel +devsys.rel +devinput.rel +platform-zxdiv48/devtty.rel +platform-zxdiv48/devide.rel +platform-zxdiv48/devide_discard.rel +platform-zxdiv48/ide.rel +platform-zxdiv48/divide.rel +platform-zxdiv48/zxcf.rel +platform-zxdiv48/mbr.rel +platform-zxdiv48/blkdev.rel +platform-zxdiv48/devrd.rel +platform-zxdiv48/rd_divideplus.rel +platform-zxdiv48/devinput.rel +platform-zxdiv48/zxkeyboard.rel +-e diff --git a/Kernel/platform-zxdiv48/ide.c b/Kernel/platform-zxdiv48/ide.c new file mode 100644 index 00000000..80b29f7f --- /dev/null +++ b/Kernel/platform-zxdiv48/ide.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static const uint8_t rmap_divide[8] = { + 0xA3, 0xA7, 0xAB, 0xAF, 0xB3, 0xB7, 0xBB, 0xBF +}; + +uint8_t devide_readb(uint8_t regaddr) +{ + uint16_t port = regaddr; + if (machine_type == 0) + return in(rmap_divide[regaddr]); + else + return in((port << 8) | 0xBF); +} + +void devide_writeb(uint8_t regaddr, uint8_t value) +{ + uint16_t port = regaddr; + if (machine_type == 0) + out(rmap_divide[regaddr], value); + else + out((port < 8) | 0xBF, value); +} + +void devide_read_data(void) +{ + if (machine_type == 0) + divide_read_data(); + else + zxcf_read_data(); +} + +void devide_write_data(void) +{ + if (machine_type == 0) + divide_write_data(); + else + zxcf_write_data(); +} \ No newline at end of file diff --git a/Kernel/platform-zxdiv48/kernel.def b/Kernel/platform-zxdiv48/kernel.def new file mode 100644 index 00000000..5a9f2c46 --- /dev/null +++ b/Kernel/platform-zxdiv48/kernel.def @@ -0,0 +1,14 @@ +; UZI mnemonics for memory addresses etc + +; We stick it straight after the tag +U_DATA__TOTALSIZE .equ 0x200 ; 256+256 @ 2204 + +U_DATA_STASH .equ 0xFE00 ; FE00-FFFF + +Z80_TYPE .equ 1 + +PROGBASE .equ 0x8000 +PROGLOAD .equ 0x8000 + +NBUFS .equ 7 + diff --git a/Kernel/platform-zxdiv48/loader-zxcf.s b/Kernel/platform-zxdiv48/loader-zxcf.s new file mode 100644 index 00000000..c073a307 --- /dev/null +++ b/Kernel/platform-zxdiv48/loader-zxcf.s @@ -0,0 +1,177 @@ +; +; Some juggling required as we need to vapourise our environment +; fairly early on but don't have a ton of safe working memory +; +; We don't care too much how we get loaded as we just grab control +; from whomever called us and own the system from that point. +; +; We can run this code from BASIC, from some kind of DOS loader +; etc, as a snapshot or just a tap loaded via FATware. +; + + .area _BOOT(ABS) + .org 0xE000 ; we are loaded at 0x2000 for ESX, but it + ; doesn't matter where in reality providing + ; it's not just below and overlapping E000 + +start: + di + ld sp,#0xE000 + ld hl,#0x2000 + ld de,#0xE000 + ld bc,#0x0400 + ldir + jp go +; +; We are now mapped as expected and in common high space +; +go: + ld hl,#0x4000 + ld de,#0x4001 + ld bc,#0x1AFF + ld (hl),#0 + ldir + + ; + ; Ensure the master drive is selected + ; + ld bc,#0x07BF +wait1: + in a,(c) + rla + jr c, wait1 + dec b ; devhead + ld a,#0xE0 ; master in LBA + out (c),a + nop + inc b ; back to status +wait2: + in a,(c) + and #0xC0 + cp #0x40 ; want busy off, drdy + jr nz, wait2 + + ; + ; Load sectors. We shortcut stuff here because we never + ; load over 256 sectors + ; + ; Sector 0 is the partition table in a PC style setup + ; Sectors 2048+ are the file system as a vaguely modern fdisk leaves + ; a nice big boot area which we will purloin + ; + xor a ; LBA28 high bits + ld bc,#0x04bf + out (c),a + inc b + out (c),a + + out (254),a + + ; + ; Select bank 0 + ; + ld bc,#0x10BF + ld a,#0x40 ; RAM mode bank 0, writable + out (c),a + + ld de,#0x2001 ; Load 32 sectors (0000-3FFF) + ; from sector 1 into bank 0 + ld hl,#0000 ; Starting address to load + call load_loop + + ld a,#0x01 + out (254),a + + ld a,#0x41 + out (c),a + ld d,#0x20 ; Load 32 sectors (0000-3FFF) + ld hl,#0x0000 ; into bank 1 + call load_loop + + ld a,#0x02 + out (254),a + + ld a,#0x42 + out (c),a + ld d,#0x20 ; Load 32 sectors (0000-3FFF) + ld hl,#0x0000 ; into bank 2 + call load_loop + + ld a,#0x03 + out (254),a + + ld a,#0x43 + out (c),a + ld d,#0x20 ; Load 32 sectors (0000-3FFF) + ld hl,#0x0000 ; into bank 3 + call load_loop + + ld a,#0x04 + out (254),a + + ; FIXME: for this we should just load 5B00->7FFF + ld d,#0x20 ; Load 32 sectors (4000-7FFF) + ld hl,#0x4000 ; into 4000-7FFF + call load_loop + + ; FIXME: for this we should just load A000-BFFF + ld d,#0x20 ; Load 32 sectors (8000-BFFF) + ld hl,#0x8000 ; into 4000-7FFF + call load_loop + + ld a,#0x05 + out (254),a + + ld a,#0x60 ; switch back + out (c),a + ld a,#1 ; tell the kernel this is ZXCF+ + jp 0x0080 ; and into crt0. + +load_loop: + push bc + ld a,e + inc e + ld bc,#0x03BF + out (c),a ; sector number to load + dec a + + ; Progress bar + push hl + ld h,#0x58 + ld l,a + ld (hl),#0x20 ; green square + pop hl + + dec b ; sector count register + ld a,#1 ; load one sector + out (c),a + ld b,#7 + ld a,#0x20 ; READ + out (c),a + nop +wait3: + in a, (c) + rlca + jr c, wait3 ; Busy + bit 4,a ; DRQ ? + jr z, failed ; Nope - bad + + ld b,#0x00 ; data port + xor a ; count +sector_xfer: + ini + inc b + ini + inc b + dec a + jr nz, sector_xfer + dec d + jr nz, load_loop + pop bc + ret + +failed: + xor a + out (254),a + di + halt diff --git a/Kernel/platform-zxdiv48/loader.s b/Kernel/platform-zxdiv48/loader.s new file mode 100644 index 00000000..ac35e54b --- /dev/null +++ b/Kernel/platform-zxdiv48/loader.s @@ -0,0 +1,159 @@ +; +; Some juggling required as we need to vapourise our environment +; fairly early on but don't have a ton of safe working memory +; +; We don't care too much how we get loaded as we just grab control +; from whomever called us and own the system from that point. +; +; We can run this code from BASIC, from some kind of DOS loader +; etc, as a snapshot or just a tap loaded via FATware. +; + + .area _BOOT(ABS) + .org 0xE000 ; we are loaded at 0x2000 for ESX, but it + ; doesn't matter where in reality providing + ; it's not just below and overlapping E000 + +start: + di + ld sp,#0xE000 + ld hl,#0x2000 + ld de,#0xE000 + ld bc,#0x0400 + ldir + jp go +; +; We are now mapped as expected and in common high space +; +go: + ld hl,#0x4000 + ld de,#0x4001 + ld bc,#0x1AFF + ld (hl),#0 + ldir + + ; + ; Ensure the master drive is selected + ; +wait1: + in a,(191) + rla + jr c, wait1 + ld a,#0xE0 ; master in LBA + out (187),a + nop +wait2: + in a,(191) + and #0xC0 + cp #0x40 ; want busy off, drdy + jr nz, wait2 + + ; + ; Load sectors. We shortcut stuff here because we never + ; load over 256 sectors + ; + ; Sector 0 is the partition table in a PC style setup + ; Sectors 2048+ are the file system as a vaguely modern fdisk leaves + ; a nice big boot area which we will purloin + ; + xor a ; LBA28 high bits + out (179),a + out (183),a + + out (254),a + ; + ; DivIDE plus to RAM mode (this won't work on older DivIDE) + ; + ld a,#0x60 ; RAM mode bank 0, writable + out (0x17),a + + ld de,#0x2001 ; Load 32 sectors (0000-3FFF) + ; from sector 1 into bank 0 + ld hl,#0000 ; Starting address to load + call load_loop + + ld a,#0x01 + out (254),a + + ld a,#0x61 + out (0x17),a + ld d,#0x20 ; Load 32 sectors (0000-3FFF) + ld hl,#0x0000 ; into bank 1 + call load_loop + + ld a,#0x02 + out (254),a + + ld a,#0x62 + out (0x17),a + ld d,#0x20 ; Load 32 sectors (0000-3FFF) + ld hl,#0x0000 ; into bank 2 + call load_loop + + ld a,#0x03 + out (254),a + + ld a,#0x63 + out (0x17),a + ld d,#0x20 ; Load 32 sectors (0000-3FFF) + ld hl,#0x0000 ; into bank 3 + call load_loop + + ld a,#0x04 + out (254),a + + ; FIXME: for this we should just load 5B00->7FFF + ld d,#0x20 ; Load 32 sectors (4000-7FFF) + ld hl,#0x4000 ; into 4000-7FFF + call load_loop + + ; FIXME: for this we should just load A000-BFFF + ld d,#0x20 ; Load 32 sectors (8000-BFFF) + ld hl,#0x8000 ; into 4000-7FFF + call load_loop + + ld a,#0x05 + out (254),a + + ld a,#0x60 ; switch back + out (0x17),a + xor a ; tell the kernel this is DivIDE plus mode + jp 0x0080 ; and into crt0. + +load_loop: + ld a,e + inc e + out (175),a ; sector number to load + dec a + + ; Progress bar + push hl + ld h,#0x58 + ld l,a + ld (hl),#0x20 ; green square + pop hl + + ld a,#1 ; load one sector + out (171),a + ld a,#0x20 ; READ + out (191),a + nop +wait3: + in a, (191) + rlca + jr c, wait3 ; Busy + bit 4,a ; DRQ ? + jr z, failed ; Nope - bad + ld bc,#163 ; Data port + inir + inir + dec d + jr nz, load_loop + + ret + +failed: + xor a + out (254),a + di + halt diff --git a/Kernel/platform-zxdiv48/main.c b/Kernel/platform-zxdiv48/main.c new file mode 100644 index 00000000..29b13ce0 --- /dev/null +++ b/Kernel/platform-zxdiv48/main.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include + +uint16_t ramtop = PROGTOP; + +/* On idle we spin checking for the terminals. Gives us more responsiveness + for the polled ports */ +void platform_idle(void) +{ + /* We don't want an idle poll and IRQ driven tty poll at the same moment */ + __asm + halt + __endasm; +} + +uint8_t timer_wait; + +void platform_interrupt(void) +{ + tty_pollirq(); + timer_interrupt(); + poll_input(); + if (timer_wait) + wakeup(&timer_interrupt); +} + +/* + * So that we don't suck in a library routine we can't use from + * the runtime + */ + +int strlen(const char *p) +{ + int len = 0; + while(*p++) + len++; + return len; +} + +/* + * We pack discard into the memory image is if it were just normal + * code but place it at the end after the buffers. When we finish up + * booting we turn everything from the buffer pool to the start of + * user space into buffers. + * + * We don't touch discard. Discard is just turned into user space. + */ +void platform_discard(void) +{ +} + +#ifndef SWAPDEV +/* Adding dummy swapper since it is referenced by tricks.s */ +void swapper(ptptr p) +{ + p; +} +#endif diff --git a/Kernel/platform-zxdiv48/platform_ide.h b/Kernel/platform-zxdiv48/platform_ide.h new file mode 100644 index 00000000..bf680c23 --- /dev/null +++ b/Kernel/platform-zxdiv48/platform_ide.h @@ -0,0 +1,33 @@ +/* + * DivIDE interface + * + * This is a 16bit interface with a latched data port. Each read + * from A3 fetches a word then returns low then high etc. In the other + * direction it latches then writes. + * + * The latch is reset to the first state by any other port access in the + * IDE space (so the command write sets it up nicely for us) + */ + +#define ide_select(x) +#define ide_deselect() + +#define IDE_DRIVE_COUNT 2 + +#define IDE_NONSTANDARD_XFER +#define IDE_REG_INDIRECT + +#define ide_reg_data 0 +#define ide_reg_error 1 +#define ide_reg_features 1 +#define ide_reg_sec_count 2 +#define ide_reg_lba_0 3 +#define ide_reg_lba_1 4 +#define ide_reg_lba_2 5 +#define ide_reg_lba_3 6 +#define ide_reg_devhead 6 +#define ide_reg_command 7 +#define ide_reg_status 7 + +extern void divide_read_data(void); +extern void zxcf_read_data(void); diff --git a/Kernel/platform-zxdiv48/rd_divideplus.s b/Kernel/platform-zxdiv48/rd_divideplus.s new file mode 100644 index 00000000..3fd69f92 --- /dev/null +++ b/Kernel/platform-zxdiv48/rd_divideplus.s @@ -0,0 +1,51 @@ +; +; RAM disc helpers for DivIDE Plus +; + + .area _COMMONMEM + + .globl _rd_io + + .globl _rd_wr + .globl _rd_dptr + .globl _rd_page + .globl _rd_addr + + + .globl _int_disabled + .globl map_bank_a + .globl map_kernel_restore + + +_rd_io: + di + ld a,(_rd_page) + call map_bank_a + ld hl,(_rd_addr) + ld de,(_rd_dptr) + ld bc,#512 + ld a,(_rd_wr) + or a + jr z, is_wr + ldir +cleanup: + call map_kernel_restore + ld a,(_int_disabled) + or a + ret nz + ei + ret +is_wr: + ex de,hl + ldir + jr cleanup + + +_rd_wr: + .db 0 +_rd_dptr: + .dw 0 +_rd_page: + .db 0 +_rd_addr: + .dw 0 diff --git a/Kernel/platform-zxdiv48/rules.mk b/Kernel/platform-zxdiv48/rules.mk new file mode 100644 index 00000000..d3c29bad --- /dev/null +++ b/Kernel/platform-zxdiv48/rules.mk @@ -0,0 +1,18 @@ +# +# ZX128 uses banked kernel images +# +CROSS_CCOPTS += --external-banker +# +# Tell the core code we are using the banked helpers +# +export BANKED=-banked +# +export CROSS_CC_SEG1=--codeseg CODE1 +export CROSS_CC_SEG2=--codeseg CODE2 +export CROSS_CC_SEG3=--codeseg CODE3 +export CROSS_CC_SEG4=--codeseg CODE4 +export CROSS_CC_SYS1=--codeseg CODE1 +export CROSS_CC_SYS2=--codeseg CODE2 +export CROSS_CC_SYS3=--codeseg CODE2 +export CROSS_CC_SYS4=--codeseg CODE4 +export CROSS_CC_SYS5=--codeseg CODE3 diff --git a/Kernel/platform-zxdiv48/target.mk b/Kernel/platform-zxdiv48/target.mk new file mode 100644 index 00000000..3bffcde0 --- /dev/null +++ b/Kernel/platform-zxdiv48/target.mk @@ -0,0 +1 @@ +export CPU = z80 diff --git a/Kernel/platform-zxdiv48/tricks.s b/Kernel/platform-zxdiv48/tricks.s new file mode 100644 index 00000000..5c87799f --- /dev/null +++ b/Kernel/platform-zxdiv48/tricks.s @@ -0,0 +1,3 @@ + .include "kernel.def" + .include "../kernel-z80.def" + .include "../lib/z80single-banked.s" diff --git a/Kernel/platform-zxdiv48/zx48.h b/Kernel/platform-zxdiv48/zx48.h new file mode 100644 index 00000000..49a69279 --- /dev/null +++ b/Kernel/platform-zxdiv48/zx48.h @@ -0,0 +1 @@ +extern uint8_t machine_type; diff --git a/Kernel/platform-zxdiv48/zx48.s b/Kernel/platform-zxdiv48/zx48.s new file mode 100644 index 00000000..6763d9ab --- /dev/null +++ b/Kernel/platform-zxdiv48/zx48.s @@ -0,0 +1,719 @@ +; +; ZX Spectrum 48K with DivIDE plus hardware support +; + + .module zx48 + + ; exported symbols + .globl init_early + .globl init_hardware + .globl _program_vectors + .globl platform_interrupt_all + .globl interrupt_handler + .globl unix_syscall_entry + .globl null_handler + + .globl map_kernel + .globl map_process_always + .globl map_process + .globl map_kernel_di + .globl map_process_always_di + .globl map_save_kernel + .globl map_restore + .globl map_process_save + .globl map_kernel_restore + .globl map_buffers + .globl map_bank_a + .globl current_map + .globl switch_bank + + .globl _need_resched + .globl _int_disabled + + .globl _machine_type + .globl mapport + .globl mapmod + ; exported debugging tools + .globl _platform_monitor + .globl _platform_reboot + .globl outchar + + ; imported symbols + .globl _ramsize + .globl _procmem + + .globl _vtoutput + .globl _vtinit + + .globl outcharhex + .globl outhl, outde, outbc + .globl outnewline + .globl outstring + .globl outstringhex + + ; banking support + .globl __bank_0_1 + .globl __bank_0_2 + .globl __bank_0_3 + .globl __bank_0_4 + .globl __bank_1_2 + .globl __bank_1_3 + .globl __bank_1_4 + .globl __bank_2_1 + .globl __bank_2_3 + .globl __bank_2_4 + .globl __bank_3_1 + .globl __bank_3_2 + .globl __bank_3_4 + .globl __bank_4_1 + .globl __bank_4_2 + .globl __bank_4_3 + + .globl __stub_0_1 + .globl __stub_0_2 + .globl __stub_0_3 + .globl __stub_0_4 + .globl __stub_1_2 + .globl __stub_1_3 + .globl __stub_1_4 + .globl __stub_2_1 + .globl __stub_2_3 + .globl __stub_2_4 + .globl __stub_3_1 + .globl __stub_3_2 + .globl __stub_3_4 + .globl __stub_4_1 + .globl __stub_4_2 + .globl __stub_4_3 + + .include "kernel.def" + .include "../kernel-z80.def" + +; ----------------------------------------------------------------------------- +; COMMON MEMORY BANK (below 0xC000) +; ----------------------------------------------------------------------------- + .area _COMMONMEM + +_platform_monitor: + ; + ; Not so much a monitor as wait for space + ; + ld a, #0x7F + in a, (0xFE) + rra + jr c, _platform_monitor + +_platform_reboot: + di + im 1 + ; This sequence triggers the DivIDE Plus reset mechanism + ld a,(_machine_type) + or a + jr nz, reboot_zxcf + xor a + ld i,a + ld a,#0xC0 + out (0xE3),a + halt +reboot_zxcf: + di + halt + +platform_interrupt_all: + ret + + .area _COMMONDATA + +_int_disabled: + .db 1 +_machine_type: + .db 1 +mapmod: + .dw 0x00E0 ; for DivIDE Plus +mapport: + .dw 0xFF17 ; for DivIDE plus + +; ----------------------------------------------------------------------------- +; KERNEL MEMORY BANK (above 0xC000, only accessible when the kernel is mapped) +; ----------------------------------------------------------------------------- + .area _CODE1 + +; +; The memory banker will deal with the map setting +; +init_early: + ld (_machine_type),a + or a + ret z + ; Switch to mappings for ZXCF + ld bc,#0x10BF + ld (mapport),bc + ld bc,#0x0240 ; or with 0x40 move on by 2 + ld (mapmod),bc + ret + + .area _VIDEO + +init_hardware: + ; set system RAM size + ld hl, #560 + ld a,(_machine_type) + or a + jr nz, inith2 + ld hl,#528 ; we lose bits to ResiDOS +inith2: + ld (_ramsize), hl + ld hl, #(560 - 80) ; 4 x 16K banked + 16K go to the OS/screen + ld (_procmem), hl + + ; screen initialization + push af + call _vtinit + pop af + + ; Vectors + + ld a,#4 +vecloop: + dec a + push af + call map_bank_a + call vectors + call map_kernel_restore + pop af + jr nz, vecloop + ret + +; What's our vector, Victor ? + +vectors: + ld a,#0xC3 + ld (0),a + ld hl,#null_handler + ld (1),hl + ld (0x30),a + ld hl,#unix_syscall_entry + ld (0x31),a + ld (0x38),a + ld hl,#interrupt_handler + ld (0x39),hl + ret + +;------------------------------------------------------------------------------ +; COMMON MEMORY PROCEDURES FOLLOW + + .area _COMMONMEM + +_program_vectors: + ret + + ; Swap helper. Map the page in A into the address space such + ; that swap_map() gave the correct pointer to use. Undone by + ; a map_kernel_{restore} + +map_buffers: + ld a,#2 +map_bank_a: + ; bank switching procedure. On entrance: + ; A - bank number to set + push af + ld a, (current_map) + ld (ksave_map), a + pop af + ; Then fall through to set the bank up + +switch_bank: + push bc + ; Write the store first, that way any interrupt will restore + ; the new bank and our out will just be a no-op + ld (current_map), a + ld bc,(mapmod) ; 0x60 for DivIDE plus 0x40 for ZXCF+ + add b ; have to avoid bank 0 on ZXCF+ (residos) + or c + ld bc,(mapport) ; 0x17 for DivIDE plus 0x4278 for ZXCF+ + out (c), a + pop bc + ret + +map_process: + ld a, h + or l + jr z, map_kernel_nosavea + push af + ld a, (hl) + call switch_bank + pop af + ret +; +; We always save here so that existing code works until we have a +; clear usage of save/restore forms across the kernel +; +map_process_save: +map_process_always: +map_process_always_di: + push af + ld a, (current_map) + ld (ksave_map), a + pop af + ret + +; +; Save and switch to kernel +; +map_save_kernel: + push af + ld a, (current_map) + ld (map_store), a + pop af +; +; This may look odd. However the kernel is banked so any +; invocation of kernel code in fact runs common code and the +; common code will bank in the right kernel bits for us when it calls +; out of common into banked code. We do a restore to handle all the +; callers who do map_process_always/map_kernel pairs. Probably we +; should have some global change to map_process_save/map_kernel_restore +; +map_kernel_di: +map_kernel: +map_kernel_nosavea: ; to avoid double reg A saving +map_kernel_restore: + push af + ld a, (ksave_map) + call switch_bank + pop af + ret + +map_restore: + push af + ld a, (map_store) + call switch_bank + pop af + ret + +; +; We have no easy serial debug output instead just breakpoint this +; address when debugging. +; +outchar: + ld (_tmpout), a + push bc + push de + push hl + push ix + ld hl, #1 + push hl + ld hl, #_tmpout + push hl + push af + call _vtoutput + pop af + pop af + pop af + pop ix + pop hl + pop de + pop bc + ret + + .area _COMMONDATA +_tmpout: + .db 1 + +current_map: ; place to store current page number. Is needed + .db 0 ; because we have no ability to read 7ffd port + ; to detect what page is mapped currently +map_store: + .db 0 + +ksave_map: + .db 0 + +_need_resched: + .db 0 + + .area _COMMONMEM +; +; Banking helpers +; +; Logical Physical +; 0 COMMON (0x4000) +; 1 0 +; 2 1 +; 3 2 +; +; +__bank_0_1: + xor a ; switch to physical bank 0 (logical 1) +bankina0: + ; + ; Get the target address first, otherwise we will change + ; bank and read it from the wrong spot! + ; + pop hl ; Return address (points to true function address) + ld e, (hl) ; DE = function to call + inc hl + ld d, (hl) + inc hl + push hl ; Restore corrected return pointer + ld bc, (current_map) ; get current bank into B + call switch_bank ; Move to new bank + ; figure out which bank to map on the return path + ld a, c + or a + jr z, __retmap1 + dec a + jr z, __retmap2 + jr __retmap3 + +callhl: jp (hl) +__bank_0_2: + ld a, #1 ; logical 2 -> physical 1 + jr bankina0 +__bank_0_3: + ld a, #2 ; logical 3 -> physical 2 + jr bankina0 +__bank_0_4: + ld a, #3 ; logical 4 -> physical 3 + jr bankina0 + +__bank_1_2: + ld a, #1 +bankina1: + pop hl ; Return address (points to true function address) + ld e, (hl) ; DE = function to call + inc hl + ld d, (hl) + inc hl + push hl ; Restore corrected return pointer + call switch_bank ; Move to new bank +__retmap1: + ex de, hl + call callhl ; call the function + xor a ; return to bank 1 (physical 0) + jp switch_bank +__bank_1_3: + ld a, #2 + jr bankina1 +__bank_1_4: + ld a, #3 + jr bankina1 +__bank_2_1: + xor a +bankina2: + pop hl ; Return address (points to true function address) + ld e, (hl) ; DE = function to call + inc hl + ld d, (hl) + inc hl + push hl ; Restore corrected return pointer + call switch_bank ; Move to new bank +__retmap2: + ex de, hl + call callhl ; call the function + ld a, #1 ; return to bank 2 + jp switch_bank +__bank_2_3: + ld a, #2 + jr bankina2 +__bank_2_4: + ld a, #3 + jr bankina2 +__bank_3_1: + xor a +bankina3: + pop hl ; Return address (points to true function address) + ld e, (hl) ; DE = function to call + inc hl + ld d, (hl) + inc hl + push hl ; Restore corrected return pointer + call switch_bank ; Move to new bank +__retmap3: + ex de, hl + call callhl ; call the function + ld a, #2 ; return to bank 3 + jp switch_bank +__bank_3_2: + ld a, #1 + jr bankina3 +__bank_3_4: + ld a, #3 + jr bankina3 + +__bank_4_1: + xor a +bankina4: + pop hl ; Return address (points to true function address) + ld e, (hl) ; DE = function to call + inc hl + ld d, (hl) + inc hl + push hl ; Restore corrected return pointer + call switch_bank ; Move to new bank +__retmap4: + ex de, hl + call callhl ; call the function + ld a, #3 ; return to bank 4 + jp switch_bank +__bank_4_2: + ld a, #1 + jr bankina3 +__bank_4_3: + ld a, #2 + jr bankina3 + +; +; Stubs need some stack munging and use DE +; + +__stub_0_1: + xor a +__stub_0_a: + pop hl ; the return + ex (sp), hl ; write it over the discard + ld bc, (current_map) + call switch_bank + ld a, c + or a + jr z, __stub_1_ret + dec a + jr z, __stub_2_ret + jr __stub_3_ret +__stub_0_2: + ld a, #1 + jr __stub_0_a +__stub_0_3: + ld a, #2 + jr __stub_0_a +__stub_0_4: + ld a, #3 + jr __stub_0_a + +__stub_1_2: + ld a, #1 +__stub_1_a: + pop hl ; the return + ex (sp), hl ; write it over the discad + call switch_bank +__stub_1_ret: + ex de, hl + call callhl + xor a + call switch_bank + pop de + push de ; dummy the caller will discard + push de ; FIXME don't we need to use BC and can't we get + ret ; rid of all non 0_x stubs ? +__stub_1_3: + ld a, #2 + jr __stub_1_a +__stub_1_4: + ld a, #4 + jr __stub_1_a + +__stub_2_1: + xor a +__stub_2_a: + pop hl ; the return + ex (sp), hl ; write it over the discad + call switch_bank +__stub_2_ret: + ex de, hl ; DE is our target + call callhl + ld a,#1 + call switch_bank + pop de + push de ; dummy the caller will discard + push de + ret +__stub_2_3: + ld a, #2 + jr __stub_2_a +__stub_2_4: + ld a, #3 + jr __stub_2_a + +__stub_3_1: + xor a +__stub_3_a: + pop hl ; the return + ex (sp), hl ; write it over the discad + call switch_bank +__stub_3_ret: + ex de, hl + call callhl + ld a,#2 + call switch_bank + pop de + push de ; dummy the caller will discard + push de + ret +__stub_3_2: + ld a, #1 + jr __stub_3_a +__stub_3_4: + ld a, #3 + jr __stub_3_a + +__stub_4_1: + xor a +__stub_4_a: + pop hl ; the return + ex (sp), hl ; write it over the discad + call switch_bank +__stub_4_ret: + ex de, hl + call callhl + ld a,#3 + call switch_bank + pop de + push de ; dummy the caller will discard + push de + ret +__stub_4_2: + ld a, #1 + jr __stub_4_a +__stub_4_3: + ld a, #2 + jr __stub_4_a + +; +; We know that we only ever copy between the user space and 4000-7FFF +; or the buffers (0000-3FFF in our bank). So if we put our copiers in +; the right bank and not common space we can just ldir stuff about +; which is far simpler +; + ; exported symbols + .globl __uget + .globl __ugetc + .globl __ugetw + + .globl outcharhex + .globl outhl + + .globl __uput + .globl __uputc + .globl __uputw + .globl __uzero + + .globl map_process_always + .globl map_kernel +; +; We need these in _CODE3 so they are in the right bank to copy disk +; cache buffers +; + .area _CODE3 + +; +; The basic operations are copied from the standard one. Only the +; blk transfers are different. uputget is a bit different as we are +; not doing 8bit loop pairs. +; +uputget: + ; load DE with the byte count + ld c, 8(ix) ; byte count + ld b, 9(ix) + ld a, b + or c + ret z ; no work + ; load HL with the source address + ld l, 4(ix) ; src address + ld h, 5(ix) + ; load DE with destination address (in userspace) + ld e, 6(ix) + ld d, 7(ix) + ret ; Z is still false + +__uputc: + pop bc ; return + pop de ; char + pop hl ; dest + push hl + push de + push bc + call map_process_always + ld (hl), e +uputc_out: + jp map_kernel ; map the kernel back below common + +__uputw: + pop bc ; return + pop de ; word + pop hl ; dest + push hl + push de + push bc + call map_process_always + ld (hl), e + inc hl + ld (hl), d + jp map_kernel + +__ugetc: + pop bc ; return + pop hl ; address + push hl + push bc + call map_process_always + ld l, (hl) + ld h, #0 + jp map_kernel + +__ugetw: + pop bc ; return + pop hl ; address + push hl + push bc + call map_process_always + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + jp map_kernel + +__uput: + push ix + ld ix, #0 + add ix, sp + call uputget ; source in HL dest in DE, count in BC + jr z, uput_out ; but count is at this point magic + call map_process_always + ldir +uput_out: + call map_kernel + pop ix + ld hl, #0 + ret + +__uget: + push ix + ld ix, #0 + add ix, sp + call uputget ; source in HL dest in DE, count in BC + jr z, uput_out ; but count is at this point magic + call map_process_always + ldir + jr uput_out + +; +__uzero: + pop de ; return + pop hl ; address + pop bc ; size + push bc + push hl + push de + ld a, b ; check for 0 copy + or c + ret z + call map_process_always + ld (hl), #0 + dec bc + ld a, b + or c + jp z, uputc_out + ld e, l + ld d, h + inc de + ldir + jp uputc_out diff --git a/Kernel/platform-zxdiv48/zxcf.c b/Kernel/platform-zxdiv48/zxcf.c new file mode 100644 index 00000000..e50ada74 --- /dev/null +++ b/Kernel/platform-zxdiv48/zxcf.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include +#include + +/* + * Might be worth unifying with the other versions but this one + * differs a little + */ +COMMON_MEMORY + +void zxcf_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, #0x00BF ; setup port number + ; and count + cp #1 + push af + call z, map_buffers + ld a,#0x05 + out (0xfe),a + ; The ZXCF design is sucky as we have a 16bit port decode + ld a,#0x40 + +next4: + ini + inc b + ini + inc b + ini + inc b + ini + inc b + dec a + jr nz, next4 + ld a,#0x02 + out (0xfe),a + ld a,#0x40 + +next4_2: + ini + inc b + ini + inc b + ini + inc b + ini + inc b + dec a + jr nz, next4_2 + ld a,(_vtborder) + out (0xfe),a + pop af + ret nz + jp map_kernel_restore ; else map kernel then return + __endasm; +} + +void zxcf_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, #0x00BF ; setup port number + ; and count + cp #1 + push af + call z, map_buffers + ld a,#0x05 + out (0xfe),a + ; The ZXCF design is sucky as we have a 16bit port decode + ld a,#0x40 + +w_next4: + outi + inc b + outi + inc b + outi + inc b + outi + inc b + dec a + jr nz, w_next4 + ld a,#0x02 + out (0xfe),a + ld a,#0x40 + +w_next4_2: + outi + inc b + outi + inc b + outi + inc b + outi + inc b + dec a + jr nz, w_next4_2 + ld a,(_vtborder) + out (0xfe),a + pop af + ret nz + jp map_kernel_restore ; else map kernel then return + __endasm; +} diff --git a/Kernel/platform-zxdiv48/zxvideo.s b/Kernel/platform-zxdiv48/zxvideo.s new file mode 100644 index 00000000..950cb76c --- /dev/null +++ b/Kernel/platform-zxdiv48/zxvideo.s @@ -0,0 +1,28 @@ +; +; zx128 vt primitives +; + + .module zxvideo + + ; exported symbols + .globl _plot_char + .globl _scroll_down + .globl _scroll_up + .globl _cursor_on + .globl _cursor_off + .globl _cursor_disable + .globl _clear_lines + .globl _clear_across + .globl _do_beep + .globl _fontdata_8x8 + .globl _curattr + .globl _vtattr + + ; Build the video library as the only driver + +ZXVID_ONLY .equ 1 + + .area _VIDEO + + .include "../dev/zx/video-banked.s" +