From 72729e8924ebe3c5aee96c3a2e9a888f2116ce65 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 8 Aug 2018 00:13:43 +0100 Subject: [PATCH] kernel: initial development tree for the Sam Coupe This is so that the 32K/32K split code can be developed. This memory model is actually a lot more complex and different to the existing Z80 models we use. Don't expect anything runnable for quite some time. --- Kernel/platform-sam/Makefile | 25 +++ Kernel/platform-sam/README | 90 ++++++++ Kernel/platform-sam/boot.s | 84 ++++++++ Kernel/platform-sam/commonmem.s | 10 + Kernel/platform-sam/config.h | 52 +++++ Kernel/platform-sam/crt0.s | 70 ++++++ Kernel/platform-sam/devfd.c | 102 +++++++++ Kernel/platform-sam/devfd.h | 15 ++ Kernel/platform-sam/devices.c | 42 ++++ Kernel/platform-sam/devlpr.c | 38 ++++ Kernel/platform-sam/devlpr.h | 8 + Kernel/platform-sam/devtty.c | 177 ++++++++++++++++ Kernel/platform-sam/devtty.h | 7 + Kernel/platform-sam/floppy.s | 16 ++ Kernel/platform-sam/fuzix.lnk | 43 ++++ Kernel/platform-sam/kernel.def | 14 ++ Kernel/platform-sam/main.c | 50 +++++ Kernel/platform-sam/rules.mk | 1 + Kernel/platform-sam/sam.s | 365 ++++++++++++++++++++++++++++++++ Kernel/platform-sam/sam_vt.s | 186 ++++++++++++++++ Kernel/platform-sam/target.mk | 1 + Kernel/platform-sam/tricks.s | 239 +++++++++++++++++++++ 22 files changed, 1635 insertions(+) create mode 100644 Kernel/platform-sam/Makefile create mode 100644 Kernel/platform-sam/README create mode 100644 Kernel/platform-sam/boot.s create mode 100644 Kernel/platform-sam/commonmem.s create mode 100644 Kernel/platform-sam/config.h create mode 100644 Kernel/platform-sam/crt0.s create mode 100644 Kernel/platform-sam/devfd.c create mode 100644 Kernel/platform-sam/devfd.h create mode 100644 Kernel/platform-sam/devices.c create mode 100644 Kernel/platform-sam/devlpr.c create mode 100644 Kernel/platform-sam/devlpr.h create mode 100644 Kernel/platform-sam/devtty.c create mode 100644 Kernel/platform-sam/devtty.h create mode 100644 Kernel/platform-sam/floppy.s create mode 100644 Kernel/platform-sam/fuzix.lnk create mode 100644 Kernel/platform-sam/kernel.def create mode 100644 Kernel/platform-sam/main.c create mode 100644 Kernel/platform-sam/rules.mk create mode 100644 Kernel/platform-sam/sam.s create mode 100644 Kernel/platform-sam/sam_vt.s create mode 100644 Kernel/platform-sam/target.mk create mode 100644 Kernel/platform-sam/tricks.s diff --git a/Kernel/platform-sam/Makefile b/Kernel/platform-sam/Makefile new file mode 100644 index 00000000..a9856afb --- /dev/null +++ b/Kernel/platform-sam/Makefile @@ -0,0 +1,25 @@ + +CSRCS = devlpr.c devtty.c devfd.c +CSRCS += devices.c main.c + +ASRCS = sam.s crt0.s sam_vt.s floppy.s +ASRCS += tricks.s commonmem.s + +COBJS = $(CSRCS:.c=.rel) +AOBJS = $(ASRCS:.s=.rel) +OBJS = $(COBJS) $(AOBJS) + +JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:.s=.lst) $(ASRCS:.s=.sym) $(CSRCS:.c=.rst) $(ASRCS:.s=.rst) + +all: $(OBJS) + +$(COBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +$(AOBJS): %.rel: %.s + $(CROSS_AS) $(ASOPTS) $< + +clean: + rm -f $(OBJS) $(JUNK) core *~ + +image: diff --git a/Kernel/platform-sam/README b/Kernel/platform-sam/README new file mode 100644 index 00000000..ab6effbd --- /dev/null +++ b/Kernel/platform-sam/README @@ -0,0 +1,90 @@ +Sam Coupe prototyping and 32K/32K split development work + +Proposed memory layout + +Kernel mode + +0-7FFF Kernel in bank 2/3 +8000-FFFF Kernel in bank 0/1 + +Video + +0-5FFF Frame buffer (mode 3) bank 4/5 +6000-6FFF Font +7000-7FFF Free +8000-FFFF Kernel in bank 0/1 + +User Space + +0000-00FF Thunking code +0100-7FFF User page +8000-FCFFF User page +FD00-FEFF UDATA stash +FF00-FFFF Thunking code + +Probably will move the thunking code mostly into FFxx somewhere so we +can do CP/M emulation still + +Extmem support would require extra work and separate high/low pools for +the bank allocator + +User Copies + +0000-7FFF User bank for copy +8000-FFFF Kernel in bank 0/1 + +Forking + +0000-00FF Parent low stubs +0100-7FFF Parent low bank +8000-80FF Child low stubs (not yet set) +8100-FFFF Child low bank + +then + +0000-7EFF Parent high bank +7F00-7FFF Parent high stubs +8000-FEFF Child high bank +FF00-FFFF Child stubs (not yet set) + +Stub code is in the low parts of the low banks and the top of the high banks +so we have two copier routines one for each half. + + +To Do: + +- Keyboard map +- Interrupt logic +- Boot loader + +Then once we can get to the point of being able to see and type + +- Move fonts to video bank after frame buffer +- Floppy driver (at least for read) +- Get to init +- Debug all the new banking code and stubs +- Atom and Atom-lite IDE driver +- Look at how to preserve high colour bits + +- RTC driver +- Serial driver +- Mouse/Joystick/Input + +- Mode setting/graphics mode support +- Video ioctls +- UDG ioctls +- Is there any way to do video mapping into user process sanely + (probably not) + +- Sound + +- MegaRAM + +- Maybe look at a 16K boundary aware allocator to get better memory + packing - but it's really hairy. + +- Less interrupt masking (but the banking logic makes it really foul + especially as we don't have proper IM2 support) + +- Maybe look at the modern add ons (SD card, network etc) + diff --git a/Kernel/platform-sam/boot.s b/Kernel/platform-sam/boot.s new file mode 100644 index 00000000..9e6a03fe --- /dev/null +++ b/Kernel/platform-sam/boot.s @@ -0,0 +1,84 @@ +; +; Boot block, loaded at 0x4000 +; +; Banks and SP we need to double check are ROM0/1/2/3 and valid +; +; The boot block is trivial but it gets a bit fun once we've loaded +; our 14 tracks +; +; We load +; 32K into bank 2/3 +; 32K into bank 4/5 +; 5.5K into bank 6/7 +; +; by loading 14 tracks off side 0 skipping track 0 sector 1 which is +; the boot block itself. +; +; We then run from 0x8000 in bank 6, which will untangle everything +; for us shuffling the kernel so that 0/1 is the low kernel in RAM +; 2/3 is the high kernel in RAM and 4/5 is the video +; (or maybe it'll be saner to use 4/5 for kernel 2/3 video..) +; +boot: + di + ld de,#2 ; track 0 sector 2 (we are sector 0) + ld a,e ; Start in bank 2 + ld hl,#0x8000 ; Which we map high + out (HIMEM),a + ex af,af' +next_sec: + bit 7,h ; If the last read went to 0000 + jr nz, still_good + set 7,h ; Go back to 8000 and move on 32K + ex af,af' + inc a + inc a + out (HIMEM),a ; switch bank + ex af,af' +still_good: + ld a,e ; update the sector register + ld (SECTOR),a +dread: ld a,#CMD_READ ; Ask for data from the FDC + out (CMD),a + ld b,#20 ; Wait for FDC +nap: djnz nap + ld bc,DATA ; Begin reading from data port + jr waitbyte +byte: ini +wait: in a,(STATUS) ; Wait for DRQ + bit 1,a + jr nz,byte ; When we get a DRQ read a byte + rrca ; Check for end/error + jr c,wait + ; and with 0d to check error FIXME + ; + ; Sector done. Move on a sector + ; + inc e + ld a,#11 + cp e + jr nz, next_sec + ; + ; Move on a track - do a step in command + ; + ld e,#1 + inc d + ; + ; We read 14 tracks minus 1 sector which loads us + ; + ; + ld a,#14 + cp d + ; Jump to the start of the last bank loaded. This is actually + ; packed up boot stuff and font. We loaded 5.5K here, of which 4K + ; was font, leaving a spacious 1.5K to untangle all the banks and + ; boot up. + jp z, 0x8000 + ld a,#CMD_STEPIN + out (CMD),a +nap2: ld b,#20 + djnz nap2 +wait2: in a,(STATUS) + bit 0,a + jr nz,wait2 + jr next_sec diff --git a/Kernel/platform-sam/commonmem.s b/Kernel/platform-sam/commonmem.s new file mode 100644 index 00000000..bafd590b --- /dev/null +++ b/Kernel/platform-sam/commonmem.s @@ -0,0 +1,10 @@ +; +; We have no real common on the TRS80so just tuck it up at the top of +; memory leaving room for the keyboard and video (3K) +; + .module commonmem + + .area _COMMONMEM + + .include "../cpu-z80/std-commonmem.s" + diff --git a/Kernel/platform-sam/config.h b/Kernel/platform-sam/config.h new file mode 100644 index 00000000..3377692d --- /dev/null +++ b/Kernel/platform-sam/config.h @@ -0,0 +1,52 @@ +/* Set if you want RTC support and have an RTC on ports 0xB0-0xBC */ +#undef CONFIG_RTC + +/* 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 */ +#define CONFIG_MULTI +/* Single tasking */ +#undef CONFIG_SINGLETASK + +/* Video terminal, not a serial tty */ +#define CONFIG_VT +/* Fonts are pre-expanded as we have reasonable amounts of memory and + need to deal with a 2bit deep bitmap display */ +#define CONFIG_FONT_8X8_EXP2 + +/* Banked memory set up */ +/* FIXME */ +#define CONFIG_BANK_FIXED +#define MAX_MAPS 16 +#define MAP_SIZE 0xFD00 + +#define CONFIG_BANKS 2 /* 2 x 32K */ + +/* Vt definitions */ +#define VT_WIDTH 80 +#define VT_HEIGHT 24 +#define VT_RIGHT 79 +#define VT_BOTTOM 23 + +#define TICKSPERSEC 50 /* Ticks per second */ +#define PROGBASE 0x0000 /* Base of user */ +#define PROGLOAD 0x0100 /* Load and run here */ +#define PROGTOP 0xFD00 /* Top of program, base of U_DATA stash */ +#define PROC_SIZE 64 /* Memory needed per process */ + +#define BOOT_TTY (512 + 1) /* 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 2 +#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ + +#define NBUFS 10 /* Number of block buffers */ +#define NMOUNTS 4 /* Number of mounts at a time */ diff --git a/Kernel/platform-sam/crt0.s b/Kernel/platform-sam/crt0.s new file mode 100644 index 00000000..d0ef9148 --- /dev/null +++ b/Kernel/platform-sam/crt0.s @@ -0,0 +1,70 @@ + ; For 32K banking we need to keep anything that is common + ; and anything that might be copied to or from user space + ; in the top 32K. So fill the bottom with code + .area _CODE + .area _CODE2 + .area _HOME + ; Must be above the 32K boundary + ; FIXME: we really want to bank the fonts in the video area + .area _VIDEO + .area _HIGH + .area _FONT + .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 _GSINIT + .area _GSFINAL + .area _DISCARD + .area _INITIALIZER + .area _COMMONMEM + + .area _PAGE0 + .area _PAGEH + + ; imported symbols + .globl _fuzix_main + .globl init_early + .globl init_hardware + .globl s__DATA + .globl l__DATA + .globl s__DISCARD + .globl l__DISCARD + .globl s__COMMONMEM + .globl l__COMMONMEM + .globl s__INITIALIZER + .globl kstack_top + + ; startup code + .area _CODE + +; +; Once the loader completes it jumps here +; +start: + ld sp, #kstack_top + ; move the common memory where it belongs + ld hl, #s__DATA + ld de, #s__COMMONMEM + ld bc, #l__COMMONMEM + ldir + ; then 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 + call init_early + call init_hardware + call _fuzix_main + di +stop: halt + jr stop diff --git a/Kernel/platform-sam/devfd.c b/Kernel/platform-sam/devfd.c new file mode 100644 index 00000000..77503e18 --- /dev/null +++ b/Kernel/platform-sam/devfd.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +#define MAX_FD 4 + +#define OPDIR_NONE 0 +#define OPDIR_READ 1 +#define OPDIR_WRITE 2 + +#define FD_READ 0x80 /* 2797 needs 0x88, 1797 needs 0x80 */ +#define FD_WRITE 0xA0 /* Likewise A8 v A0 */ + +static uint8_t motorct; +static uint8_t fd_selected = 0xFF; +static uint8_t fd_tab[MAX_FD] = { 0xFF, 0xFF, 0xFF, 0xFF }; + +/* + * We only support normal block I/O for the moment. We do need to + * add swapping! + */ + +static uint8_t selmap[4] = { 0x01, 0x02, 0x04, 0x08 }; + +static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag) +{ + int ct = 0; + int tries; + uint8_t err = 0; + uint8_t *driveptr = fd_tab + minor; + uint8_t cmd[6]; + + if(rawflag == 2) + goto bad2; + + if (fd_selected != minor) { + uint8_t err; + /* FIXME: We force DD for now */ + err = fd_motor_on(selmap[minor]|0x80); + if (err) + goto bad; + } + + if (*driveptr == 0xFF) + fd_reset(driveptr); + + if (rawflag && d_blkoff(BLKSHIFT)) + return -1; + + cmd[0] = is_read ? FD_READ : FD_WRITE; + cmd[1] = udata.u_block / 9; /* 2 sectors per block */ + cmd[2] = ((udata.u_block % 9) << 1) + 1; /*eww.. */ + cmd[3] = is_read ? OPDIR_READ: OPDIR_WRITE; + cmd[4] = ((uint16_t)udata.u_dptr) & 0xFF; + cmd[5] = ((uint16_t)udata.u_dptr) >> 8; + + while (ct < 2) { + for (tries = 0; tries < 4 ; tries++) { + err = fd_operation(cmd, driveptr); + if (err == 0) + break; + if (tries > 1) + fd_reset(driveptr); + } + /* FIXME: should we try the other half and then bale out ? */ + if (tries == 4) + goto bad; + cmd[5]++; /* Move on 256 bytes in the buffer */ + cmd[2]++; /* Next sector for 2nd block */ + ct++; + } + return udata.u_nblock << 8; +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; + if(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;rawflag;minor; +// return 0; + return fd_transfer(minor, false, rawflag); +} diff --git a/Kernel/platform-sam/devfd.h b/Kernel/platform-sam/devfd.h new file mode 100644 index 00000000..5f945073 --- /dev/null +++ b/Kernel/platform-sam/devfd.h @@ -0,0 +1,15 @@ +#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 *cmd, uint8_t *driveptr); +uint16_t fd_motor_on(uint16_t drivesel); +uint16_t fd_motor_off(uint16_t driveptr); + +#endif /* __DEVFD_DOT_H__ */ diff --git a/Kernel/platform-sam/devices.c b/Kernel/platform-sam/devices.c new file mode 100644 index 00000000..66c71840 --- /dev/null +++ b/Kernel/platform-sam/devices.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct devsw dev_tab[] = /* The device driver switch table */ +{ + /* 0: FIXME IDE */ + { fd_open, no_close, fd_read, fd_write, no_ioctl }, + /* 1: /dev/dd Hard disc block devices */ + { fd_open, no_close, fd_read, fd_write, no_ioctl }, + /* 2: /dev/tty TTY devices */ + { tty_open, tty_close, tty_read, tty_write, tty_ioctl }, + /* 3: /dev/lpr Printer devices */ + { lpr_open, lpr_close, no_rdwr, lpr_write, no_ioctl }, + /* 4: /dev/mem etc System devices (one offs) */ + { no_open, no_close, sys_read, sys_write, sys_ioctl }, + /* Pack to 7 with nxio if adding private devices and start at 8 */ +}; + +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) +{ + int i; +#ifdef CONFIG_RTC + /* Time of day clock */ + inittod(); +#endif +} diff --git a/Kernel/platform-sam/devlpr.c b/Kernel/platform-sam/devlpr.c new file mode 100644 index 00000000..65d8c439 --- /dev/null +++ b/Kernel/platform-sam/devlpr.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +__sfr __at 0x02 lpstat; /* I/O 2 and 3 */ +__sfr __at 0x03 lpdata; + +int lpr_open(uint8_t minor, uint16_t flag) +{ + minor; flag; // shut up compiler + return 0; +} + +int lpr_close(uint8_t minor) +{ + minor; // shut up compiler + return 0; +} + +int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + int c = udata.u_count; + char *p = udata.u_base; + minor; rawflag; flag; // shut up compiler + + while(c) { + /* Note; on real hardware it might well be necessary to + busy wait a bit just to get acceptable performance */ + while (lpstat != 0xFF) { +// if (psleep_flags(&clocktick, flag)) +// return -1; + } + /* FIXME: tidy up ugetc and sysio checks globally */ + lpdata = ugetc(p++); + } + return (-1); +} diff --git a/Kernel/platform-sam/devlpr.h b/Kernel/platform-sam/devlpr.h new file mode 100644 index 00000000..7765c187 --- /dev/null +++ b/Kernel/platform-sam/devlpr.h @@ -0,0 +1,8 @@ +#ifndef __DEVLPR_DOT_H__ +#define __DEVLPR_DOT_H__ + +int lpr_open(uint8_t minor, uint16_t flag); +int lpr_close(uint8_t minor); +int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag); + +#endif diff --git a/Kernel/platform-sam/devtty.c b/Kernel/platform-sam/devtty.c new file mode 100644 index 00000000..78dfb7a0 --- /dev/null +++ b/Kernel/platform-sam/devtty.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +char tbuf1[TTYSIZ]; +char tbuf2[TTYSIZ]; + +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 } +}; + +/* Write to system console */ +void kputchar(char c) +{ + if(c=='\n') + tty_putc(1, '\r'); + tty_putc(1, c); +} + +uint8_t tty_writeready(uint8_t minor) +{ + return TTY_READY_NOW; +} + +void tty_putc(uint8_t minor, unsigned char c) +{ + if (minor == 1) + vtoutput(&c, 1); +} + +void tty_interrupt(void) +{ +} + +void tty_setup(uint8_t minor) +{ +} + +int tty_carrier(uint8_t minor) +{ + return 1; +} + +void tty_sleeping(uint8_t minor) +{ +} + +void tty_data_consumed(uint8_t minor) +{ +} + +static uint8_t keymap[9]; +uint8_t keyin[9]; +static uint8_t keybyte, keybit; +static uint8_t newkey; +static int keysdown = 0; +static uint8_t shiftmask[8] = { + 0, 0, 0, 0, 0, 0, 0, 7 +}; + +static void keyproc(void) +{ + int i; + uint8_t key; + + /* Call asm keyin here FIXME */ + for (i = 0; i < 8; i++) { + /* Set one of A0 to A7, and read the byte we get back. + Invert that to get a mask of pressed buttons */ + key = keyin[i] ^ keymap[i]; + if (key) { + int n; + int m = 1; + for (n = 0; n < 8; 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 += m; + + } + } + keymap[i] = keyin[i]; + } +} + +static uint8_t keyboard[8][8] = { + {'@', 'a', 'b', 'c', 'd', 'e', 'f', 'g' }, + {'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o' }, + {'p', 'q', 'r', 's', 't', 'u', 'v', 'w' }, + {'x', 'y', 'z', '[', '\\', ']', '^', '_' }, + {'0', '1', '2', '3', '4', '5', '6', '7' }, + {'8', '9', ':', ';', ',', '-', '.', '/' }, + {13, 12, 3, 0/*up*/, 0/*down*/, 8/* left */, 0/*right*/, ' '}, + { 0, 0, 0, 0, 0xF1, 0xF2, 0xF3, 0 } +}; + +static uint8_t shiftkeyboard[8][10] = { + {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G' }, + {'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O' }, + {'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W' }, + {'X', 'Y', 'Z', '{', '|', '}', '^', '_' }, + {'0', '!', '"', '#', '$', '%', '&', '\'' }, + {'(', ')', '*', '+', '<', '=', '>', '?' }, + {13, 12, 3, 0/*up*/, 0/*down*/, 8/* left */, 0/*right*/, ' '}, + { 0, 0, 0, 0, 0xF1, 0xF2, 0xF3, 0 } +}; + +static uint8_t capslock = 0; + +static void keydecode(void) +{ + uint8_t c; + + if (keybyte == 7 && keybit == 3) { + capslock = 1 - capslock; + return; + } + + if (keymap[7] & 3) /* shift */ + c = shiftkeyboard[keybyte][keybit]; + else + c = keyboard[keybyte][keybit]; + + /* The keyboard lacks some rather important symbols so remap them + with control */ + if (keymap[7] & 4) { /* control */ + if (c > 31 && c < 96) + c &= 31; + if (keymap[7] & 3) { + if (c == '(') + c = '{'; + if (c == ')') + c = '}'; + if (c == '-') + c = '_'; + if (c == '/') + c = '``'; + if (c == '<') + c = '^'; + } else { + if (c == '(') + c = '['; + if (c == ')') + c = ']'; + if (c == '-') + c = '|'; + } + } + if (capslock && c >= 'a' && c <= 'z') + c -= 'a' - 'A'; + if (c) + tty_inproc(1, c); +} + +void kbd_interrupt(void) +{ + newkey = 0; + keyproc(); + if (keysdown < 3 && newkey) + keydecode(); +} + diff --git a/Kernel/platform-sam/devtty.h b/Kernel/platform-sam/devtty.h new file mode 100644 index 00000000..bda124eb --- /dev/null +++ b/Kernel/platform-sam/devtty.h @@ -0,0 +1,7 @@ +#ifndef _DEVTTY_H +#define _DEVTTY_H + +extern void tty_interrupt(void); +extern void kbd_interrupt(void); + +#endif diff --git a/Kernel/platform-sam/floppy.s b/Kernel/platform-sam/floppy.s new file mode 100644 index 00000000..62983937 --- /dev/null +++ b/Kernel/platform-sam/floppy.s @@ -0,0 +1,16 @@ +; +; TODO +; + + .module floppy + + .area _HIGH + + .globl _fd_motor_on + .globl _fd_operation + .globl _fd_reset + +_fd_operation: +_fd_reset: +_fd_motor_on: + ret diff --git a/Kernel/platform-sam/fuzix.lnk b/Kernel/platform-sam/fuzix.lnk new file mode 100644 index 00000000..62ea0f62 --- /dev/null +++ b/Kernel/platform-sam/fuzix.lnk @@ -0,0 +1,43 @@ +-mwxuy +-i fuzix.ihx +-b _PAGE0=0x0001 +-b _CODE=0x0100 +-b _COMMONMEM=0xF800 +-b _PAGEH=0xFF00 +-l z80 +platform-sam/crt0.rel +platform-sam/commonmem.rel +start.rel +version.rel +lowlevel-z80-thunked.rel +usermem.rel +usermem_std-z80-thunked.rel +timer.rel +kdata.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 +mm.rel +swap.rel +bankfixed.rel +vt.rel +font8x8_exp2.rel +devsys.rel +platform-sam/devfd.rel +platform-sam/devices.rel +platform-sam/devlpr.rel +platform-sam/devtty.rel +platform-sam/floppy.rel +platform-sam/main.rel +platform-sam/sam.rel +platform-sam/tricks.rel +platform-sam/sam_vt.rel +-e diff --git a/Kernel/platform-sam/kernel.def b/Kernel/platform-sam/kernel.def new file mode 100644 index 00000000..28a47e3f --- /dev/null +++ b/Kernel/platform-sam/kernel.def @@ -0,0 +1,14 @@ +; UZI mnemonics for memory addresses etc + +U_DATA .equ 0x010 ; (this is struct u_data from kernel.h) +U_DATA__TOTALSIZE .equ 0x200 ; 512 bytes. + +U_DATA_STASH .equ 0xFD00 ; FD00-FEFF + ; FF00-FFFF is the high stubs + + +PROGBASE .equ 0x0000 +PROGLOAD .equ 0x0100 + +Z80_TYPE .equ 1 + diff --git a/Kernel/platform-sam/main.c b/Kernel/platform-sam/main.c new file mode 100644 index 00000000..4aaac461 --- /dev/null +++ b/Kernel/platform-sam/main.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include + +uint16_t ramtop = PROGTOP; +uint8_t need_resched; + +/* On idle we spin checking for the terminals. Gives us more responsiveness + for the polled ports */ +void platform_idle(void) +{ + __asm + halt + __endasm; +} + +void do_beep(void) +{ +} + +void platform_interrupt(void) +{ + tty_interrupt(); + kbd_interrupt(); + timer_interrupt(); +} + +void map_init(void) +{ +} + +void platform_discard(void) +{ +} + +void pagemap_init(void) +{ + pagemap_add(0x63); /* Mode 3, U64K low 32K mapped as low 32K */ + pagemap_add(0x73); /* Mode 3, U64K high 32K mapped as low 32K */ +} + + +uint8_t platform_param(char *p) +{ + used(p); + return 0; +} + diff --git a/Kernel/platform-sam/rules.mk b/Kernel/platform-sam/rules.mk new file mode 100644 index 00000000..c3701b05 --- /dev/null +++ b/Kernel/platform-sam/rules.mk @@ -0,0 +1 @@ +export BANKED=-thunked diff --git a/Kernel/platform-sam/sam.s b/Kernel/platform-sam/sam.s new file mode 100644 index 00000000..a502c7f0 --- /dev/null +++ b/Kernel/platform-sam/sam.s @@ -0,0 +1,365 @@ +; +; SAM Coupe initial hardware support +; + + .module sam + + ; exported symbols + .globl init_early + .globl init_hardware + .globl interrupt_handler + .globl _program_vectors + .globl _kernel_flag + .globl map_page_low + .globl map_kernel_low + .globl map_user_low + .globl map_save_low + .globl map_restore_low + .globl map_video + .globl unmap_video + .globl _platform_doexec + .globl _platform_reboot + .globl _platform_copier_l + .globl _platform_copier_h + + ; exported debugging tools + .globl _platform_monitor + .globl outchar + + ; imported symbols + .globl _ramsize + .globl _procmem + .globl istack_top + .globl istack_switched_sp + .globl kstack_top + .globl unix_syscall_entry + .globl outcharhex + .globl _keyin + + .globl s__COMMONMEM + .globl l__COMMONMEM + + .include "kernel.def" + .include "../kernel.def" + +KERNEL_HIGH .equ 0 +KERNEL_LOW .equ 2 ; 0/1 high 2/3 low +VIDEO_LOW .equ 4 + +; ----------------------------------------------------------------------------- +; +; Because of the 32K split our memory model is a bit different to the +; usual Z80 setup. +; +; ----------------------------------------------------------------------------- + .area _COMMONMEM + +_platform_monitor: + di + halt ; NMI button will cause a reboot.. +_platform_reboot: + xor a + out (250), a ; ROM appears low + rst 0 ; bang + +; ----------------------------------------------------------------------------- +; KERNEL MEMORY BANK (below common, only accessible when the kernel is mapped) +; ----------------------------------------------------------------------------- + .area _DISCARD + +; colours - FIXME correct these + .db 0,32,64,120 +clutmap: +init_early: + ld a, #0x44 ; Kernel is in 0/1 2/3, so video goes above it + ; Video mode 3 (with luck) + out (252), a + ld a, #0x10 ; black border, mic 1 + out (254), a + in a, (251) + and #0x9F ; low palette colours + ld hl, #clutmap + ld bc, #4*256 + 248 ; 4 colours, port 248 + otdr + ret + +init_hardware: + ; set system RAM size (FIXME - check for 512/1M) + ld hl, #256 + ld (_ramsize), hl + ld hl, #(256-64) ; 64K for kernel + ld (_procmem), hl + + ; set up interrupt vectors for the kernel (also sets up common memory in page 0x000F which is unused) + ld hl, #0 + push hl + call _program_vectors + pop hl + + im 1 ; set CPU interrupt mode + + ; interrupt mask + ; 60Hz timer on + + ret + + .area _HIGH + +_kernel_flag: + .db 1 ; We start in kernel mode +low_map: + .db KERNEL_LOW +low_save: + .db 0 +video_save: + .db 0 + +map_save_low: + push af + ld a,(low_map) + ld (low_save),a + pop af + ; Fall through as we also map the kernel +map_kernel_low: + push af + ld a,#KERNEL_LOW + ld (low_map),a + out (250),a + pop af + ret +map_restore_low: + ld a, (low_save) + jr map_page_low +map_user_low: + ld a,(U_DATA__U_PAGE) +map_page_low: + ld (low_map),a + out (250),a + ret +map_video: + ld a,(low_map) + ld (video_save),a + ld a,#VIDEO_LOW + jr map_page_low +unmap_video: + ld a,(video_save) + jr map_page_low + +_program_vectors: + ; we are called, with interrupts disabled, by both newproc() and crt0 + ; will exit with interrupts off + di ; just to be sure + pop de ; temporarily store return address + pop hl ; function argument -- base page number + push hl ; put stack back as it was + push de + + ld a,(hl) ; get the low page + call map_page_low + + ; Copy the stub page into place + ld hl, #stub_page + ld de, #0x0000 + ld bc, #0x0100 + ldir + jp map_kernel_low + +; outchar: Wait for UART TX idle, then print the char in A +; destroys: AF +; +; We use the MIDI port for debug - it's not useful for much else after all. +; +outchar: + push af +outcw: in a,(248) + bit 1,a + jr nz, outcw + pop af + out (253),a + ret +; +; Keyboard scanner. This is easiest kept in asm space +; (reference code from the technical manual) +; +_keyscan: + ld hl, #_keyin + ld b, #0xFE +keyscl: ld c, #249 ; need BC out to get address lines + in a, (c) + and #0xE0 ; top 3 bits valid + ld d, a + ld c, #254 + in a, (c) + and #0x1F ; low 5 bits + or d + ld (hl), a + inc hl + scf + rlc b + jr c, keyscl + in a, (c) + and #0x1F + ld (hl), a + ret + + .area _PAGE0 +; +; Loaded into 00-FF in the bottom of the kernel and then into other +; pages. There is an argument for putting most of this at the top of +; the high page of everything. That might allow us to run CP/M +; emulation +; +; Move what we can into PAGEH and high kernel code +; +; We'd still need some low space but we have 0A-3A/3B-4F/57-5B +; +; We can't deal with NMI in the CP/M case though. +; +; Starts at address 1 to avoid an SDCC flaw +; +stub_page: + +stub0: .word 0 ; cp/m emu changes this + .byte 0 ; cp/m emu I/O byte + .byte 0 ; cp/m emu drive and user + jp 0 ; cp/m emu bdos entry point +rst8: +_platform_copier_l: + ld a,(hl) + out (251),a + exx + ldir + exx + nop ; fall through +rst10: ld a,#KERNEL_HIGH + out (251),a + ret + nop + nop + nop +rst18: +_platform_doexec: + out (251),a ; FIXME: trashing of clut high bits + ei + jp (hl) + nop +rst20: nop + nop + nop + nop + nop + nop + nop + nop +rst28: jp syscall_path + nop + nop + nop + nop + nop + nop + nop +rst30: nop + nop + nop + nop + nop + nop + nop + nop +rst38: ; Interrupt handling stub + push af + push de + push hl + ; FIXME: this trashes any clut overrides + ld a,#KERNEL_HIGH + out (251),a + ; Kernel is now the high map. Our stack may be invalid + ; so be careful + ld (istack_switched_sp),sp + ld sp,#istack_top + call interrupt_handler + ; On return HL = signal vector E= signal (if any) A = page for + ; high + ; FIXME 0 = kernel do we need to xlate ? + out (251),a + ; stack is invalid again now + ld sp,(istack_switched_sp) + ; back on user stack + xor a + cp e + call nz, sigpath + pop hl + pop de + pop af + ei + ret + nop + nop + nop +nmi_handler: ; FIXME: check ends up at 0066 + retn +sigpath: + push de ; signal number + ld de,#irqsigret + push de ; clean up + jp (hl) +irqsigret: + inc sp ; drop signal number + inc sp + ret + +syscall_path: + push ix + ;TODO : BC/DE/HL/IX off stack + ; FIXME trashes the clut bits + ld a,#KERNEL_HIGH + di + out (251),a + ; Stack now invalid + ld (U_DATA__U_SYSCALL_SP),sp + ld sp,#kstack_top + call unix_syscall_entry + ; FIXME check di rules + ; On return A is the page (0 means kernel), + ; FIXME: do we need to xlate + out (251),a + ; stack now invalid + ld sp,(U_DATA__U_SYSCALL_SP) + xor a + cp h + call nz, syscall_sigret + ; FIXME for now do the grungy C flag HL DE stuff from + ; lowlevel-z80 until we fix the ABI + ld bc,#0 + pop ix + ret +syscall_sigret: + push hl ; save errno + push de ; save retval + ld l,h + ld h,#0 + push hl ; signal + ld hl,#syscall_sighelp + push bc ; vector + ret +syscall_sighelp: + pop de ; discard signal + pop de ; recover error info + pop hl + ld h,#0 ; clear signal bit + ret + + .area _PAGEH +; +; High stubs. Present in each bank +; +_platform_copier_h: + ld a,(hl) + out (251),a + exx + ldir + exx + ld a,#KERNEL_HIGH + out (251),a + ret diff --git a/Kernel/platform-sam/sam_vt.s b/Kernel/platform-sam/sam_vt.s new file mode 100644 index 00000000..72a509ad --- /dev/null +++ b/Kernel/platform-sam/sam_vt.s @@ -0,0 +1,186 @@ +; +; Video layer for the SAM. For the moment we use a fairly primitve +; set up. We'd really like a mono hi-res mode but you don't get one +; +; Our display consists of 192 lines of 128 bytes (24K) and is page +; aligned. +; +; We really want to do 80 column 6bit chars but start simple +; +; Our video is 0 based so we don't add an offset +; Must preserve BC +; + + .module sam_vt + + .area _VIDEO + + .globl _scroll_up + .globl _scroll_down + .globl _plot_char + .globl _clear_lines + .globl _clear_across + + .globl _cursor_on + .globl _cursor_off + .globl _cursor_disable + + .globl _vtattr_notify + .globl _vtattr_cap + + .globl map_video + .globl unmap_video + .globl ___hard_di + .globl _fontdata_8x8_exp2 + +VIDEO_PAGE .equ 4 + +_vtattr_cap: + .byte 0 ; for now- we can add funky effects later + +base_addr: + ld a,d ; save X + ld d,e ; 256 * Y + ld e,#0 + srl d + srl e ; this is DE = E * 128 + ; A is a char 0-63 - need a byte 0-126 + ; Aligned so no carry issues + add a + add e + ld e,a + ret + +; +; This is awkward. We can't map the video in 0000-7FFF without +; interrupts off, and we can't map it high without confusing the +; hell out of everything else. For now just di. 32K/32K splits suck +; +_scroll_up: + call ___hard_di + push af + call map_video + ld hl,#128 + ld de,#0 + ld bc,#24576-128 +ldir_pop: + ldir +pop_unmap: + call unmap_video + pop af + ret c + ei + ret + +_scroll_down: + call ___hard_di + push af + call map_video + ld hl,#24576 + ld de,#24576-128 + ld b,d + ld c,e + lddr + jr pop_unmap + +_plot_char: + pop hl ; return + pop de ; H = X L = Y + pop bc ; C = char + push bc + push de + push hl + call ___hard_di + push af + call base_addr + ld h,#0 + ld l,c + add hl,hl ; char * 16 (expanded 8 x 8) + add hl,hl + add hl,hl + add hl,hl + ld bc,#_fontdata_8x8_exp2 ; plus font base + add hl,bc + call map_video + ld b,#20 ; it gets decremented by 4 by the ldi's + ; and once by djnz, and we need to do it + ; four times +plot_loop: + ldi ; copy expanded char + ldi + dec e + dec e + set 7,e ; de += 126 + ldi ; copy second char row + ldi + dec e ; Similar idea + dec e ; toggle the bit back and gives us + res 7,e + inc d ; de += 126 + djnz plot_loop + jr pop_unmap + +_clear_lines: + pop hl + pop de + push de + push hl + call ___hard_di + push af + ld a,d ; count (0-23) + ld d,#0 + ; Now how much to copy ? + ; 128 bytes per scan line, 8 scan lines per char = 1024 + add a,a ; x 2 + add a,a ; x 4 + ld b,a ; x 1024 + ld c,d ; d is 0 + call base_addr + call map_video + ld h,d + ld e,l + inc de + dec bc + ld (hl),#0 + jr ldir_pop + +_clear_across: + pop hl + pop de ; DE = X/Y + pop bc ; C = count + push bc + push de + push hl + + call ___hard_di + push af + + call base_addr + call map_video + + xor a + ld l,#4 ; 4 line pairs + ld h,e ; save offset start +wipe_line: + ld b,c +wipe_rowpair: + ld (de),a + set 7,e + ld (de),a + res 7,e + inc de + djnz wipe_rowpair + ld e,h ; restore offset. As we are 256 byte aligned we don't need + inc d ; to worry about carries just restore low, inc high + dec l + jr nz, wipe_line + jp pop_unmap + +; +; TODO +; +_cursor_on: +_cursor_off: +_cursor_disable: +_vtattr_notify: + ret diff --git a/Kernel/platform-sam/target.mk b/Kernel/platform-sam/target.mk new file mode 100644 index 00000000..3bffcde0 --- /dev/null +++ b/Kernel/platform-sam/target.mk @@ -0,0 +1 @@ +export CPU = z80 diff --git a/Kernel/platform-sam/tricks.s b/Kernel/platform-sam/tricks.s new file mode 100644 index 00000000..dbe2770d --- /dev/null +++ b/Kernel/platform-sam/tricks.s @@ -0,0 +1,239 @@ + + .include "../kernel.def" + .include "kernel.def" + +;FIXME .include "../lib/z80fixedbank.s" + + .module tricks32 + + .globl _ptab_alloc + .globl _newproc + .globl _chksigs + .globl _getproc + .globl _runticks + .globl _platform_monitor + + .globl _platform_switchout + .globl _switchin + .globl _dofork + + .globl map_page_low + .globl map_kernel_low + + .globl outhl + .globl outstring + + .globl _platform_copier_l + .globl _platform_copier_h + + .area _HIGH + +; +; Switch out this process for another task. All the optimisation +; tricks have been done by switchout, so when we are called we +; really need to switch. +; +_platform_switchout: + di + ld hl,#0 + push hl ; stack a retcode of 0 (see fork) + push ix ; save registers + push iy + ld (U_DATA__U_SP), sp + + ; Map the to + ld a,(U_DATA__U_PAGE2) + call map_page_low + ld hl,#U_DATA + ld de,#U_DATA_STASH-0x8000 + ld bc,#U_DATA__TOTALSIZE + ldir + call map_kernel_low + ; switch the new process in + call _getproc + push hl + call _switchin + ; Should never be hit + call _platform_monitor + +_switchin: + di + pop bc ; return address (we never do) + pop de ; new process pointer + + ld hl,#P_TAB__P_PAGE2_OFFSET + add hl,de + ld a,(hl) ; our high page + + ; FIXME: add swap support + + ld hl,(U_DATA__U_PTAB) + or a + sbc hl,de + jr z, skip_copyback + + call map_page_low + + ; We are going to write our stack directly under us. No calls for + ; a moment + exx + ld hl,#U_DATA_STASH + ld de,#U_DATA + ld bc,#U_DATA__TOTALSIZE + ldir + exx + ld sp, (U_DATA__U_SP) + + ; Ok our stack is now valid + + call map_kernel_low + ld hl, (U_DATA__U_PTAB) + or a + sbc hl,de + jr nz, switchin_failed ; invalid u_data + +skip_copyback: + ld sp, (U_DATA__U_SP) + ld ix, (U_DATA__U_PTAB) + ld P_TAB__P_STATUS_OFFSET(ix), #P_RUNNING + ld a, P_TAB__P_PAGE_OFFSET(ix) + ld (U_DATA__U_PAGE),a + ld a, P_TAB__P_PAGE2_OFFSET(ix) + ld (U_DATA__U_PAGE2),a + ld hl,#0 + ld (_runticks), hl + ; Recover IX and IY, return value + pop ix + pop iy + pop hl + + ; if we pre-empted in an ISR IRQ's stay off, if not they get enabled + ld a, (U_DATA__U_ININTERRUPT) + or a + ret nz + ei + ret + +switchin_failed: + call outhl + ld hl, #bad_switch + call outstring + jp _platform_monitor + +fork_proc_ptr: + .dw 0 + +bad_switch: + .asciz '!SWITCH' + +_dofork: + di + pop de + pop hl ; HL is the new process ptab pointer + push hl + push de + + ld (fork_proc_ptr),hl + + ld de,#P_TAB__P_PID_OFFSET + add hl,de + ld a,(hl) + inc hl + ld h,(hl) + ld l,a + + ; Build a parent frame for switchin that has the child pid + ; on it + push hl + push ix + push iy + + ld (U_DATA__U_SP), sp + + ; Parent state built and in u_data + + call copy_process + + ; Low page is undefined at this point which is ok as we are + ; about to change it again + + ; Now put the u_data into the stash for the parent, then we can + ; modify it for the child + + ; Map parent + ld a,(U_DATA__U_PAGE) + call map_page_low + ; Copy udata + ld hl, #U_DATA + ld de, #U_DATA_STASH - 0x8000 + ld bc, #U_DATA__TOTALSIZE + ldir + ; Kernel back + call map_kernel_low + + pop bc ; discard stack frame we built + pop bc + pop bc + + ; Back into the kernel to finish the process creation + + ld hl,(fork_proc_ptr) + push hl + call _newproc + pop bc + + ; Clear runticks, and return 0 + ld hl,#0 + ld (_runticks),hl + ret + +; +; Process copying is tricky. We want to do a direct copy for speed +; but that requires mapping both banks and means +; 1. We can't put all of this routine in a common helper when we make one +; 2. We need interrupts off for now +; 3. We need a magic helper in the low page +; +; HL is the child ptab, udata is the parent +; +; The platform needs to provide a platform_copier method for each +; bank that copies low to high of all process, stubs and user data +; in that bank (basically a straight 32K copy) +; +; typically that would be +; +; ld a,(hl) +; out (whatever),a +; exx +; ldir +; exx +; ld a,#kernel +; out (whatever),a +; ret +; +; There have to be two of these - one in high stubs one in low because +; the high banks will both be mapped so the low one isn't available +; +copy_process: + ld de,#P_TAB__P_PAGE2_OFFSET + add hl,de + ld a,(U_DATA__U_PAGE) + call map_page_low + call setup_platform_copier + call _platform_copier_l + inc hl + inc hl + ld a,(U_DATA__U_PAGE2) + call map_page_low + call setup_platform_copier + call _platform_copier_h + ret + +setup_platform_copier: + exx + ld hl,#0 + ld de,#0x8000 + ld b,d + ld c,e + exx + ret \ No newline at end of file -- 2.34.1