From: Alan Cox Date: Mon, 3 Nov 2014 21:45:13 +0000 (+0000) Subject: msx2: Add an initial wild guess at what MSX2 will need X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=2746304e79bee01aaf6e88af543fb70acf990b90;p=FUZIX.git msx2: Add an initial wild guess at what MSX2 will need Hopefully that'll be a basis for someone who knows something about MSX2 to fill in the blanks. In particular my VDP code is a wild untested guess --- diff --git a/Kernel/Makefile b/Kernel/Makefile index b2f9d776..a6216902 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -1,6 +1,6 @@ TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-spec3 platform-trs80 platform-z80pack platform-z80pack-lite -export TARGET= nc100 +export TARGET= msx2 export CPU = z80 #export TARGET = 6809test #export CPU = 6809 diff --git a/Kernel/platform-msx2/Makefile b/Kernel/platform-msx2/Makefile new file mode 100644 index 00000000..eb8c11b2 --- /dev/null +++ b/Kernel/platform-msx2/Makefile @@ -0,0 +1,25 @@ + +CSRCS = devtty.c devfd.c devhd.c devlpr.c +CSRCS += devices.c main.c + +ASRCS = msx2.s crt0.s vdp.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-msx2/README b/Kernel/platform-msx2/README new file mode 100644 index 00000000..6df92ce9 --- /dev/null +++ b/Kernel/platform-msx2/README @@ -0,0 +1,4 @@ +First wild guess at an MSX2 target + +And it is exactly that a guess 8) + diff --git a/Kernel/platform-msx2/commonmem.s b/Kernel/platform-msx2/commonmem.s new file mode 100644 index 00000000..d3d30a88 --- /dev/null +++ b/Kernel/platform-msx2/commonmem.s @@ -0,0 +1,49 @@ +; +; Put the udata at the start of common. We have four 16K banks so we +; keep the non .common kernel elements below C000 and then keep bank 3 as a +; true common bank +; + .module commonmem + + ; exported symbols + .globl _ub + .globl _udata + .globl kstack_top + .globl istack_top + .globl istack_switched_sp + + .area _COMMONMEM + +_ub: ; first 512 bytes: starts with struct u_block, with the kernel stack working down from above +_udata: +kstack_base: + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +kstack_top: + + ; next 256 bytes: 254 byte interrupt stack, then 2 byte saved stack pointer +istack_base: + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +istack_top: +istack_switched_sp: .dw 0 diff --git a/Kernel/platform-msx2/config.h b/Kernel/platform-msx2/config.h new file mode 100644 index 00000000..abc04bd4 --- /dev/null +++ b/Kernel/platform-msx2/config.h @@ -0,0 +1,41 @@ +/* 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 - for now while we get it booting */ +#undef CONFIG_SINGLETASK +/* Video terminal, not a serial tty */ +#define CONFIG_VT +/* 16K banking so use the helper */ +#define CONFIG_BANK16 +#define MAX_MAPS 255 + +/* As reported to user space - 4 banks, 16K page size */ +#define CONFIG_BANKS 4 + +/* Vt definitions */ +#define VT_WIDTH 80 +#define VT_HEIGHT 24 +#define VT_RIGHT 79 +#define VT_BOTTOM 23 + +#define TICKSPERSEC 50 /* Ticks per second (actually should be dynamic FIXME) */ +#define PROGBASE ((char *)(0x0100)) /* also data base */ +#define PROGTOP ((char *)(0xF000)) /* Top of program, base of U_DATA */ + +#define BOOT_TTY (512 + 1) /* Set this to default device for stdio, stderr */ + /* In this case, the default is the first TTY device */ + /* Temp FIXME set to serial port for debug ease */ + +/* 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-msx2/crt0.s b/Kernel/platform-msx2/crt0.s new file mode 100644 index 00000000..dbab60fc --- /dev/null +++ b/Kernel/platform-msx2/crt0.s @@ -0,0 +1,59 @@ + ; Ordering of segments for the linker. + ; WRS: Note we list all our segments here, even though + ; we don't use them all, because their ordering is set + ; when they are first seen. + .area _CODE + .area _CODE2 + .area _VIDEO + .area _CONST + .area _DISCARD + .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 _COMMONMEM + + ; imported symbols + .globl _fuzix_main + .globl init_early + .globl init_hardware + .globl s__DATA + .globl l__DATA + .globl s__COMMONMEM + .globl l__COMMONMEM + .globl s__INITIALIZER + .globl kstack_top + + ; startup code @0 + .area _CODE + +; +; Execution begins with us correctly mapped and at 0x0x100 +; +start: di + ld sp, #kstack_top + ; move the common memory where it belongs + ld hl, #s__INITIALIZER + ld de, #s__COMMONMEM + ld bc, #l__COMMONMEM + 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-msx2/devfd.c b/Kernel/platform-msx2/devfd.c new file mode 100644 index 00000000..e3db9715 --- /dev/null +++ b/Kernel/platform-msx2/devfd.c @@ -0,0 +1,70 @@ +/* + * Dummy fd driver code + * + */ + +#include +#include +#include +#include + +static int fd_transfer(bool is_read, uint8_t rawflag) +{ + blkno_t block; + int block_xfer; + uint16_t dptr; + int dlen; + int ct = 0; + int map; + + is_read; + + /* FIXME: raw is broken unless nicely aligned */ + if(rawflag) { + dlen = udata.u_count; + dptr = (uint16_t)udata.u_base; + if (dptr & 0x1FF) { + udata.u_error = EIO; + return -1; + } + block = udata.u_offset >> 9; + block_xfer = dlen >> 9; + map = 1; + } else { /* rawflag == 0 */ + dlen = 512; + dptr = (uint16_t)udata.u_buf->bf_data; + block = udata.u_buf->bf_blk; + block_xfer = 1; + map = 0; + } + + while (ct < block_xfer) { + /* FIXME: Do stuff */ + block++; + ct++; + } + return ct; +} + +int fd_open(uint8_t minor, uint16_t flag) +{ + flag; + if(minor != 0) { + udata.u_error = ENODEV; + return -1; + } + return 0; +} + +int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return fd_transfer(true, rawflag); +} + +int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return fd_transfer(false, rawflag); +} + diff --git a/Kernel/platform-msx2/devfd.h b/Kernel/platform-msx2/devfd.h new file mode 100644 index 00000000..b59ff215 --- /dev/null +++ b/Kernel/platform-msx2/devfd.h @@ -0,0 +1,10 @@ +#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); + +#endif /* __DEVRD_DOT_H__ */ + diff --git a/Kernel/platform-msx2/devhd.c b/Kernel/platform-msx2/devhd.c new file mode 100644 index 00000000..16530faa --- /dev/null +++ b/Kernel/platform-msx2/devhd.c @@ -0,0 +1,70 @@ +/* + * Dummy hd driver code + * + */ + +#include +#include +#include +#include + +static int hd_transfer(bool is_read, uint8_t rawflag) +{ + blkno_t block; + int block_xfer; + uint16_t dptr; + int dlen; + int ct = 0; + int map; + + is_read; + + /* FIXME: raw is broken unless nicely aligned */ + if(rawflag) { + dlen = udata.u_count; + dptr = (uint16_t)udata.u_base; + if (dptr & 0x1FF) { + udata.u_error = EIO; + return -1; + } + block = udata.u_offset >> 9; + block_xfer = dlen >> 9; + map = 1; + } else { /* rawflag == 0 */ + dlen = 512; + dptr = (uint16_t)udata.u_buf->bf_data; + block = udata.u_buf->bf_blk; + block_xfer = 1; + map = 0; + } + + while (ct < block_xfer) { + /* FIXME: Do stuff */ + block++; + ct++; + } + return ct; +} + +int hd_open(uint8_t minor, uint16_t flag) +{ + flag; + if(minor != 0) { + udata.u_error = ENODEV; + return -1; + } + return 0; +} + +int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return hd_transfer(true, rawflag); +} + +int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return hd_transfer(false, rawflag); +} + diff --git a/Kernel/platform-msx2/devhd.h b/Kernel/platform-msx2/devhd.h new file mode 100644 index 00000000..cdb55de5 --- /dev/null +++ b/Kernel/platform-msx2/devhd.h @@ -0,0 +1,10 @@ +#ifndef __DEVHD_DOT_H__ +#define __DEVHD_DOT_H__ + +/* public interface */ +int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag); +int hd_open(uint8_t minor, uint16_t flag); + +#endif /* __DEVRD_DOT_H__ */ + diff --git a/Kernel/platform-msx2/devices.c b/Kernel/platform-msx2/devices.c new file mode 100644 index 00000000..b5805f17 --- /dev/null +++ b/Kernel/platform-msx2/devices.c @@ -0,0 +1,38 @@ +#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 */ + { fd_open, no_close, fd_read, fd_write, no_ioctl }, + /* 1: /dev/hd Hard disc block devices (and RAM etc) */ + { hd_open, no_close, hd_read, hd_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, no_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) + 255) + return false; + else + return true; +} + +void device_init(void) +{ +} diff --git a/Kernel/platform-msx2/devlpr.c b/Kernel/platform-msx2/devlpr.c new file mode 100644 index 00000000..6ca6cef9 --- /dev/null +++ b/Kernel/platform-msx2/devlpr.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +__sfr __at 0x90 lpstat; +__sfr __at 0x91 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; + uint16_t ct; + + minor; + rawflag; + flag; // shut up compiler + + while (c-- > 0) { + ct = 0; + + /* Try and balance polling and sleeping */ + while (lpstat & 2) { + ct++; + if (ct == 10000) { + udata.u_ptab->p_timeout = 3; + if (psleep_flags(NULL, flag)) { + if (udata.u_count) + udata.u_error = 0; + return udata.u_count; + } + ct = 0; + } + } + /* Data */ + lpdata = ugetc(p++); + /* Strobe */ + lpstat |= 1; + lpstat &= ~1; + } + return udata.u_count; +} diff --git a/Kernel/platform-msx2/devlpr.h b/Kernel/platform-msx2/devlpr.h new file mode 100644 index 00000000..7765c187 --- /dev/null +++ b/Kernel/platform-msx2/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-msx2/devtty.c b/Kernel/platform-msx2/devtty.c new file mode 100644 index 00000000..75af1dfb --- /dev/null +++ b/Kernel/platform-msx2/devtty.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG /* UNdefine to delete debug code sequences */ + +__sfr __at 0x2F tty_debug2; + +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} +}; + +/* tty1 is the screen tty2 is the debug port */ + +/* Output for the system console (kprintf etc) */ +void kputchar(char c) +{ + /* Debug port for bringup */ + if (c == '\n') + tty_putc(2, '\r'); + tty_putc(2, c); +} + +/* Both console and debug port are always ready */ +bool tty_writeready(uint8_t minor) +{ + minor; + return 1; +} + +void tty_putc(uint8_t minor, unsigned char c) +{ + minor; + if (minor == 1) { + vtoutput(&c, 1); + return; + } + tty_debug2 = c; +} + +int tty_carrier(uint8_t minor) +{ + minor; + return 1; +} + +void tty_setup(uint8_t minor) +{ + minor; +} + + +#if 0 +static uint8_t keymap[10]; +static uint8_t keyin[10]; +static uint8_t keybyte, keybit; +static uint8_t newkey; +static int keysdown = 0; +static uint8_t shiftmask[10] = { + 3, 3, 2, 0, 0, 0, 0, 0x10, 0, 0 +}; + +static void keyproc(void) +{ + int i; + uint8_t key; + + for (i = 0; i < 10; i++) { + key = keyin[i] ^ keymap[i]; + if (key) { + int n; + int m = 128; + 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; + } + + } + } + keymap[i] = keyin[i]; + } +} + +static uint8_t keyboard[10][8] = { + {0, 0, 0, 10, '?' /*left */ , 0, 0, 0}, + {0, '5', 0, 0, ' ', 27, 0, 0}, + {0, 0, 0, 0, '\t', '1', 0, 0}, + {'d', 's', 0, 'e', 'w', 'q', '2', '3'}, + {'f', 'r', 0, 'a', 'x', 'z', 0, '4'}, + {'c', 'g', 'y', 't', 'v', 'b', 0, 0}, + {'n', 'h', '/', '#', '?' /*right */ , 127, '?' /*down */ , '6'}, + {'k', 'm', 'u', 0, '?' /*up */ , '\\', '7', '='}, + {',', 'j', 'i', '\'', '[', ']', '-', '8'}, + {'.', 'o', 'l', ';', 'p', 8, '9', '0'} +}; + +static uint8_t shiftkeyboard[10][8] = { + {0, 0, 0, 10, '?' /*left */ , 0, 0, 0}, + {0, '%', 0, 0, ' ', 3, 0, 0}, + {0, 0, 0, 0, '\t', '!', 0, 0}, + {'D', 'S', 0, 'E', 'W', 'Q', '"', '?' /* pound */ }, + {'F', 'R', 0, 'A', 'X', 'Z', 0, '$'}, + {'C', 'G', 'Y', 'T', 'V', 'B', 0, 0}, + {'N', 'H', '?', '~', '?' /*right */ , 127, '?' /*down */ , '^'}, + {'K', 'M', 'U', 0, '?' /*up */ , '|', '&', '+'}, + {'<', 'J', 'I', '@', '{', '}', '_', '*'}, + {'>', 'O', 'L', ':', 'P', 8, '(', ')'} +}; + +static uint8_t capslock = 0; + +static void keydecode(void) +{ + uint8_t c; + + if (keybyte == 2 && keybit == 7) { + capslock = 1 - capslock; + return; + } + + if (keymap[0] & 3) /* shift */ + c = shiftkeyboard[keybyte][keybit]; + else + c = keyboard[keybyte][keybit]; + if (keymap[1] & 2) { /* control */ + if (c > 31 && c < 96) + c &= 31; + } + if (keymap[1] & 1) { /* function: not yet used */ + ; + } +// kprintf("char code %d\n", c); + if (keymap[2] & 1) { /* symbol */ + ; + } + if (capslock && c >= 'a' && c <= 'z') + c -= 'a' - 'A'; + if (keymap[7] & 0x10) { /* menu: not yet used */ + ; + } + tty_inproc(1, c); +} + +#endif + +void platform_interrupt(void) +{ +#if 0 + uint8_t a = irqmap; + uint8_t c; + if (!(a & 2)) + wakeup(&ttydata[2]); + if (!(a & 1)) { + /* work around sdcc bug */ + c = uarta; + tty_inproc(2, c); + } + if (!(a & 8)) { + keyin[0] = kmap0; + keyin[1] = kmap1; + keyin[2] = kmap2; + keyin[3] = kmap3; + keyin[4] = kmap4; + keyin[5] = kmap5; + keyin[6] = kmap6; + keyin[7] = kmap7; + keyin[8] = kmap8; + keyin[9] = kmap9; /* This resets the scan for 10mS on */ + + newkey = 0; + keyproc(); + if (keysdown < 3 && newkey) + keydecode(); + timer_interrupt(); + } + + /* clear the mask */ + irqmap = a; +#endif +} + +/* This is used by the vt asm code, but needs to live in the kernel */ +uint16_t cursorpos; + diff --git a/Kernel/platform-msx2/devtty.h b/Kernel/platform-msx2/devtty.h new file mode 100644 index 00000000..c387e904 --- /dev/null +++ b/Kernel/platform-msx2/devtty.h @@ -0,0 +1,4 @@ +#ifndef __DEVTTY_DOT_H__ +#define __DEVTTY_DOT_H__ + +#endif diff --git a/Kernel/platform-msx2/kernel.def b/Kernel/platform-msx2/kernel.def new file mode 100644 index 00000000..1189bbee --- /dev/null +++ b/Kernel/platform-msx2/kernel.def @@ -0,0 +1,5 @@ +; UZI mnemonics for memory addresses etc + +U_DATA .equ 0xF000 ; (this is struct u_data from kernel.h) +U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes. + diff --git a/Kernel/platform-msx2/main.c b/Kernel/platform-msx2/main.c new file mode 100644 index 00000000..57c2b4b2 --- /dev/null +++ b/Kernel/platform-msx2/main.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +uint16_t vdpport = 0x98; + +void platform_idle(void) +{ + __asm + halt + __endasm; +} + +void do_beep(void) +{ +} + +/* + * Map handling: We have flexible paging. Each map table consists of a set + * of pages with the last page repeated to fill any holes. + */ + +void pagemap_init(void) +{ + int i = /*FIXME*/ 16; /* in 16K banks */ + /* Add all the RAM, except 0,1,2 , which is the kernel data/bss, add 3 + last to become the common for init */ + while (i > 0) + pagemap_add(i--); +} + +void map_init(void) +{ +} diff --git a/Kernel/platform-msx2/msx2.s b/Kernel/platform-msx2/msx2.s new file mode 100644 index 00000000..1609ecb7 --- /dev/null +++ b/Kernel/platform-msx2/msx2.s @@ -0,0 +1,226 @@ +; +; MSX2 hardware support +; + + .module msx2 + + ; exported symbols + .globl init_early + .globl init_hardware + .globl interrupt_handler + .globl _program_vectors + .globl map_kernel + .globl map_process + .globl map_process_always + .globl map_save + .globl map_restore + + ; video driver + .globl _vtinit + + ; exported debugging tools + .globl _trap_monitor + .globl outchar + + ; imported symbols + .globl _ramsize + .globl _procmem + .globl _tty_inproc + .globl unix_syscall_entry + .globl trap_illegal + .globl nmi_handler + .globl null_handler + + ; debug symbols + .globl outcharhex + .globl outhl, outde, outbc + .globl outnewline + .globl outstring + .globl outstringhex + + .include "kernel.def" + .include "../kernel.def" + +; ----------------------------------------------------------------------------- +; COMMON MEMORY BANK (0xF000 upwards) +; ----------------------------------------------------------------------------- + .area _COMMONMEM + +trapmsg: .ascii "Trapdoor: SP=" + .db 0 +trapmsg2: .ascii ", PC=" + .db 0 +tm_user_sp: .dw 0 + +tm_stack: + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +tm_stack_top: + +; Ideally return to any debugger/monitor +_trap_monitor: + di + halt + + +_trap_reboot: +;FIXME: TODO + di + halt + +; ----------------------------------------------------------------------------- +; KERNEL MEMORY BANK (below 0xF000, only accessible when the kernel is mapped) +; ----------------------------------------------------------------------------- + .area _CODE + +init_early: + ld a, #0x33 ; multibyte/ascii + out (0x2E), a + ret + +init_hardware: + ; set up interrupt vectors for the kernel mapped low page and + ; data area + ld hl, #0 + push hl + call _program_vectors + pop hl + + im 1 ; set CPU interrupt mode + call _vtinit ; init the console video + ret + + +;------------------------------------------------------------------------------ +; COMMON MEMORY PROCEDURES FOLLOW + + .area _COMMONMEM + +_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 + + ; At this point the common block has already been copied + call map_process + + ; write zeroes across all vectors + ld hl, #0 + ld de, #1 + ld bc, #0x007f ; program first 0x80 bytes only + ld (hl), #0x00 + ldir + + ; now install the interrupt vector at 0x0038 + ld a, #0xC3 ; JP instruction + ld (0x0038), a + ld hl, #interrupt_handler + ld (0x0039), hl + + ; set restart vector for Fuzix system calls + ld (0x0030), a ; (rst 30h is unix function call vector) + ld hl, #unix_syscall_entry + ld (0x0031), hl + + ld (0x0000), a + ld hl, #null_handler ; to Our Trap Handler + ld (0x0001), hl + + ld (0x0066), a ; Set vector for NMI + ld hl, #nmi_handler + ld (0x0067), hl + jr map_kernel + +; +; All registers preserved +; +map_process_always: + push hl + push af + ld hl, #U_DATA__U_PAGE + call map_process_2 + pop af + pop hl + ret +; +; HL is the page table to use, A is eaten, HL is eaten +; +map_process: + ld a, h + or l + jr nz, map_process_2 +; +; Map in the kernel below the current common, all registers preserved +; This maps 0-3 but I guess we should save the map from the boot +; somehow and use that? +; +map_kernel: + push af + xor a + out (0xFC), a + inc a + out (0xFD), a + inc a + out (0xFE), a + inc a + out (0xFF), a + pop af + ret +map_process_2: + push de + ld de, #map_table ; Write only so cache in RAM + ld a, (hl) + ld (de), a + out (0xFC), a ; Low 16K + inc hl + inc de + ld a, (hl) + out (0xFD), a ; Next 16K + ld (de), a + inc hl + inc de + ld a, (hl) ; Next 16K. Leave the common for the task + out (0xFE), a ; switcher + ld (de), a + pop de + ; NOTE: map_restore relies on the HL for + ; exit of this + ret +; +; Restore a saved mapping. We are guaranteed that we won't switch +; common copy between save and restore. Preserve all registers +; +map_restore: + push hl + push af + ld hl,#map_savearea + call map_process_2 ; Put the mapper back right + pop af + pop hl + ret +; +; Save the current mapping. +; +map_save: push hl + ld hl, (map_table) + ld (map_savearea), hl + ld hl, (map_table + 2) + ld (map_savearea + 2), hl + pop hl + ret + +map_table: + .db 0,0,0,0 +map_savearea: + .db 0,0,0,0 + +; emulator debug port for now +outchar: + push af +outcharw: + out (0x2F), a + ret + diff --git a/Kernel/platform-msx2/tricks.s b/Kernel/platform-msx2/tricks.s new file mode 100644 index 00000000..6a170fa2 --- /dev/null +++ b/Kernel/platform-msx2/tricks.s @@ -0,0 +1,247 @@ +; 2013-12-21 William R Sowerbutts + + .module tricks + + .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 dispatch_process_signal + .globl map_kernel + .globl _ramtop + + ; imported debug symbols + .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex + + .include "kernel.def" + .include "../kernel.def" + + .area _COMMONMEM + +; ramtop must be in common for single process swapping cases +; and its a constant for the others from before init forks so it'll be fine +; here +_ramtop: + .dw 0 + +; Switchout switches out the current process, finds another that is READY, +; possibly the same process, and switches it in. When a process is +; restarted after calling switchout, it thinks it has just returned +; from switchout(). +; +; This function can have no arguments or auto variables. +_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 + +_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 + + ld hl, #P_TAB__P_PAGE_OFFSET+3 ; Common + add hl, de ; process ptr + ld a, (hl) + out (0xFF), a ; *CAUTION* our stack just left the building + + ; ------- No stack ------- + ; 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 hl, #P_TAB__P_STATUS_OFFSET + add hl, de + ld (hl), #P_RUNNING + + ; 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) + + ; ---- New task stack ---- + + ld hl, #0 + add hl, 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: + ; This will crap somewhere random in the stack space of the failure + ; case. We aren't coming back, it doesn't matter + 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. + + ; --------- we switch stack copies in this call ----------- + call fork_copy ; copy 0x0000 to udata.u_top and the + ; uarea and return on the childs + ; common + ; We are now in the kernel child context + + ; 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 + + ; The child makes its own new process table entry, etc. + ld hl, (fork_proc_ptr) + push hl + call _newproc + pop bc + + ; any calls to map process will now map the childs memory + + ; 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 + +; +; Interrupts need to be off here (if not we need to fix map_save +; and map_restore +; +fork_copy: + ld hl, (U_DATA__U_TOP) + ld de, #0x0fff + add hl, de ; + 0x1000 (-1 for the rounding to follow) + ld a, h + rlca + rlca ; get just the number of banks in the bottom + ; bits + and #3 + inc a ; and round up to the next bank + ld b, a + ; we need to copy the relevant chunks + ld hl, (fork_proc_ptr) + ld de, #P_TAB__P_PAGE_OFFSET + add hl, de + ; hl now points into the child pages + ld de, #U_DATA__U_PAGE + ; and de is the parent +fork_next: + ld a,(hl) + out (0xFD), a ; 0x4000 map the child + ld c, a + inc hl + ld a, (de) + out (0xFE), a ; 0x8000 maps the parent + inc de + exx + ld hl, #0x8000 ; copy the bank + ld de, #0x4000 + ld bc, #0x4000 ; we copy the whole bank, we could optimise + ; further + ldir + exx + call map_kernel ; put the maps back so we can look in p_tab + djnz fork_next + ld a, c + out (0xFF), a ; our last bank repeats up to common + ; --- we are now on the stack copy, parent stack is locked away --- + ret ; this stack is copied so safe to return on + + diff --git a/Kernel/platform-msx2/uzi.lnk b/Kernel/platform-msx2/uzi.lnk new file mode 100644 index 00000000..f504ebd9 --- /dev/null +++ b/Kernel/platform-msx2/uzi.lnk @@ -0,0 +1,39 @@ +-mwxuy +-i uzi.ihx +-b _CODE=0x0100 +-b _COMMONMEM=0xF000 +-k /usr/share/sdcc/lib/z80 +-l z80 +platform-msx2/crt0.rel +platform-msx2/commonmem.rel +platform-msx2/msx2.rel +platform-msx2/vdp.rel +start.rel +version.rel +lowlevel-z80.rel +platform-msx2/tricks.rel +platform-msx2/main.rel +timer.rel +kdata.rel +platform-msx2/devtty.rel +platform-msx2/devfd.rel +platform-msx2/devhd.rel +platform-msx2/devlpr.rel +platform-msx2/devices.rel +devio.rel +filesys.rel +process.rel +inode.rel +syscall_fs.rel +syscall_fs2.rel +syscall_proc.rel +syscall_other.rel +mm.rel +swap.rel +bank16k.rel +tty.rel +vt.rel +devsys.rel +usermem.rel +usermem_std-z80.rel +-e diff --git a/Kernel/platform-msx2/vdp.s b/Kernel/platform-msx2/vdp.s new file mode 100644 index 00000000..77bac44c --- /dev/null +++ b/Kernel/platform-msx2/vdp.s @@ -0,0 +1,227 @@ + .module vdp + + .include "kernel.def" + .include "../kernel.def" + + ; video driver + .globl _scroll_up + .globl _scroll_down + .globl _plot_char + .globl _clear_lines + .globl _clear_across + .globl _cursor_on + .globl _cursor_off + .globl _cursorpos + + .globl _vdpport + + .area _CODE +; +; Register write value E to register A +; +vdpout: ld bc, (_vdpport) + out (c), e + out (c), d + ret + +; +; FIXME: table.. and move into init ROM +; +vdpinit: ld de, #0x8004 ; M4 } + call vdpout ; } 80x25 mode + ld de, #0x8170 ; M1 } screen on, virq on hirq off + call vdpout + ld de, #0x8200 ; characters at VRAM 0 + call vdpout + ld de, #0x8320 ; blink at 0x800 + ld de, #0x8402 ; font at 0x1000 + call vdpout + ld de, #0x870F ; white text on black + call vdpout + ; FIXME: read reg 9 and set bit 7 to 0 + ld de, #0x8C00 ; vanish for blink + call vdpout + ld de, #0x8D33 ; blink time + call vdpout + ld de, #0x8A00 ; zero high bits of blink + call vdpout + ld de, #0x8F00 ; and we want status register 0 visible + call vdpout + ld de, #0x8E00 ; we only look at the low area + call vdpout + ; R45 - banking off ??? + ; Wipe ram ? + ret + +; +; FIXME: need to IRQ protect the pairs of writes +; + + +_videopos: ; turn E=Y D=X into HL = addr + ; pass B = 0x40 if writing + ; preserves C + ld a, e ; 0-24 Y + add a, a + add a, a + add a, a ; x 8 + ld l, a + ld h, #0 + add hl, hl ; x 16 + push hl + add hl, hl + add hl, hl ; x 64 + ld a, e + pop de + add hl, de ; x 80 + ld l, a + ld h, b ; 0 for read 0x40 for write + add hl, de ; + X + ret +; +; Eww.. wonder if VT should provide a hint that its the 'next char' +; +_plot_char: pop hl + pop de ; D = x E = y + pop bc + push bc + push de + push hl +plotit: + ld b, #0x40 ; writing + call _videopos + ld a, c + ld bc, (_vdpport) + out (c), l ; address + out (c), h ; address | 0x40 + out (c), a ; character + ret + +; +; FIXME: on the VDP9958 we can set R25 bit 6 and use the GPU +; operations +; +; +; Painful +; + .area _DATA + +scrollbuf: .ds 80 + + .area _CODE +; +; We don't yet use attributes... +; +_scroll_down: + ld b, #23 + ld de, #0x730 ; start of bottom line +upline: + push bc + ld bc, (_vdpport) ; vdpport + 1 always holds #80 + ld hl, #scrollbuf + out (c), e ; our position + out (c), d + inir ; safe on MSX2 but not MSX1 + ld hl, #0x4080 ; go down one line and into write mode + add hl, de ; relative to our position + out (c), l + out (c), h + ld b, #0x80 + ld hl, #scrollbuf + otir ; video ptr is to the line below so keep going + pop bc ; recover line counter + ld hl, #0xffb0 + add hl, de ; up 80 bytes + ex de, hl ; and back into DE + djnz upline + ret + +_scroll_up: + ld b, #23 + ld de, #80 ; start of second line +downline: push bc + ld bc, (_vdpport) + ld hl, #scrollbuf + out (c), e + out (c), d + inir + ld hl, #0x3FB0 ; up 80 bytes in the low 12 bits, add 0x40 + ; for write ( we will carry one into the top + ; nybble) + add hl, de + out (c), l + out (c), h + ld hl, #scrollbuf + ld b, #0x80 + otir + pop bc + ld hl, #80 + add hl, de + ex de, hl + djnz downline + ret + +_clear_lines: + pop hl + pop de ; E = line, D = count + push de + push hl + ld c, d + ld d, #0 + call _videopos + ld e, c + ld bc, (_vdpport) + out (c), l + out (c), h + ; Safe on MSX 2 to loop the data with IRQ on + ; but *not* on MSX 1 + ld a, #' ' +l2: ld b, #80 +l1: out (c), a ; Inner loop clears a line, outer counts + ; need 20 clocks between writes. DJNZ is 13, + ; out is 11 + djnz l1 + dec e + jr nz, l2 + ret + +_clear_across: + pop hl + pop de ; DE = coords + pop bc ; C = count + push bc + push de + push hl + call _videopos + ld a, c + ld bc, (_vdpport) + out (c), l + out (c), h + ld b, a + ld a, #' ' +l3: out (c), a + djnz l1 + ret + +; +; FIXME: should use attribute blink flag not a char +; +_cursor_on: + pop hl + pop de + push de + push hl + ld (cursorpos), de + ld c, #'_' + call plotit + ret +_cursor_off: + ld de, (cursorpos) + ld c, #' ' + call plotit + ret +; +; This must be in data or common not code +; + .area _DATA +cursorpos: .dw 0