msx1: Add the initial bits
authorAlan Cox <alan@etchedpixels.co.uk>
Thu, 6 Nov 2014 22:22:54 +0000 (22:22 +0000)
committerAlan Cox <alan@etchedpixels.co.uk>
Thu, 6 Nov 2014 22:22:54 +0000 (22:22 +0000)
This is basically in parallel with MSX2 but with the various memory manager
bits stripped out (new one needed) and 40 char vdp code

19 files changed:
Kernel/platform-msx1/Makefile [new file with mode: 0644]
Kernel/platform-msx1/bootrom.s [new file with mode: 0644]
Kernel/platform-msx1/commonmem.s [new file with mode: 0644]
Kernel/platform-msx1/config.h [new file with mode: 0644]
Kernel/platform-msx1/crt0.s [new file with mode: 0644]
Kernel/platform-msx1/devfd.c [new file with mode: 0644]
Kernel/platform-msx1/devfd.h [new file with mode: 0644]
Kernel/platform-msx1/devhd.c [new file with mode: 0644]
Kernel/platform-msx1/devhd.h [new file with mode: 0644]
Kernel/platform-msx1/devices.c [new file with mode: 0644]
Kernel/platform-msx1/devlpr.c [new file with mode: 0644]
Kernel/platform-msx1/devlpr.h [new file with mode: 0644]
Kernel/platform-msx1/devtty.c [new file with mode: 0644]
Kernel/platform-msx1/devtty.h [new file with mode: 0644]
Kernel/platform-msx1/main.c [new file with mode: 0644]
Kernel/platform-msx1/msx1.s [new file with mode: 0644]
Kernel/platform-msx1/tricks.s [new file with mode: 0644]
Kernel/platform-msx1/uzi.lnk [new file with mode: 0644]
Kernel/platform-msx1/vdp.s [new file with mode: 0644]

diff --git a/Kernel/platform-msx1/Makefile b/Kernel/platform-msx1/Makefile
new file mode 100644 (file)
index 0000000..5a595e4
--- /dev/null
@@ -0,0 +1,27 @@
+
+CSRCS = devtty.c devfd.c devhd.c devlpr.c
+CSRCS += devices.c main.c
+
+ASRCS = msx1.s crt0.s vdp.s
+ASRCS += tricks.s commonmem.s bootrom.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:
+       dd if=../fuzix.bin of=../fuzix.com bs=256 skip=1
+       dd if=../fuzix.bin of=../fuzix.cart bs=16384 conv=sync
diff --git a/Kernel/platform-msx1/bootrom.s b/Kernel/platform-msx1/bootrom.s
new file mode 100644 (file)
index 0000000..f7db4fc
--- /dev/null
@@ -0,0 +1,94 @@
+               .area _CODE
+
+               .db 'A'
+               .db 'B'
+               .dw wtfami + 0x4000
+               .dw 0,0,0,0,0,0
+
+;
+;      Entered at 0x4000 but linked at 0x0000 so be careful
+;
+wtfami:                di
+
+               ld a, #0x23             ; Debug port
+               out (0x2e), a
+               ld a, #':'
+               out (0x2f), a
+
+               in a, (0xA8)
+               ld d, a
+               and #0x0C               ; bits for 0x4000
+               ld b, a
+               rla
+               rla                     ; to 0x8000
+               or b                    ; and 0x4000
+               rla
+               rla                     ; to 0xC000/8000
+               or b                    ; and 0x4000
+               ld b, a                 ; B is now the bits for
+                                       ; putting 48K of cartridge
+                                       ; in place
+               out (0xA8), a           ; Map cartridge
+               ld a, #3
+               out (0xFC), a           ; Begin mapping RAM
+               ld a, #'1'
+               out (0x2f), a
+               exx
+               ld hl, #0x4000          ; Cartridge 0x4000 -> RAM 0
+               ld de, #0x0
+               ld bc, #0x4000
+               ldir
+               dec a
+               out (0xFC), a
+               ld de, #0               ; 0x8000 -> RAM 0x4000
+               ld bc, #0x4000
+               ldir
+               dec a
+               out (0xFC), a
+               ld de, #0               ; 0xC000 -> RAM 0x8000
+               ld bc, #0x4000
+               ldir
+               exx     
+               ld a, #3                ; put the maps right
+               out (0xFC), a
+               ld a, #2
+               out (0xFD), a
+               ld a, #1
+               out (0xFE), a
+               xor a
+               out (0xFF), a
+               ld a, #'G'
+               out (0x2f), a
+               ld a, d
+               and #0xC0               ; RAM in 0xC000 slot bits
+               ld e, a                 
+               rra                     ; Propogate into other banks
+               rra
+               or e
+               rra
+               rra
+               or e
+               rra
+               rra
+               or e
+               ld e, a                 ; E is now "all RAM"
+               and #0xF3
+               ld c, a
+               ld a, d                 ; Get original status
+               and #0x0c               ; bits for 0x4000 as cartridge
+               or c                    ; bits for the RAM
+               out (0xA8), a
+               ld a, #'O'
+               out (0x2f), a
+               ;
+               ;       We now have RAM where we need it
+               ;
+               jp ramgo
+ramgo:         ld a, #'!'
+               out (0x2f), a
+               ld a, e
+               out (0xA8), a           ; Now go all ram
+               jp 0x100
+
+               ; Hack Hack FIXME
+               .ds 0x72
diff --git a/Kernel/platform-msx1/commonmem.s b/Kernel/platform-msx1/commonmem.s
new file mode 100644 (file)
index 0000000..d3d30a8
--- /dev/null
@@ -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-msx1/config.h b/Kernel/platform-msx1/config.h
new file mode 100644 (file)
index 0000000..50ba56d
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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 - for now while we get it booting */
+#undef CONFIG_SINGLETASK
+/* Video terminal, not a serial tty */
+#define CONFIG_VT
+/* Fixed banks */
+#define CONFIG_BANK_FIXED
+#define MAX_MAPS       16
+/* For now lets play simple and assume 32K banks as possible with some of the
+   MSX1 megafoo devices */
+#define MAP_SIZE       32768
+
+/* As reported to user space - 4 banks, 16K page size */
+#define CONFIG_BANKS   1
+
+/* Vt definitions */
+#define VT_WIDTH       40
+#define VT_HEIGHT      24
+#define VT_RIGHT       39
+#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 *)(0x8000))  /* Top of program */
+
+#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-msx1/crt0.s b/Kernel/platform-msx1/crt0.s
new file mode 100644 (file)
index 0000000..1765f36
--- /dev/null
@@ -0,0 +1,75 @@
+               ; 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 _BOOT
+               .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
+
+               ; Just for the benefit of the map file
+               .globl start
+
+               ; startup code @0x100
+               .area _CODE
+
+;
+; Execution begins with us correctly mapped and at 0x0x100
+;
+; We assume here that the kernel packs below 48K for now we've got a few
+; KBytes free but revisit as needed
+;
+start:         di
+               ld a, #4                ; 0 holds 8K of stuff we might want
+                out (0xff), a          ; if we go the OS route, so use 4
+                                       ; plus "0" is magic so this saves
+                                       ; shifting them all by one.
+               ; Debug port
+               ld a, #0x23
+               out (0x2e), a
+               ld a, #'@'
+               out (0x2f), a
+               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-msx1/devfd.c b/Kernel/platform-msx1/devfd.c
new file mode 100644 (file)
index 0000000..e3db971
--- /dev/null
@@ -0,0 +1,70 @@
+/* 
+ * Dummy fd driver code
+ *
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+
+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-msx1/devfd.h b/Kernel/platform-msx1/devfd.h
new file mode 100644 (file)
index 0000000..b59ff21
--- /dev/null
@@ -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-msx1/devhd.c b/Kernel/platform-msx1/devhd.c
new file mode 100644 (file)
index 0000000..16530fa
--- /dev/null
@@ -0,0 +1,70 @@
+/* 
+ * Dummy hd driver code
+ *
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devhd.h>
+
+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-msx1/devhd.h b/Kernel/platform-msx1/devhd.h
new file mode 100644 (file)
index 0000000..cdb55de
--- /dev/null
@@ -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-msx1/devices.c b/Kernel/platform-msx1/devices.c
new file mode 100644 (file)
index 0000000..b5805f1
--- /dev/null
@@ -0,0 +1,38 @@
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devhd.h>
+#include <devfd.h>
+#include <devlpr.h>
+#include <devsys.h>
+#include <tty.h>
+#include <devtty.h>
+
+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-msx1/devlpr.c b/Kernel/platform-msx1/devlpr.c
new file mode 100644 (file)
index 0000000..6ca6cef
--- /dev/null
@@ -0,0 +1,55 @@
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devlpr.h>
+
+__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-msx1/devlpr.h b/Kernel/platform-msx1/devlpr.h
new file mode 100644 (file)
index 0000000..7765c18
--- /dev/null
@@ -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-msx1/devtty.c b/Kernel/platform-msx1/devtty.c
new file mode 100644 (file)
index 0000000..d045d55
--- /dev/null
@@ -0,0 +1,202 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <devtty.h>
+#include <vt.h>
+#include <tty.h>
+
+#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 tty_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-msx1/devtty.h b/Kernel/platform-msx1/devtty.h
new file mode 100644 (file)
index 0000000..c387e90
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+
+#endif
diff --git a/Kernel/platform-msx1/main.c b/Kernel/platform-msx1/main.c
new file mode 100644 (file)
index 0000000..52ce283
--- /dev/null
@@ -0,0 +1,47 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+/* These are set by the msx startup asm code */
+uint16_t vdpport = 0x99 + 256 * 40;
+uint16_t infobits;
+uint16_t msxmaps;
+
+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 = msxmaps - 1;
+    /* Add all the RAM, except 0,1,2,3 which is the kernel data/bss, add 0
+       last to become the common for init */
+    while (i > 4)
+        pagemap_add(i--);
+    /* Init will pick this up correctly as its common */
+    pagemap_add(4);
+}
+
+void map_init(void)
+{
+}
+
+
+void platform_interrupt(void)
+{
+       timer_interrupt();
+}
diff --git a/Kernel/platform-msx1/msx1.s b/Kernel/platform-msx1/msx1.s
new file mode 100644 (file)
index 0000000..d6a4d79
--- /dev/null
@@ -0,0 +1,223 @@
+;
+;          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 _msxmaps
+
+            .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
+
+           ; stuff to save
+           .globl _vdpport
+           .globl _infobits
+
+           ;
+           ; vdp - we must initialize this bit early for the vt
+           ;
+           .globl vdpinit
+
+            .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, #'F'
+           out (0x2F), a
+            ret
+
+init_hardware:
+           ld a, #'U'
+           out (0x2F), a
+           ; save the useful bits of low memory first
+           ld hl, (0x2B)
+           ld (_infobits), a
+;          ld a, (0x07)
+;          ld (_vdpport), a
+
+           ; Size RAM
+           call size_memory
+
+            ; set up interrupt vectors for the kernel mapped low page and
+            ; data area
+            ld hl, #0
+            push hl
+            call _program_vectors
+            pop hl
+
+           ld a, #'Z'
+           out (0x2F), a
+
+           ; Program the video engine
+           call vdpinit
+
+           ld a, #'I'
+           out (0x2F), a
+
+            im 1                       ; set CPU interrupt mode
+           call _vtinit                ; init the console video
+
+           ld a, #'X'
+           out (0x2F), a
+            ret
+
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+            .area _COMMONMEM
+
+;
+;      Called with interrupts off. See if we can work out how much RAM
+;      there is
+;
+size_memory:
+           ; set system RAM size in KB
+           ld hl, #64
+           ld (_ramsize), hl
+           ld de, #0xFFD0
+           add hl, de          ; subtract 48K for the kernel
+           ld (_procmem), hl
+           ret
+
+_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
+           ; on MSX this is probably the wrong thing to do!!! FIXME
+            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:
+           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, go via the helper
+;      so our cached copy is correct.
+;
+map_kernel:
+           ret
+map_process_2:
+            ret
+;
+;      Restore a saved mapping. We are guaranteed that we won't switch
+;      common copy between save and restore. Preserve all registers
+;
+map_restore:
+           ret
+;
+;      Save the current mapping.
+;
+map_save:
+           ret
+
+; emulator debug port for now
+outchar:
+           push af
+           out (0x2F), a
+           pop af
+            ret
+
diff --git a/Kernel/platform-msx1/tricks.s b/Kernel/platform-msx1/tricks.s
new file mode 100644 (file)
index 0000000..6a170fa
--- /dev/null
@@ -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-msx1/uzi.lnk b/Kernel/platform-msx1/uzi.lnk
new file mode 100644 (file)
index 0000000..1b01d6c
--- /dev/null
@@ -0,0 +1,39 @@
+-mwxuy
+-i uzi.ihx
+-b _CODE=0x0000
+-b _COMMONMEM=0xF000
+-l z80
+platform-msx1/bootrom.rel
+platform-msx1/crt0.rel
+platform-msx1/commonmem.rel
+platform-msx1/msx1.rel
+platform-msx1/vdp.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+platform-msx1/tricks.rel
+platform-msx1/main.rel
+timer.rel
+kdata.rel
+platform-msx1/devtty.rel
+platform-msx1/devfd.rel
+platform-msx1/devhd.rel
+platform-msx1/devlpr.rel
+platform-msx1/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
+bankfixed.rel
+tty.rel
+vt.rel
+devsys.rel
+usermem.rel
+usermem_std-z80.rel
+-e
diff --git a/Kernel/platform-msx1/vdp.s b/Kernel/platform-msx1/vdp.s
new file mode 100644 (file)
index 0000000..2bdeda6
--- /dev/null
@@ -0,0 +1,253 @@
+            .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
+
+           .globl vdpinit
+           .globl platform_interrupt_all
+
+           .area _CODE
+
+;
+;      FIXME: table.. and move into init RAM
+;
+vdpinit:    ld de, #0x8000             ; M4 }
+           call vdpout                 ;    }  40x25 mode
+           ld de, #0x81F0              ; M1 }  screen on, virq on hirq off
+                                       ; F0 ????
+           call vdpout
+           ld de, #0x8200              ; characters at VRAM 0
+           call vdpout
+           ld de, #0x8300              ; blink is unused
+           call vdpout
+           ld de, #0x8401              ; font at 0x0800
+           call vdpout
+           ld de, #0x87F5              ; white text on black
+;      Fall through...
+;
+;      Register write value E to register A
+;
+vdpout:            ld bc, (_vdpport)
+           out (c), e                  ; Write the data
+           out (c), d                  ; and then the register | 0x80
+           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
+           push hl
+           add hl, hl                  ; x 16
+           add hl, hl                  ; x 32
+           ld a, d
+           pop de
+           add hl, de                  ; x 40
+           ld e, a
+           ld d, 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
+plotit2:
+           ld bc, (_vdpport)
+           out (c), l                  ; address
+           out (c), h                  ; address | 0x40
+           dec c
+           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               40
+
+           .area       _CODE
+;
+;      We don't yet use attributes...
+;
+_scroll_down:
+           ret
+           ld b, #23
+           ld de, #0x3C0       ; 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
+           dec c
+           inir                ; safe on MSX2 but not MSX1? - FIXME 
+           inc c
+           ld hl, #0x4028      ; go down one line and into write mode
+           add hl, de          ; relative to our position
+           out (c), l
+           out (c), h
+           ld b, #0x28
+           ld hl, #scrollbuf
+           dec c
+           otir                ; video ptr is to the line below so keep going
+           pop bc              ; recover line counter
+           ld hl, #0xffd8
+           add hl, de          ; up 40 bytes
+           ex de, hl           ; and back into DE
+           djnz upline
+           ret
+
+_scroll_up:
+           ret
+           ld b, #23
+           ld de, #40          ; start of second line
+downline:   push bc
+           ld bc, (_vdpport)
+           ld hl, #scrollbuf
+           out (c), e
+           out (c), d
+           dec c
+           inir                ; MSX1 fixme ?
+           inc c
+           ld hl, #0x3FD8      ; up 40 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, #0x40
+           otir                ; FIXME: is otir ok for MSX 1 ???
+           pop bc
+           ld hl, #40
+           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
+           ld b, #0x40
+           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, #' '
+           dec c
+l2:        ld b, #40           
+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
+           ld b, #0x40
+           call _videopos
+           ld a, c
+           ld bc, (_vdpport)
+           out (c), l
+           out (c), h
+           ld b, a
+            ld a, #' '
+           dec c
+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 b, #0x00                 ; reading
+           call _videopos
+           ld a, c
+           ld bc, (_vdpport)
+           out (c), l                  ; address
+           out (c), h                  ; address
+           dec c
+           in a, (c)                   ; character
+           ld (cursorpeek), a          ; save it away
+           set 6, h                    ; make it a write command
+           ld a, #'_'                  ; write the cursor
+           jp plotit2
+
+_cursor_off:
+           ld de, (cursorpos)
+           ld a, (cursorpeek)
+           ld c, a
+           call plotit
+           ret
+;
+;      This must be in data or common not code
+;
+           .area _DATA
+cursorpos:  .dw 0
+cursorpeek: .db 0
+
+           .area _COMMONMEM
+
+;
+;      FIXME: should use vdpport, but right now vdpport is in data not
+;      common space.
+;
+platform_interrupt_all:
+           ld c, #0x99
+           in a, (c)
+           ret