From: Alan Cox Date: Sat, 28 Feb 2015 14:04:06 +0000 (+0000) Subject: plus3: Initial files for spectrum +3 X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=db8800168d765e403f94679dc23acf8aa39eb615;p=FUZIX.git plus3: Initial files for spectrum +3 This is just development in progress to make it easier to see how to split the 128 and the plus 3 features. --- diff --git a/Kernel/Makefile b/Kernel/Makefile index a59aaee4..77ecfc37 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -1,4 +1,4 @@ -TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-z80pack32 platform-dragon platform-tgl6502 +TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-z80pack32 platform-dragon platform-tgl6502 platform-plus3 #export TARGET = 8086test #export TARGET = atarist @@ -11,13 +11,14 @@ TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 #export TARGET = nc100 #export TARGET = p112 #export TARGET = pcw8256 +export TARGET = plus3 #export TARGET = px4plus #export TARGET = tgl6502 #export TARGET = trs80 #export TARGET = ubee #export TARGET = z80pack #export TARGET = z80pack-lite -export TARGET= zx128 +#export TARGET= zx128 export VERSION = "0.1" export SUBVERSION = "ac1" diff --git a/Kernel/platform-plus3/README b/Kernel/platform-plus3/README new file mode 100644 index 00000000..c96f0a1d --- /dev/null +++ b/Kernel/platform-plus3/README @@ -0,0 +1,29 @@ +An FUZIX target for ZX Spectrum +2A/+3 + +The +2A and +3 have the following choice of memory configurations on top of +the standard 128K spectrum + + 00 40 80 C0 +conf0 [0] [1] [2] [3] +conf1 [4] [5] [6] [7] +conf2 [4] [5] [6] [3] +conf3 [4] [7] [6] [3] + + +That gives us a conventional low 0/1 and 4/5 for user space with the kernel +using 2/3/6/7. + +Kernel maps are then + +3 = common (always mapped high) +2/6 = banked (at 0x8000) +7 = banked with screen (at 0x4000) + +although 7 does not appear to be part of the banks we cannot map 2 and 7 +together so it's effectively banked. + +Alternatively we could go with a single 64K swapping user space with +kernel mapped normally at 4-7 (with screen hole at C000) and user at 0-3 +and a small copied common in bank 3 and 7 + + diff --git a/Kernel/platform-plus3/commonmem.s b/Kernel/platform-plus3/commonmem.s new file mode 100644 index 00000000..dc31c24e --- /dev/null +++ b/Kernel/platform-plus3/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 _COMMONDATA + + .include "../cpu-z80/std-commonmem.s" diff --git a/Kernel/platform-plus3/config.h b/Kernel/platform-plus3/config.h new file mode 100644 index 00000000..51b72539 --- /dev/null +++ b/Kernel/platform-plus3/config.h @@ -0,0 +1,69 @@ +/* Simple IDE interface */ +#define CONFIG_IDE +#define IDE_REG_DATA 0xA3 +#define IDE_REG_ERROR 0xA7 +#define IDE_REG_FEATURES 0xA7 +#define IDE_REG_SEC_COUNT 0xAB +#define IDE_REG_LBA_0 0xAF +#define IDE_REG_LBA_1 0xB3 +#define IDE_REG_LBA_2 0xB7 +#define IDE_REG_LBA_3 0xBB +#define IDE_REG_DEVHEAD 0xBB +#define IDE_REG_STATUS 0xBF +#define IDE_REG_COMMAND 0xBF + +/* 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 */ +#define CONFIG_MULTI +/* Single tasking */ +#undef CONFIG_SINGLETASK +/* Swap only : swap via IDE or banked low RAM or similar */ +#define CONFIG_SWAP_ONLY +/* Banks as reported to user space */ +#define CONFIG_BANKS 1 + +/* Keyboard contains not ascii symbols */ +#define CONFIG_UNIKEY + +/* Video terminal, not a serial tty */ +#define CONFIG_VT +/* 8x8 font for the moment */ +#define CONFIG_FONT8X8 +/* 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 0x0000 /* also data base */ +#define PROGLOAD 0x0100 /* also data base */ +#define PROGTOP 0xF800 /* Top of program */ +#define PROC_SIZE 64 /* Memory needed per process */ + +#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 SWAPDEV (swap_dev) /* Swap device (dynamic) */ +#define SWAP_SIZE 0x7C /* 64K minus the common in blocks */ +#define SWAPBASE 0x0000 /* We swap the lot in one, include the */ +#define SWAPTOP 0xF800 /* vectors so its a round number of sectors */ +#define MAX_SWAPS 64 + + +#define NBUFS 9 /* Number of block buffers */ +#define NMOUNTS 4 /* Number of mounts at a time */ +#define MAX_BLKDEV 2 /* 2 IDE drives, 1 SD drive */ diff --git a/Kernel/platform-plus3/crt0.s b/Kernel/platform-plus3/crt0.s new file mode 100644 index 00000000..f1025b97 --- /dev/null +++ b/Kernel/platform-plus3/crt0.s @@ -0,0 +1,103 @@ + .module crt0 + + .module crt0 + + ; + ; Bank 4-7 + ; + .area _CODE + .area _CODE2 + .area _CODE3 + .area _CONST + .area _VIDEO + .area _DATA + .area _INITIALIZED + .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 _INITIALIZER + .area _GSINIT + .area _GSFINAL + .area _DISCARD + .area _FONT + ; + ; Above 0xC000 in the top of bank 3 + ; + .area _COMMONMEM + .area _COMMONDATA + + ; imported symbols + .globl _fuzix_main + .globl init_early + .globl init_hardware + .globl _sysconfig + .globl s__INITIALIZER + .globl s__DATA + .globl l__DATA + .globl s__FONT + .globl l__FONT + .globl s__DISCARD + .globl l__DISCARD + .globl s__COMMONMEM + .globl l__COMMONMEM + + .globl kstack_top + + ; startup code + .area _CODE + + .include "kernel.def" + +; +; The bootloader has executed and now enters our code. HL points +; to a table of properties extracted from the 3DOS and BASIC +; environment. Do the memory shuffle and get going +; +init1: + di + ld sp, #kstack_top + push hl + + ; Configure memory map + call init_early + + ; move the common memory where it belongs + ld hl, #s__INITIALIZER + ld de, #s__COMMONMEM + ld bc, #l__COMMONMEM + ldir + ; font + ld de, #s__FONT + ld bc, #l__FONT + ldir + ; and the discard + ld de, #s__DISCARD + ld bc, #l__DISCARD + ldir + ; then zero the data area + ld hl, #s__DATA + ld de, #s__DATA + 1 + ld bc, #l__DATA - 1 + ld (hl), #0 + ldir + + pop hl + ld (_sysconfig), hl ; We keep this safe until we wiped data + + ; Hardware setup + call init_hardware + + ; Call the C main routine + call _fuzix_main + + ; main shouldn't return, but if it does... + di +stop: halt + jr stop + + + .area _DATA +_sysconfig: + .word 0 diff --git a/Kernel/platform-plus3/devfd.c b/Kernel/platform-plus3/devfd.c new file mode 100644 index 00000000..3fe79082 --- /dev/null +++ b/Kernel/platform-plus3/devfd.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include + +#define MAX_FD 2 + +extern uint8_t fdc_user; +extern uint16_t fdc_addr; +extern uint8_t rwcmd[9]; +extern uint8_t seekcmd[3]; + +extern void fdc_motoron(void); +extern void fdc_motoroff(void); +extern uint8_t fdc_read(void); +extern uint8_t fdc_write(void); +extern uint8_t fdc_recal(void); +extern uint8_t fdc_seek(void); + +uint8_t track[MAX_FD] = { 0xFF, 0xFF }; + +static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag) +{ + blkno_t block; + uint16_t dptr; + int ct = 0; + int tries; + uint8_t err = 0; + uint8_t nblock; + + if(rawflag == 2) + goto bad2; + + if (rawflag == 0) { + dptr = (uint16_t)udata.u_buf->bf_data; + block = udata.u_buf->bf_blk; + nblock = 1; + } else { + if (((uint16_t)udata.u_offset|udata.u_count) & BLKMASK) + goto bad2; + dptr = (uint16_t)udata.u_base; + block = udata.u_offset >> 9; + nblock = udata.u_count >> 9; + } + + rwcmd[1] = 1 << minor; + rwcmd[2] = block / 9; + rwcmd[3] = 0; /* Single sided only for now */ + rwcmd[4] = 1 + block % 9; /* Sector */ + rwcmd[6] = rwcmd[4]; + + fdc_user = rawflag; + + while (ct < nblock) { + fdc_addr = dptr; + if (track[minor] != rwcmd[2]) { + seekcmd[1] = 1 << minor; + seekcmd[2] = rwcmd[2]; + fdc_seek(); + } + for (tries = 0; tries < 4 ; tries++) { + /* FIXME: need to return status properly and mask it */ + if (is_read) + err = fdc_read(); + else + err = fdc_write(); + if (err == 0) + break; + } + if (tries > 1) /* FIXME: set drive */ + fdc_recal(); + if (tries == 4) + goto bad; + + dptr += 0x200; /* Move on 512 bytes in the buffer */ + + rwcmd[4]++; + /* Step a track */ + if (rwcmd[4] > 10) { + rwcmd[4] = 1; + rwcmd[2]++; + } + rwcmd[6] = rwcmd[4]; + ct++; + } + return 1; +bad: + kprintf("fd%d: error %x\n", minor, err); +bad2: + udata.u_error = EIO; + return -1; +} + +int fd_open(uint8_t minor, uint16_t flag) +{ + flag; + /* Check we have a floppy */ + if(!(sysconfig & CONF_PLUS3) || minor >= MAX_FD) { + udata.u_error = ENODEV; + return -1; + } + return 0; +} + +int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag; + return fd_transfer(minor, true, rawflag); +} + +int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag; + return fd_transfer(minor, false, rawflag); +} diff --git a/Kernel/platform-plus3/devfd.h b/Kernel/platform-plus3/devfd.h new file mode 100644 index 00000000..50f92831 --- /dev/null +++ b/Kernel/platform-plus3/devfd.h @@ -0,0 +1,13 @@ +#ifndef __DEVFD_DOT_H__ +#define __DEVFD_DOT_H__ + +/* public interface */ +int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag); +int fd_open(uint8_t minor, uint16_t flag); + +/* low level interface */ +uint16_t fd_reset(uint8_t *driveptr); +uint16_t fd_operation(uint8_t *driveptr); + +#endif /* __DEVFD_DOT_H__ */ diff --git a/Kernel/platform-plus3/devices.c b/Kernel/platform-plus3/devices.c new file mode 100644 index 00000000..51e43f36 --- /dev/null +++ b/Kernel/platform-plus3/devices.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct devsw dev_tab[] = /* The device driver switch table */ +{ + /* 0: /dev/fd Floppy disc block devices: disciple */ + { fd_open, no_close, fd_read, fd_write, no_ioctl }, +#ifdef CONFIG_IDE + /* 1: /dev/hd Hard disc block devices */ + { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl }, +#else + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, +#endif + /* 2: /dev/tty TTY devices */ + { tty_open, tty_close, tty_read, tty_write, vt_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 } +}; + +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; +} + +void device_init(void) +{ +#ifdef CONFIG_IDE + devide_init(); +#endif +} diff --git a/Kernel/platform-plus3/devtty.c b/Kernel/platform-plus3/devtty.c new file mode 100644 index 00000000..d50893d6 --- /dev/null +++ b/Kernel/platform-plus3/devtty.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +char tbuf1[TTYSIZ]; + +/* buffer for port scan procedure */ +uint8_t keybuf[8]; +/* Previous state */ +uint8_t keymap[8]; + +static uint8_t keybyte, keybit; +static uint8_t newkey; +static int keysdown = 0; + +uint8_t keyboard[8][5] = { + {' ', 0 , 'm', 'n', 'b'}, + {13 , 'l', 'k', 'j', 'h'}, + {'p', 'o', 'i', 'u', 'y'}, + {'0', '9', '8', '7', '6'}, + {'1', '2', '3', '4', '5'}, + {'q', 'w', 'e', 'r', 't'}, + {'a', 's', 'd', 'f', 'g'}, + {0 , 'z', 'x', 'c', 'v'} +}; + +/* SYMBOL SHIFT MODE */ +uint8_t shiftkeyboard[8][5] = { + {' ', 0 , '.', ',', '*'}, + {13 , '=', '+', '-', '^'}, + {'"', ';', '@', ']', '['}, + {'_', ')', '(', '\'','&'}, + {'!', '@', '#', '$', '%'}, + {'`', 0 , 0 , '<', '>'}, + {'~' ,'|', '\\','{', '}'}, + {0 , ':', KEY_POUND , '?', '/'} +}; + + +static uint8_t shiftmask[8] = { 0x02, 0, 0, 0, 0, 0, 0, 0x01 }; + +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) +{ + minor; +} + +void tty_sleeping(uint8_t minor) +{ + minor; +} + +void update_keyboard(void) +{ + /* We need this assembler code because SDCC __sfr cannot handle 16-bit addresses. And because it is much faster, of course */ + /* TODO: make it naked? */ + __asm + ld hl,#_keybuf + ld c, #0xFE + ld b, #0x7f + ld e, #8 ; 8 keyboard ports, 7FFE, BFFE, DFFE and so on + read_halfrow: + in a, (c) +; and #0 + cpl + ld(hl), a + rrc b + inc hl + dec e + jr nz, read_halfrow + __endasm; +} + +void tty_pollirq(void) +{ + int i; + + update_keyboard(); + + newkey = 0; + + for (i = 0; i < 8; i++) { + int n; + uint8_t key = keybuf[i] ^ keymap[i]; + if (key) { + uint8_t m = 0x10; + for (n = 4; n >= 0; n--) { + if ((key & m) && (keymap[i] & m)) + if (!(shiftmask[i] & m)) + keysdown--; + + if ((key & m) && !(keymap[i] & m)) { + if (!(shiftmask[i] & m)) + keysdown++; + keybyte = i; + keybit = n; + newkey = 1; + } + m >>= 1; + } + } + keymap[i] = keybuf[i]; + } + + if (keysdown < 3 && newkey) + keydecode(); +} + +static uint8_t cursor[4] = { KEY_LEFT, KEY_DOWN, KEY_UP, KEY_RIGHT }; + +static void keydecode(void) +{ + uint8_t c; + + uint8_t ss = keymap[0] & 0x02; /* SYMBOL SHIFT */ + uint8_t cs = keymap[7] & 0x01; /* CAPS SHIFT */ + + if (ss) { + c = shiftkeyboard[keybyte][keybit]; + } else { + c = keyboard[keybyte][keybit]; + if (cs) { + if (c >= 'a' && c <= 'z') + c -= 'a' - 'A'; + else if (c == '0') /* CS + 0 is backspace) */ + c = 0x08; + else if (c == ' ') + c = KEY_STOP; /* ^C map for BREAK */ + else if (c >= '5' && c <= '8') + c = cursor[c - '5']; + } + } + + + if (c != 0) + tty_inproc(1, c); +} + + +/* This is used by the vt asm code, but needs to live in the kernel */ +uint16_t cursorpos; diff --git a/Kernel/platform-plus3/devtty.h b/Kernel/platform-plus3/devtty.h new file mode 100644 index 00000000..039589fd --- /dev/null +++ b/Kernel/platform-plus3/devtty.h @@ -0,0 +1,13 @@ +#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]; + +#endif diff --git a/Kernel/platform-plus3/floppy.s b/Kernel/platform-plus3/floppy.s new file mode 100644 index 00000000..d367d8b1 --- /dev/null +++ b/Kernel/platform-plus3/floppy.s @@ -0,0 +1,302 @@ +; +; ROM timings are used for the drives. +; +; 0x0A, motor on +; 0x32, motor off +; 0xAF, write off +; 0x1E, head settle ms +; 0x0C, step rate ms +; 0x0F, head unload +; 0x03, head load x 2 + 1 +; +; + .area _COMMONMEM + + .globl _fdc_motoron, _fdc_motoroff + .globl _fdc_read, _fdc_write, _fdc_recal, _fdc_seek + .globl _fdc_user, _fdc_addr + .globl _fdc_statbuf + .globl _seekcmd + .globl _rwcmd + .globl _recalcmd + .globl port_map + .globl map_kernel, map_process_always + +fdc_cmd: + dec b + jr z, fdclast +fdc_cmdl: + call fdc_outcmd + inc hl + ret c ; Error + djnz fdc_cmdl +fdclast: + di +fdc_outcmd: + push bc + ld bc, #0x2ffd + ld e, (hl) +fdc_outcmdw: ; Wait non busy + in a, (c) + add a, a + jr c, fdc_outcmdw + add a, a ; FIXME: can we use ret m or similar here ? + ret c ; Failed + ld b, #0x3F + out (c), e ; Output the byte + ex (sp), hl + ex (sp), hl ; Nap + pop bc + ret ; NC + +fdc_cmdq: ; Quick (non data) command + call fdc_outcmd + djnz fdc_cmdq + ret + +fdc_status: + ld bc, #0x2ffd + ld hl, #_fdc_statbuf +fdc_statw: + in a, (c) + add a, a + jr c, fdc_statw + add a, a + ret nc ; Finished copying block + ld b, #0x3F + ini + ld b, #0x2F + ex (sp), hl + ex (sp), hl + jr fdc_statw + +fdc_statusa: + call fdc_status + ld a, (_fdc_statbuf) + ret + +fdc_read: + ld hl, #_rwcmd + ld (hl), #66 + ld b, #9 + call fdc_cmd ; Will return with DI +fdc_readgo: + ld bc, #0x2ffd + ld d, #0x20 + jr fdc_rwait +; +; This would be faster using EXX. May be worth looking at depending +; upon clock counts. +; +fdc_rbyte: + ld b, #0x3F ; 3FFD + ini + ld b, #0x2f +fdc_rwait: ; wait for ready + in a, (c) + jp p, fdc_rwait + and d + jp nz, fdc_rbyte + ; now get status + jr fdc_statusa + +fdc_write: + ld hl, #_rwcmd + ld (hl), #65 + ld b, #9 + call fdc_cmd ; Will return with DI +fdc_writego: + ld bc, #0x2ffd + ld d, #0x20 + jr fdc_wwait +fdc_wbyte: + ld b, #0x40 + outi + ld b, #0x2f +fdc_wwait: + in a, (c) + jp p, fdc_wwait + ; now read status + jr fdc_statusa + +; +; Seek is interesting as we have to second guess the delays +; +fdc_seek: + ld (_seekcmd + 2), a ; Cylinder we want +fdc_seek2: + ld b, a + ld b, #3 + call fdc_cmdq + ld bc, (_seekcmd + 2) + ld a, (curtrack) ; Current track FIXME + sub c + bit 7, a + jr z, seekin + neg +seekin: ; milliseconds of time for the seek + add a, a + ld b, a + add a, a + add a, b + add a, a ; 12ms step rate assumed + add a, #0x1e ; head settle + call msdelay + ; We should now get a sense interrupt + call fdc_sense + ; Check if it worked + bit 6, a + jr nz, fdc_seekfail + ld a, (_seekcmd + 2) + ld (curtrack), a ; FIXME per drive + ret ; Z +fdc_seekfail: + ld a, #0xFF + ld (curtrack), a ; Mark as busted + ret ; NZ + +fdc_recalibrate: + ld hl, #_recalcmd + ld b, #2 ; recalibrate, unit + call fdc_cmdq + ld a, #80 ; or 40 if 40 track ! + ld b, #12 ; 12ms +fdc_recald: + ld a, #80 ; or 40 if 40 track ! + call msdelay + djnz fdc_recald + ld a, #0x1e ; settle + call msdelay + call fdc_status + bit 6, a + jr nz, fdc_seekfail + xor a + ld (curtrack), a + ret ; Z + +fdc_sense: + ld hl, #sensecmd + call fdc_outcmd + call fdc_statusa + bit 7, a + jr nz, fdc_sense + ld a, (_fdc_statbuf) + ret + +fdc_cmdwait: + call fdc_sense + and #0xC0 + cp #0x80 + jr nz, fdc_cmdwait + ret + +;fdc_getunit: +; ld hl, #fdc_guscmd +; ld b, #2 +; call fdc_cmdq +; jr fdc_statusa + +; +; Delay 'a' milliseconds. Trashes A, C. +; +msdelay: ; For uncontended RAM + ld c, #0xDC + dec c + jr nz, msdelay + dec a + jr nz, msdelay + ret + + +; +; Must be called with the motor timeout stopped +; +_fdc_motoron: + ld a, (port_map) + bit 3, a + ret nz + or #9 + ld (port_map), a + ld bc, #0x1ffd + out (c), a +_fdc_mwait: + ld bc, #0x3548 + dec bc + ld a, b + or c + jr nz, _fdc_mwait + ret + +_fdc_motoroff: + ld a, (port_map) + and #0xF7 + ld (port_map), a + ld bc, #0x1ffd + out (c), a + ret + +_fdc_read: + ld hl, (_fdc_addr) + ld a, (_fdc_user) + or a + push af + call nz, map_process_always + call fdc_read + pop af + call nz, map_kernel + ret + +_fdc_write: + ld hl, (_fdc_addr) + ld a, (_fdc_user) + or a + push af + call nz, map_process_always + call fdc_write + pop af + call nz, map_kernel + ret + +_fdc_recal: + call fdc_recalibrate + ld l, a + ret + +_fdc_seek: + call fdc_seek2 + ret + + .area _COMMONDATA + +curtrack: + .byte 0 ; FIXME + +_rwcmd: + .byte 0x66 ; MFM read / 0x65 for write + .byte 0 ; Drive 0, head 0 (for now) + .byte 0 ; Track + .byte 0 ; Head + .byte 0 ; Sector + .byte 2 ; 512 byte blocks + .byte 0 ; Last sector (== sector) + .byte 0x2A ; Gap length + .byte 0xFF + +sensecmd: + .byte 0x08 + +_seekcmd: + .byte 0x0F + .byte 0x00 + .byte 0x00 ; Cylinder + +_recalcmd: + .byte 0x07 + .byte 0 + +_fdc_user: + .byte 0x00 +_fdc_addr: + .word 0x0000 +_fdc_statbuf: + .word 0,0,0,0 diff --git a/Kernel/platform-plus3/fuzix.lnk b/Kernel/platform-plus3/fuzix.lnk new file mode 100644 index 00000000..0430d962 --- /dev/null +++ b/Kernel/platform-plus3/fuzix.lnk @@ -0,0 +1,45 @@ +-mwxuy +-r +-i fuzix.ihx +-b _COMMONDATA=0xF800 +-b _CODE=0x0000 +-b _DISCARD=0xC000 +-l z80 +platform-plus3/crt0.rel +platform-plus3/commonmem.rel +platform-plus3/plus3.rel +platform-plus3/zxvideo.rel +platform-plus3/main.rel +start.rel +version.rel +lowlevel-z80.rel +usermem_std-z80.rel +platform-plus3/tricks.rel +timer.rel +kdata.rel +usermem.rel +platform-plus3/devices.rel +platform-plus3/devfd.rel +platform-plus3/floppy.rel +devio.rel +filesys.rel +process.rel +inode.rel +syscall_exec16.rel +syscall_fs.rel +syscall_fs2.rel +syscall_proc.rel +syscall_other.rel +tty.rel +vt.rel +font8x8.rel +mm.rel +simple.rel +swap.rel +devsys.rel +platform-plus3/devtty.rel +platform-plus3/devide.rel +platform-plus3/devide_discard.rel +platform-plus3/mbr.rel +platform-plus3/blkdev.rel +-e diff --git a/Kernel/platform-plus3/kernel.def b/Kernel/platform-plus3/kernel.def new file mode 100644 index 00000000..88bc5c42 --- /dev/null +++ b/Kernel/platform-plus3/kernel.def @@ -0,0 +1,10 @@ +; UZI mnemonics for memory addresses etc + +U_DATA .equ 0xF800 ; (this is struct u_data from kernel.h) +U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes. + +Z80_TYPE .equ 1 + +PROGBASE .equ 0x0000 +PROGLOAD .equ 0x0100 + diff --git a/Kernel/platform-plus3/main.c b/Kernel/platform-plus3/main.c new file mode 100644 index 00000000..37987a79 --- /dev/null +++ b/Kernel/platform-plus3/main.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include + +uint16_t ramtop = PROGTOP; +uint16_t swap_dev; + +void platform_idle(void) +{ + __asm + halt + __endasm; +} + +void platform_interrupt(void) +{ + tty_pollirq(); +//FIXME floppy_timer(); + timer_interrupt(); +} + +/* Nothing to do for the map of init */ +void map_init(void) +{ +} diff --git a/Kernel/platform-plus3/plus3.s b/Kernel/platform-plus3/plus3.s new file mode 100644 index 00000000..61f75b98 --- /dev/null +++ b/Kernel/platform-plus3/plus3.s @@ -0,0 +1,235 @@ +; +; ZX Spectrum Plus 2A and Plus 3 hardware support +; + + .module plus3 + + ; 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 nmi_handler + + .globl map_kernel + .globl map_process + .globl map_process_always + .globl map_save + .globl map_restore + .globl map_process_save + .globl map_kernel_restore + .globl map_video + .globl unmap_video + + .globl _kernel_flag + .globl port_map + + ; exported debugging tools + .globl _trap_monitor + .globl outchar + + ; imported symbols + .globl _ramsize + .globl _procmem + + .globl outcharhex + .globl outhl, outde, outbc + .globl outnewline + .globl outstring + .globl outstringhex + + .include "kernel.def" + .include "../kernel.def" + +; ----------------------------------------------------------------------------- +; COMMON MEMORY BANK (above 0xC000 in page 3) +; ----------------------------------------------------------------------------- + .area _COMMONMEM + +_trap_monitor: + ; + ; Not so much a monitor as wait for space + ; + ld a, #0x7F + in a, (0xFE) + rra + jr c, _trap_monitor + +_trap_reboot: + di + im 1 + ld bc, #0x7ffd + xor a ; 128K ROM, initial banks, low screen + out (c), a + rst 0 ; Into the ROM + +platform_interrupt_all: + ret + +; ----------------------------------------------------------------------------- +; KERNEL MEMORY BANK (below 0xC000, only accessible when the kernel is mapped) +; ----------------------------------------------------------------------------- + .area _CODE + +init_early: + ld bc, #0x7ffd + ld a, #3 + 8 ; Screen into page 7 + ret + + .area _VIDEO + +init_hardware: + ; set system RAM size + ld hl, #128 + ld (_ramsize), hl + ld hl, #(128 - 64) ; 64K for kernel/screen/etc + ld (_procmem), hl + + call map_video + ; screen initialization + ; clear + ld hl, #0xC000 + ld de, #0xC001 + ld bc, #0x1800 ; There should be 0x17FF, but we are going + xor a ; to copy additional byte to avoid need of + ld (hl), a ; DE and HL increment before attribute + ldir ; initialization (2 bytes of RAM economy) + + ; set color attributes + ld a, #7 ; black paper, white ink + ld bc, #0x300 - #1 + ld (hl), a + ldir + + call unmap_video + ret + +;------------------------------------------------------------------------------ +; COMMON MEMORY PROCEDURES FOLLOW + + .area _COMMONMEM + +_program_vectors: + di + pop de + pop hl + push hl + push de + + 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 + + ; Set vector for jump to NULL + 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 + +switch_kernel: + ld a, (port_map) + and #0xF8 ; Preserve the other bits + or #0x05 ; Map 4,5,6,3 +switchit: + push bc + ld (port_map), a + ld bc, #0x1FFD + out (c), a + pop bc + ret + +switch_user: + ld a, (port_map) + and #0xF8 ; Preserve the other bits + and #0x01 ; Map 0,1,2,3 + jr switch_kernel + +switch_video: + ld a, (port_map) + or #0x7 ; Map 4,7,6,3 + jr switchit + +map_process: + ld a, h + or l + jr z, map_kernel +map_process_save: +map_process_always: + push af + call switch_user + pop af + ret + +unmap_video: +map_kernel: +map_kernel_restore: + push af + call switch_kernel + pop af + ret + +map_video: + push af + call switch_video + pop af + ret + +map_save: + push af + ld a, (port_map) + and #7 + ld (map_store), a + pop af + ret + +map_restore: + push af + push hl + ld a, (port_map) + and #0xF8 + ld hl, #map_store + or (hl) + call switchit + pop hl + pop af + ret +; +; We have no easy serial debug output instead just breakpoint this +; address when debugging. +; +outchar: + ret + + .area _COMMONDATA +_kernel_flag: + .db 1 +port_map: ; place to store current map register values + .db 0 ; because we have no ability to read 1ffd port + ; to detect what page is mapped currently +map_store: + .db 0 +ksave_map: + .db 0 diff --git a/Kernel/platform-plus3/rules.mk b/Kernel/platform-plus3/rules.mk new file mode 100644 index 00000000..41bd765a --- /dev/null +++ b/Kernel/platform-plus3/rules.mk @@ -0,0 +1,3 @@ +# +# ZXSpectrum +3 uses unbanked kernel images (for now anyway) +# diff --git a/Kernel/platform-plus3/sysconfig.h b/Kernel/platform-plus3/sysconfig.h new file mode 100644 index 00000000..bdfdc9f2 --- /dev/null +++ b/Kernel/platform-plus3/sysconfig.h @@ -0,0 +1,8 @@ +#ifndef _SYSCONFIG_H +#define _SYSCONFIG_H + +extern uint16_t sysconfig; + +#define CONF_PLUS3 0x0001 /* Plus3 (has 3DOS) */ + +#endif \ No newline at end of file diff --git a/Kernel/platform-plus3/target.mk b/Kernel/platform-plus3/target.mk new file mode 100644 index 00000000..3bffcde0 --- /dev/null +++ b/Kernel/platform-plus3/target.mk @@ -0,0 +1 @@ +export CPU = z80 diff --git a/Kernel/platform-plus3/tricks.s b/Kernel/platform-plus3/tricks.s new file mode 100644 index 00000000..4744ac15 --- /dev/null +++ b/Kernel/platform-plus3/tricks.s @@ -0,0 +1,226 @@ + .module tricks + +; +; Standardised implementation of tricks.s for pure swapping Z80 +; systems. Wants moving from here to lib/ +; +; FIXME: All of this can be moved into CODE ? +; + .globl _ptab_alloc + .globl _newproc + .globl _chksigs + .globl _getproc + .globl _trap_monitor + .globl trap_illegal + .globl _inint + .globl _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(). +; +; FIXME: make sure we optimise the switch to self case higher up the stack! +; +; This function can have no arguments or auto variables. +_switchout: + di + call _chksigs + ; 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 + + ; set inint to false + xor a + ld (_inint), a + + ; find another process to run (may select this one again) + call _getproc + + push hl + call _switchin + + ; we should never get here + call _trap_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 + push hl + ; We will always swap out the current process + ld hl, (U_DATA__U_PTAB) + push hl + call _swapout + pop hl + pop hl + push de + call _swapper + pop de + pop hl + ld a, (hl) + +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, (_inint) + or a + ret z ; 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 _trap_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: diff --git a/Kernel/platform-plus3/zxvideo.s b/Kernel/platform-plus3/zxvideo.s new file mode 100644 index 00000000..8e25a032 --- /dev/null +++ b/Kernel/platform-plus3/zxvideo.s @@ -0,0 +1,289 @@ +; +; zx128 vt primitives +; +; We load this high on the Plus3 so we can page in page 7 for the +; video overlay and frame buffer space. Any font will also go in page +; 7. These routines must not call any code outside of common and their +; own bank. +; + + .module zxvideo + + ; exported symbols + .globl _plot_char + .globl _scroll_down + .globl _scroll_up + .globl _cursor_on + .globl _cursor_off + .globl _clear_lines + .globl _clear_across + .globl _do_beep + + .area _VIDEO + + ; colors are ignored everywhere for now + +videopos: + ld a,e + and #7 + rrca + rrca + rrca + add a,d + ld d,e + ld e,a + ld a,d + and #0x18 + or #0xC0 ; not 0x40 as in screen 7 + ld d,a + ret + +_plot_char: + pop iy + pop hl + pop de ; D = x E = y + pop bc + push bc + push de + push hl + push iy + + call videopos + + ; + ; TODO: Map char 0x60 to a grave accent bitmap rather + ; than fudging with a quote + ; + + ld b, #0 ; calculating offset in font table + ld a, c + cp #0x60 + jr nz, nofiddle + ld a, #0x27 +nofiddle: + or a ; clear carry + rla + rl b + rla + rl b + rla + rl b + ld c, a + + ld hl, #0x3C00 ; ROM font + add hl, bc ; hl points to first byte of char data + + + ; printing + ld c, #8 +plot_char_loop: + ld a, (hl) + ld (de), a + inc hl ; next byte of char data + inc d ; next screen line + dec c + jr nz, plot_char_loop + ret + + +_clear_lines: + pop bc + pop hl + pop de ; E = line, D = count + push de + push hl + push bc + +clear_next_line: + push de + ld d, #0 ; from the column #0 + ld b, d ; b = 0 + ld c, #32 ; clear 32 cols + push bc + push de + call _clear_across + + pop hl ; clear stack + pop hl + + pop de + inc e + dec d + jr nz, clear_next_line + + ret + + +_clear_across: + pop iy + pop hl + pop de ; DE = coords + pop bc ; C = count + push bc + push de + push hl + push iy + call videopos ; first pixel line of first character in DE + push de + pop hl ; copy to hl + xor a + + ; no boundary checks. Assuming that D + C < SCREEN_WIDTH + +clear_line: + ld b, #8 ; 8 pixel lines to clear for this char +clear_char: + ld (de), a + inc d + dec b + jr nz, clear_char + + ex de, hl + inc de + push de + pop hl + + dec c + jr nz, clear_line + ret + +copy_line: + ; HL - source, DE - destination + + ; convert line coordinates to screen coordinates both for DE and HL + push de + ex de, hl + call videopos + ex de, hl + pop de + call videopos + + ld c, #8 + +copy_line_nextchar: + push hl + push de + + ld b, #32 + +copy_pixel_line: + ld a, (hl) + ld (de), a + inc e + inc l + dec b + jr nz, copy_pixel_line + + pop de + pop hl + inc d + inc h + dec c + jr nz, copy_line_nextchar + ret + + ; TODO: the LDIR way should be much faster + +_scroll_down: + ; set HL = (0,22), DE = (0, 23) + xor a + ld d, a + ld h, a + ld l, #22 + ld e, #23 + ld c, #23 ; 23 lines to move + +loop_scroll_down: + push hl + push de + push bc + + call copy_line + + pop bc + pop de + pop hl + + dec l + dec e + dec c + jr nz, loop_scroll_down + + ret + + +_scroll_up: + ; set HL = (0,1), DE = (0, 0) + xor a + ld d, a + ld e, a + ld h, a + ld l, #1 + ld c, #23 ; 23 lines to move + +loop_scroll_up: + push hl + push de + push bc + + call copy_line + + pop bc + pop de + pop hl + + inc l + inc e + dec c + jr nz, loop_scroll_up + + ret + +_cursor_on: + pop bc + pop hl + pop de + push de + push hl + push bc + ld (cursorpos), de + + call videopos + ld a, #7 + add a, d + ld d, a + ld a, #0xFF + ld (de), a + ret +_cursor_off: + ld de, (cursorpos) + call videopos + ld a, #7 + add a, d + ld d, a + xor a + ld (de), a + ret + + ; FIXME: now this is_do_silent_click actually +_do_beep: + ld e, #0xFF ; length + ld c, #0xFE ; beeper port + ld l, #0x10 ; beeper bit +loop_beep: + ld a, l + out (c), a + xor a + out (c), a + dec bc + ld a, b + or c + jr nz, loop_beep + ret + +; +; Must live in video or common so we are sure we can get at it with +; page 7 mapped +; +cursorpos: + .dw 0