Not yet bootable.
Still to complete:
CTC setup
DART serial ports
CP/M tools scripts to spit out a memotech floppy disk
--- /dev/null
+
+CSRCS = devlpr.c devtty.c devfd.c devsil.c
+CSRCS += devices.c main.c
+
+ASRCS = crt0.s mtx.s vdp.s floppy.s
+ASRCS += tricks.s commonmem.s
+
+AOBJS = $(ASRCS:.s=.rel)
+COBJS = $(CSRCS:.c=.rel)
+DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS))
+
+OBJS = $(AOBJS) $(COBJS) $(DOBJS)
+
+CROSS_CCOPTS += -I../dev/
+
+JUNK = *.rel *.lst *.asm *.sym *.rst
+
+all: $(OBJS)
+
+$(AOBJS): %.rel: %.s
+ $(CROSS_AS) $(ASOPTS) $<
+
+$(COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DOBJS): %.rel: ../dev/%.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+
+clean:
+ rm -f $(OBJS) $(JUNK) core *~
+
+image:
+ cp ../fuzix.bin fuzix.com
--- /dev/null
+The MTX512 has the following relevant features
+
+- "FDX" WD1791 floppy (and an actual DMA controller) at 0x40
+ SASI interface for floppies
+ (Not in emulator)
+- "SDX" WD1791 style floppy (and no DMA controller) at 0x10
+- 9918/29 VDP graphics (equivalent to MSX1 but on ports 1/2) - interrupts
+ are fed to the CTC however
+- Separate 80 column card (text mode only) (6845 on port 0x38/39)
+ this has its own memory port poked via port 30,31 (set 31
+ bits 2-0 to the high address bits, bit 5 for attr, 6 for ascii, 7 for write
+ then port 30 for the low bits. Port 32 is the data port (read after write
+ before), port 33 for read/write is the attribute byte
+- "silicon disc" (aka RAM discs) on 0x50-53 54-57 58-5b 5c-5f
+- CTC timer (at CPU speed so can support single step)
+- RS232 (Z80 DART)
+- Sound (SN76489A) on I/O port 6, (strobe on port 3, 32uS reqired post
+ strobe)
+
+Memory is split into four banks the decode for which is dependent upon
+whether the box is in "ROM based" or "RAM based" mode. The memory is then
+switched in 48K banks with a 16K true common.
+
+All memory management is controlled by the page port (port 0). Bit 7 is set
+to 1 to turn the ROMs off, bit 4-6 detemine the rom page address, bits 0-3
+select the RAM bank.
+
+Note that Rememorizer is different, it allows 0x0f to be written to the
+low 3 bits of port 0, then port 0xd2, d0, d1 control the 3 16K banks and also
+has an SD card. Not supported, if you want it supported fix MEMU ;-)
+
+
+
--- /dev/null
+;
+; Common is at 0xC000. We don't actually have any choice about that
+; the platform is wired this way.
+;
+
+ .module commonmem
+
+ .area _COMMONMEM
+
+ .include "../cpu-z80/std-commonmem.s"
--- /dev/null
+/* 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
+/* CP/M emulation */
+#define CONFIG_CPM_EMU
+/* Video terminal, not a serial tty */
+#define CONFIG_VT
+/* Fixed banking */
+#define CONFIG_BANK_FIXED
+/* 10 48K banks, 1 is kernel */
+#define MAX_MAPS 10
+#define MAP_SIZE 0xC000U
+
+/* Banks as reported to user space */
+#define CONFIG_BANKS 1
+
+#define TICKSPERSEC 50 /* Ticks per second */
+#define PROGBASE 0x0000 /* also data base */
+#define PROGLOAD 0x0100 /* also data base */
+#define PROGTOP 0xBD00 /* Top of program, base of U_DATA copy */
+#define PROC_SIZE 48 /* Memory needed per process */
+
+#define SWAP_SIZE 0x60 /* 48K in blocks */
+#define SWAPBASE 0x0000 /* We swap the lot in one, include the */
+#define SWAPTOP 0xC000 /* vectors so its a round number of sectors */
+#define MAX_SWAPS 64 /* How many swaps per disc */
+
+#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 /* Will be 4 two monitors, two serial */
+
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+#define SWAPDEV ((8*256) + 0) /* Device for swapping. - first silicon disk */
+#define NBUFS 16 /* Number of block buffers */
+#define NMOUNTS 4 /* Number of mounts at a time */
+
+/* Terminal definitions */
+#define VT_WIDTH 40
+#define VT_HEIGHT 24
+#define VT_RIGHT 39
+#define VT_BOTTOM 23
--- /dev/null
+ .module crt0
+
+ ; 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 _CONST
+ .area _DATA
+ .area _INITIALIZED
+ .area _BSEG
+ .area _BSS
+ .area _HEAP
+ ; note that areas below here may be overwritten by the heap at runtime, so
+ ; put initialisation stuff in here
+ .area _INITIALIZER
+ .area _GSINIT
+ .area _GSFINAL
+ .area _DISCARD
+ .area _COMMONMEM
+
+ ; imported symbols
+ .globl _fuzix_main
+ .globl init_early
+ .globl init_hardware
+ .globl s__INITIALIZER
+ .globl s__DATA
+ .globl l__DATA
+ .globl s__DISCARD
+ .globl l__DISCARD
+ .globl s__COMMONMEM
+ .globl l__COMMONMEM
+
+ .globl kstack_top
+
+ ; startup code
+ .area _CODE
+init:
+ di
+ ld sp, #kstack_top
+
+ ; Configure memory map
+ call init_early
+
+ ; move the common memory where it belongs
+ ld hl, #s__INITIALIZER
+ ld de, #s__COMMONMEM
+ ld bc, #l__COMMONMEM
+ ldir
+ ; and the discard
+ ld de, #s__DISCARD
+ ld bc, #l__DISCARD
+ ldir
+ ; then zero the data area
+ ld hl, #s__DATA
+ ld de, #s__DATA + 1
+ ld bc, #l__DATA - 1
+ ld (hl), #0
+ ldir
+
+ ; Hardware setup
+ call init_hardware
+
+ ; Call the C main routine
+ call _fuzix_main
+
+ ; main shouldn't return, but if it does...
+ di
+stop: halt
+ jr stop
+
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+
+/* Two drives but minors 2,3 are single density mode */
+#define MAX_FD 4
+
+#define OPDIR_NONE 0
+#define OPDIR_READ 1
+#define OPDIR_WRITE 2
+
+#define FD_READ 0x88 /* 2797 needs 0x88, 1797 needs 0x80 */
+#define FD_WRITE 0xA8 /* Likewise A8 v A0 */
+
+static uint8_t motorct;
+static uint8_t fd_selected = 0xFF;
+static uint8_t fd_tab[MAX_FD];
+
+/*
+ * We only support normal block I/O
+ */
+
+static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ blkno_t block;
+ uint16_t dptr;
+ int ct = 0;
+ int tries;
+ uint8_t err = 0;
+ uint8_t *driveptr = fd_tab + minor;
+ uint8_t cmd[6];
+
+ if(rawflag)
+ goto bad2;
+
+ if (fd_selected != minor) {
+ uint8_t err = fd_motor_on(minor|(minor > 1 ? 0: 0x10));
+ if (err)
+ goto bad;
+ }
+
+ dptr = (uint16_t)udata.u_buf->bf_data;
+ block = udata.u_buf->bf_blk;
+
+// kprintf("Issue command: drive %d\n", minor);
+ cmd[0] = is_read ? FD_READ : FD_WRITE;
+ cmd[1] = block / 8; /* 2 sectors per block */
+ cmd[2] = ((block & 7) << 1) + 1;
+ cmd[3] = is_read ? OPDIR_READ: OPDIR_WRITE;
+ cmd[4] = dptr & 0xFF;
+ cmd[5] = 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 == 3)
+ goto bad;
+ cmd[5]++;
+ cmd[2]++; /* Next sector for next block */
+ ct++;
+ }
+ return 1;
+bad:
+ kprintf("fd%d: error %x\n", minor, err);
+bad2:
+ udata.u_error = EIO;
+ return -1;
+}
+
+int fd_open(uint8_t minor, uint16_t flag)
+{
+ flag;
+ 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;
+ return fd_transfer(minor, false, rawflag);
+}
--- /dev/null
+#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__ */
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <tty.h>
+#include <devfd.h>
+#include <devsil.h>
+#include <devsys.h>
+#include <devlpr.h>
+#include <devtty.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+// minor open close read write ioctl
+// -----------------------------------------------------------------
+ /* 0: /dev/fd Floppy disc block devices */
+ { fd_open, no_close, fd_read, fd_write, no_ioctl },
+ /* 1: /dev/hd Hard disc block devices (hdx - not supported yet) */
+ { no_open, no_close, no_rdwr, no_rdwr, 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 */
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 8: /dev/sild Silicon disc block devices */
+ { sil_open, no_close, sil_read, sil_write, no_ioctl },
+};
+
+bool validdev(uint16_t dev)
+{
+ /* This is a bit uglier than needed but the right hand side is
+ a constant this way */
+ if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255)
+ return false;
+ else
+ return true;
+}
+
+void device_init(void)
+{
+// int i;
+// for (i = 0; i < MAX_SWAPS; i++)
+// swapmap_add(i);
+}
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devlpr.h>
+
+__sfr __at 0x00 lpstrobel;
+__sfr __at 0x04 lpdata; /* Data on write, status+strobe clr on read */
+
+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;
+ uint8_t reg;
+
+ minor;
+ rawflag;
+ flag; // shut up compiler
+
+ while (c-- > 0) {
+ ct = 0;
+
+ /* Try and balance polling and sleeping */
+ while ((reg = lpdata) & 1) {
+ reg ^= 2;
+ if (reg & 6) {
+ if (c == udata.u_count) {
+ if (reg & 4)
+ udata.u_error = ENOSPC;
+ else
+ udata.u_error = EIO;
+ }
+ return udata.u_count - c;
+ }
+ ct++;
+ if (ct == 10000) {
+ udata.u_ptab->p_timeout = 3;
+ if (psleep_flags(NULL, flag)) {
+ if (c != udata.u_count)
+ udata.u_error = 0;
+ return udata.u_count - c;
+ }
+ ct = 0;
+ }
+ }
+ /* Data */
+ lpdata = ugetc(p++);
+ /* Strobe - FIXME: should be 1uS */
+ for (reg = 0; reg < 128; reg++);
+ reg = lpstrobel;
+ for (reg = 0; reg < 128; reg++);
+ /* Strobe back high */
+ reg = lpdata;
+ }
+ return udata.u_count - c;
+}
--- /dev/null
+#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
--- /dev/null
+/*
+ * Memotech Silicon Disk Driver
+ *
+ * FIXME: would be sensible to add swap support to this driver
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devsil.h>
+
+#define NUM_SIL 4
+
+static int sil_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ blkno_t block;
+ int block_xfer;
+ uint16_t dptr;
+ int dlen;
+ int ct = 0;
+ int map;
+
+ 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 = udata.u_page;
+ } 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) {
+ sil_memcpy(is_read, map, dptr, block, 0x50 + 4 * minor);
+ block++;
+ ct++;
+ }
+ return ct;
+}
+
+int sil_open(uint8_t minor, uint16_t flag)
+{
+ flag;
+ if(minor >= NUM_SIL) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return 0;
+}
+
+int sil_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;minor;
+ return sil_transfer(minor, true, rawflag);
+}
+
+int sil_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;minor;
+ return sil_transfer(minor, false, rawflag);
+}
+
--- /dev/null
+#ifndef __DEVSIL_DOT_H__
+#define __DEVSIL_DOT_H__
+
+/* public interface */
+int sil_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int sil_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int sil_open(uint8_t minor, uint16_t flag);
+
+/* asm banking helper */
+void sil_memcpy(uint8_t isread, uint8_t map, uint16_t dptr, uint16_t block, uint16_t dev);
+
+#endif /* __DEVRD_DOT_H__ */
+
--- /dev/null
+#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 0x0C serialAd;
+__sfr __at 0x0D serialAc;
+__sfr __at 0x0E serialBd;
+__sfr __at 0x0F serialBc;
+
+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;
+ }
+ serialAd = c;
+}
+
+int tty_carrier(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+void tty_setup(uint8_t minor)
+{
+ minor;
+}
+
+static uint16_t keymap[8];
+static uint16_t keyin[8];
+static uint8_t keybyte, keybit;
+static uint8_t newkey;
+static int keysdown = 0;
+static uint16_t shiftmask[8] = {
+ 0, 0, 1, 0, 1, 0, 63, 0
+};
+__sfr __at 0x05 keyport;
+__sfr __at 0x06 keyporth;
+
+static void keyproc(void)
+{
+ int i;
+ uint8_t key;
+
+ for (i = 0; i < 8; i++) {
+ /* Set the row */
+ keyport = 0xff - (1 << i);
+ /* Read the matrix lines - 10 bit wide */
+ keyin[i] = (keyport | ((uint16_t)keyporth << 8)) ^ 0xffff;
+ key = keyin[i] ^ keymap[i];
+ if (key) {
+ int n;
+ int m = 1;
+ for (n = 0; n < 10; 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][10] = {
+ {'1', '3', '5', '7', '9' , '-', '\\', 0/*page */, 3/*brk*/, 0/*f1*/},
+ { 27, '2', '4', '6', '8', '0', '^', 0/*eol*/, 8, 0/*f5*/},
+ { 0/*ctrl*/, 'w', 'r', 'y', 'i', 'p', '[', 0/*up*/, 9, 0/*f2*/ },
+ {'q', 'e', 't' , 'u', 'o', '@', 10, 8/*left*/, 127, 0 /* f6 */ },
+ { 0/*capsl*/, 's', 'f', 'h', 'k', ';', ']', 0/*right*/, 0, 0/*f7*/ },
+ { 'a', 'd', 'g', 'j', 'l', ':', 13, 12/*home*/, 0, 0 /*f3 */ },
+ { 0/*shift*/, 'x', 'v', 'n', ',', '/', 0/*shift*/, 0/*down*/,0 , 0 /*f8 */},
+ {'z', 'c', 'b', 'm', '.', '_', 0/*ins*/, 0/*cls*/, ' ', 0 /* f4 */ }
+};
+
+static uint8_t shiftkeyboard[8][10] = {
+ {'!', '#', '%', '\'', ')' , '=', '|', 0/*page */, 3/*brk*/, 0/*f1*/},
+ { 27, '"', '$', '&', '(', 0, '~', 0/*eol*/, 8, 0/*f5*/},
+ { 0/*ctrl*/, 'w', 'r', 'y', 'i', 'p', '{', 0/*up*/, 9, 0/*f2*/ },
+ {'q', 'e', 't' , 'u', 'o', '`', 10, 8/*left*/, 127, 0 /* f6 */ },
+ { 0/*capsl*/, 's', 'f', 'h', 'k', '+', '}', 0/*right*/, 0, 0/*f7*/ },
+ { 'a', 'd', 'g', 'j', 'l', '*', 13, 12/*home*/, 0, 0 /*f3 */ },
+ { 0/*shift*/, 'x', 'v', 'n', '>', '/', 0/*shift*/, 0/*down*/,0 , 0 /*f8 */},
+ {'z', 'c', 'b', 'm', '<', '_', 0/*ins*/, 0/*cls*/, ' ', 0 /* f4 */ }
+};
+
+static uint8_t capslock = 0;
+
+static void keydecode(void)
+{
+ uint8_t c;
+
+ if (keybyte == 4 && keybit == 0) {
+ capslock = 1 - capslock;
+ return;
+ }
+
+ if (keymap[6] & 65) /* shift */
+ c = shiftkeyboard[keybyte][keybit];
+ else
+ c = keyboard[keybyte][keybit];
+ if (keymap[2] & 1) { /* control */
+ if (c > 31 && c < 96)
+ c &= 31;
+ }
+ if (capslock && c >= 'a' && c <= 'z')
+ c -= 'a' - 'A';
+ tty_inproc(1, c);
+}
+
+void kbd_interrupt(void)
+{
+ newkey = 0;
+ keyproc();
+ if (keysdown < 3 && newkey)
+ keydecode();
+}
+
+/* This is used by the vt asm code, but needs to live in the kernel */
+uint16_t cursorpos;
+
--- /dev/null
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+
+extern void kbd_interrupt(void);
+
+#endif
--- /dev/null
+;
+; Core floppy routines for the Memotech SDX (WD17xx)
+; Based on the 6809 code
+;
+; FIXME: better drive spin up wait
+; FIXME: double sided media
+; FIXME: correct step rates (per drive ?)
+; FIXME: precompensation
+;
+;
+ .module floppy
+
+ .globl _fd_reset
+ .globl _fd_operation
+ .globl _fd_motor_on
+ .globl _fd_motor_off
+
+FDCREG .equ 0x10
+FDCTRK .equ 0x11
+FDCSEC .equ 0x12
+FDCDATA .equ 0x13
+FDCCTRL .equ 0x14
+;
+; interrupt register reports 0x80 for interrut, 0x40 for drq
+; (0x20 is the unrelated reset button)
+;
+
+;
+; Structures we use
+;
+; Command issue
+;
+CMD .equ 0
+TRACK .equ 1
+SECTOR .equ 2
+DIRECT .equ 3 ; 0 = read 2 = write 1 = status
+DATA .equ 4
+
+ .area _COMMONMEM
+;
+; Simple routine for pauses
+;
+nap: dec bc
+ ld a, b
+ or c
+ jr nz, nap
+ ret
+
+;
+; Wait for the drive controller to become ready
+; Preserve HL, DE
+;
+waitdisk:
+ ld bc, #0
+waitdisk_l:
+ in a, (FDCREG)
+ bit 0, a
+ ret z
+ ;
+ ; Keep poking fdcctrl to avoid a hardware motor timeout
+ ;
+ djnz waitdisk_l
+ dec c
+ jr nz, waitdisk_l
+ ld a, #0xD0 ; reset
+ out (FDCREG), a
+ ex (sp),hl
+ ex (sp),hl
+ ex (sp),hl
+ ex (sp),hl
+ in a, (FDCREG) ; read to reset int status
+ bit 0, a
+ ret
+;
+; Set up and perform a disk operation
+;
+; IX points to the command block
+; HL points to the buffer
+; DE points to the track reg copy
+;
+fdsetup:
+ ld a, (de)
+ out (FDCTRK), a
+ cp TRACK(ix)
+ jr z, fdiosetup
+
+ ;
+ ; So we can verify
+ ;
+ ld a, SECTOR(ix)
+ out (FDCSEC), a
+ ;
+ ; Need to seek the disk
+ ;
+ ld a, #0x14 ; seek
+ out (FDCREG), a
+ ex (sp),hl
+ ex (sp),hl
+ ex (sp),hl
+ ex (sp),hl
+ call waitdisk
+ jr nz, setuptimeout
+ and #0x18 ; error bits
+ jr z, fdiosetup
+ ; seek failed, not good
+setuptimeout: ; NE = bad
+ ld a, #0xff ; we have no idea where we are, force a seek
+ ld (de), a ; zap track info
+ ret
+;
+; Head in the right place
+;
+fdiosetup:
+ ld a, TRACK(ix)
+ ld (de), a ; save track
+; cmp #22 ; FIXME
+; jr nc, noprecomp
+; ld a, (fdcctrl)
+; or #0x10 ; Precomp on
+; jr precomp1
+;noprecomp:
+ ld a, (fdcctrl)
+;precomp1:
+ bit 3, SECTOR(ix) ; check if we need side 1
+ jr nz, fdio_s1
+ res 1, a
+ jr fdio_setsec
+fdio_s1:set 1, a
+fdio_setsec:
+ out (FDCCTRL), a
+ ld a, SECTOR(ix)
+ and #7 ; remove side bit
+ out (FDCSEC), a
+ in a, (FDCREG) ; Clear any pending status
+
+ ld a, CMD(ix)
+
+ ld de, #0 ; timeout handling
+
+ out (FDCREG), a ; issue the command
+ ex (sp),hl ; give the FDC a moment to think
+ ex (sp),hl
+ ex (sp),hl
+ ex (sp),hl
+ ld a, DIRECT(ix)
+ dec a
+ ld a, (fdcctrl)
+ ld d, a ; we need this in a register
+ ; to meet timing
+ set 6,d ; halt mode bit
+ jr z, fdio_in
+ jr nc, fdio_out
+;
+; Status registers
+;
+fdxferdone:
+ ei
+fdxferdone2:
+ in a, (FDCREG)
+ and #0x19 ; Error bits + busy
+ bit 0, a ; Wait for busy to drop, return in a
+ ret z
+ ld a, (fdcctrl)
+ out (FDCCTRL), a
+ jr fdxferdone2
+;
+; Write to the disk - HL points to the target buffer
+;
+fdio_in:
+ ld e, #0x16 ; bits to check
+ ld bc, #FDCDATA ; 256 bytes/sector, c is our port
+fdio_inl:
+ in a, (FDCREG) ; wait for data ready
+ and e
+ jr z, fdio_inl
+ di
+ ini ; grab and go
+fdio_inl2:
+ in a, (FDCREG)
+ and e
+ jr z, fdio_inl2
+ ini
+ jr nz, fdio_inl2
+ jr fdxferdone
+
+;
+; Read from the disk - HL points to the target buffer
+;
+fdio_out:
+ ld bc, #FDCDATA ; 256 bytes/sector, c is our port
+ ld e, #0x76
+fdio_outl:
+ in a, (FDCREG) ; Wait for DRQ (or error)
+ and e
+ jr z, fdio_outl
+ outi ; Stuff byte into FDC while we think
+ di
+ in a, (FDCREG) ; No longer busy ??
+ bit 0,a
+ jr nz, fdio_outbyte
+fdxferbad: ; Bugger...
+ ld a, #0xff
+ ret
+fdio_outbyte:
+ in a, (FDCREG)
+ and e
+ jr z, fdio_outbyte
+ outi
+ jr nz, fdio_outbyte
+ jr fdxferdone
+
+
+;
+; C glue interface.
+;
+; Because of the brain dead memory paging we dump the bits into
+; kernel space always. The thought of taking an NMI while in the
+; user memory and bank flipping to recover is just too odious !
+;
+
+;
+; Reset to track 0, wait for the command then idle
+;
+; fd_reset(uint8_t *drvptr)
+;
+_fd_reset:
+ pop de
+ pop hl
+ push hl
+ push de
+ ld a, (fdcctrl)
+ out (FDCCTRL), a
+ ld a, #1
+ out (FDCSEC), a
+ xor a
+ out (FDCTRK), a
+ out (FDCREG), a ; restore
+ dec a
+ ld (hl), a ; Zap track pointer
+ ex (sp),hl ; give the FDC a moment to think
+ ex (sp),hl
+ ex (sp),hl
+ ex (sp),hl
+
+ call waitdisk
+ cp #0xff
+ ret z
+ and #0x10 ; Error bit from the reset
+ ret nz
+ ld (hl), a ; Track 0 correctly hit
+ ret
+;
+; fd_operation(uint16_t *cmd, uint16_t *drive)
+;
+; The caller must ensure the drive has been selected and the motor is
+; running.
+;
+_fd_operation:
+ pop bc ; return address
+ pop hl ; command
+ pop de ; drive track ptr
+ push de
+ push hl
+ push bc
+ push ix
+ push hl
+ pop ix
+ ld l, DATA(ix)
+ ld h, DATA+1(ix)
+ call fdsetup ; Set up for a command
+ ld l, a
+ ld h, #0
+ pop ix
+ ret
+;
+; C interface fd_motor_on(uint16_t drivesel)
+;
+; Selects this drive and turns on the motors. Also pass in the
+; choice of density
+;
+; bit 0: drive select
+; bit 1: side select
+; bit 2: motor on
+; bit 3: motor ready
+; bit 4:
+;
+;
+_fd_motor_on:
+ pop de
+ pop hl
+ push hl
+ push de
+ ;
+ ; Select drive B, turn on motor if needed
+ ;
+ ld a,(motor_running) ; nothing selected
+ or a
+ jr z, notsel
+
+ cp l
+ jr z, motor_was_on
+;
+; Select our drive
+;
+notsel:
+ ld h, a ; save state as it was
+ or l
+ out (FDCCTRL), a
+ ld (fdcctrl), a
+ bit 2, h ; FIXME - motor bit
+ jr nz, motor_was_on
+ ld bc, #8000
+motorwait:
+ in a, (FDCCTRL)
+ bit 3, a
+ jr nz, motor_good
+ dec bc
+ ld a, b
+ or c
+ jr nz, motorwait
+;
+; Timed out
+;
+ ld hl, #-1
+ ret
+;
+; All is actually good ?
+;
+; If we find the motor is not good try spinning up
+;
+motor_was_on:
+ in a, (FDCCTRL)
+ bit 3,a
+ jr z, notsel
+
+motor_good:
+ ld hl, #0
+ ret
+
+;
+; C interface fd_motor_off(void)
+;
+; Turns off the drive motors, deselects all drives
+;
+_fd_motor_off:
+ ld a, (motor_running)
+ or a
+ ret z
+ ; Should we seek to track 0 ?
+ in a, (FDCCTRL)
+ res 2,a
+ out (FDCCTRL), a
+ xor a
+ ld (motor_running), a
+ ret
+
+ .area _COMMONDATA
+curdrive:
+ .db 0xff
+motor_running:
+ .db 0
+fdcctrl:
+ .db 0
+
\ No newline at end of file
--- /dev/null
+; UZI mnemonics for memory addresses etc
+
+U_DATA .equ 0xC000 ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes.
+
+U_DATA_STASH .equ 0xBD00 ; BD00-BFFF
+
+NMOS_Z80 .equ 0
+
+VRAM_CH .equ 3 ; font data 0x1800-1BFF ?
+
+
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+uint16_t ramtop = PROGTOP;
+uint16_t vdpport = 0x01 + 256 * 40; /* port and width */
+
+void pagemap_init(void)
+{
+ int i;
+ /* Ten banks (should check memory size) FIXME */
+ for (i = 0x81; i < 0x8A; i++)
+ pagemap_add(i);
+}
+
+/* On idle we spin checking for the terminals. Gives us more responsiveness
+ for the polled ports */
+void platform_idle(void)
+{
+ /* We don't want an idle poll and IRQ driven tty poll at the same moment */
+ irqflags_t irq = di();
+// tty_pollirq();
+ irqrestore(irq);
+}
+
+void platform_interrupt(void)
+{
+ kbd_interrupt();
+ timer_interrupt();
+}
+
+/* Nothing to do for the map of init */
+void map_init(void)
+{
+}
+
+void do_beep(void)
+{
+}
--- /dev/null
+;
+; Z80Pack hardware support
+;
+;
+; This goes straight after udata for common. Because of that the first
+; 256 bytes get swapped to and from disk with the uarea (512 byte disk
+; blocks). This isn't a problem but don't put any variables in here.
+;
+; If you make this module any shorter, check what follows next
+;
+
+
+ .module mtx
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl _program_vectors
+ .globl _system_tick_counter
+ .globl platform_interrupt_all
+
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+
+ .globl _sil_memcpy
+
+ .globl _kernel_flag
+
+ ; exported debugging tools
+ .globl _trap_monitor
+ .globl outchar
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+
+ .globl unix_syscall_entry
+ .globl null_handler
+ .globl nmi_handler
+ .globl interrupt_handler
+
+ .globl outcharhex
+ .globl outhl, outde, outbc
+ .globl outnewline
+ .globl outstring
+ .globl outstringhex
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0xC000 upwards)
+; -----------------------------------------------------------------------------
+ .area _COMMONMEM
+
+_trap_monitor:
+ di
+ halt
+
+_trap_reboot:
+ di
+ xor a
+ out (0),a ; ROM mode, we are in common so survive
+ rst 0 ; kaboom
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (below 0xC000, only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+ .area _CODE
+
+init_early:
+ ret
+
+init_hardware:
+ ; FIXME: do proper size checker
+ ; set system RAM size (hardcoded for now)
+ ld hl, #480
+ ld (_ramsize), hl
+ ld hl, #(480-48) ; 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
+
+ ; FIXME: set up interrupt timer on the CTC
+ ; 08 is channel 0, which is input for vdp
+ ; 09 is channel 1, output for DART ser 0 } fed 4MHz/13
+ ; 0A is channel 2, output for DATA ser 1 }
+ ; 0B is channel 3, counting CSTTE edges (cpu clocks) at 4MHz
+ ; 0C-0F are A data, B data, A ctrl, B ctrl
+
+ im 1 ; set CPU interrupt mode
+ ret
+
+_sil_memcpy:
+ push ix
+ ld ix, #0
+ add ix, sp
+ ld l, 6(ix) ; dptr
+ ld h, 7(ix)
+ ld a, 5(ix) ; map
+ ld e, 8(ix) ; block
+ ld d, 9(ix)
+ call map_process_a ; map in the user space we want
+ ld c, 11(ix) ; port base
+ out (c), e ; block low
+ inc c
+ out (c), d ; block high
+ inc c
+ inc c
+ ld b, #0
+ bit 0, 4(ix) ; read ?
+ jr z, sil_mwrite
+ inir ; load 256 bytes
+ inir ; load 256 bytes
+sil_copydone:
+ call map_kernel
+ pop ix
+ ret
+sil_mwrite:
+ otir ; write 256 bytes
+ otir ; write 256 bytes
+ jr sil_copydone
+
+;------------------------------------------------------------------------------
+; 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
+
+ call map_process
+
+ ; write zeroes across all vectors
+ ld hl, #0
+ ld de, #1
+ ld bc, #0x007f ; program first 0x80 bytes only
+ ld (hl), #0x00
+ ldir
+
+ ; now install the interrupt vector at 0x0038
+ ld a, #0xC3 ; JP instruction
+ ld (0x0038), a
+ ld hl, #interrupt_handler
+ ld (0x0039), hl
+
+ ; set restart vector for UZI system calls
+ ld (0x0030), a ; (rst 30h is unix function call vector)
+ ld hl, #unix_syscall_entry
+ ld (0x0031), hl
+
+ ; Set vector for jump to NULL
+ ld (0x0000), a
+ ld hl, #null_handler ; to Our Trap Handler
+ ld (0x0001), hl
+
+ ld (0x0066), a ; Set vector for NMI
+ ld hl, #nmi_handler
+ ld (0x0067), hl
+
+ ; our platform has a "true" common area, if it did not we would
+ ; need to copy the "common" code into the common area of the new
+ ; process.
+
+ ; falls through
+
+ ; put the paging back as it was -- we're in kernel mode so this is predictable
+map_kernel:
+ push af
+ ld a, #0x80 ; ROM off bank 0
+ ; the map port is write only, so keep a local stash
+ ld (map_save), a
+ out (0), a
+ pop af
+ ret
+map_process:
+ ld a, h
+ or l
+ jr z, map_kernel
+ ld a, (hl)
+map_process_a:
+ ld (map_copy), a
+ out (0), a
+ ret
+map_process_always:
+ push af
+ ld a, (U_DATA__U_PAGE)
+ ld (map_copy), a
+ out (0), a
+ pop af
+ ret
+map_save:
+ push af
+ ld a, (map_copy)
+ ld (map_store), a
+ pop af
+ ret
+map_restore:
+ push af
+ ld a, (map_store)
+ out (0), a
+ pop af
+ ret
+map_store:
+ .db 0
+map_copy:
+ .db 0
+_kernel_flag:
+ .db 1
+
+; outchar: Wait for UART TX idle, then print the char in A
+; destroys: AF
+outchar:
+ out (0x0c), a
+ ret
--- /dev/null
+;
+; This is heavily based on the Z80Pack platform code. Only the
+; constants have changed.
+;
+ .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 _swapper
+
+ ; imported debug symbols
+ .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+ .area _COMMONMEM
+
+; Switchout switches out the current process, finds another that is READY,
+; possibly the same process, and switches it in. When a process is
+; restarted after calling switchout, it thinks it has just returned
+; from switchout().
+;
+; 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
+
+ ; Stash the uarea back into process memory
+ ld hl, (U_DATA__U_PAGE)
+ ld a, l
+ out (0), a
+ ld hl, #U_DATA
+ ld de, #U_DATA_STASH
+ ld bc, #U_DATA__TOTALSIZE
+ ldir
+ ld a, #0x80 ; kernel
+ out (0), a
+
+ ; find another process to run (may select this one again)
+ call _getproc
+
+ push hl
+ call _switchin
+
+ ; we should never get here
+ call _trap_monitor
+
+badswitchmsg: .ascii "_switchin: FAIL"
+ .db 13, 10, 0
+swapped: .ascii "_switchin: SWAPPED"
+ .db 13, 10, 0
+
+_switchin:
+ di
+ pop bc ; return address
+ pop de ; new process pointer
+;
+; FIXME: do we actually *need* to restore the stack !
+;
+ push de ; restore stack
+ push bc ; restore stack
+
+ ld a, #0x80
+ out (0), a ; kernel
+
+ push de
+ ld hl, #P_TAB__P_PAGE_OFFSET
+ add hl, de ; process ptr
+ pop de
+
+ ld a, (hl)
+
+ or a
+ jr nz, not_swapped
+
+ ;
+ ; We are still on the departing processes stack, which is
+ ; fine for now.
+ ;
+ ld sp, #_swapstack
+ push hl
+ push de
+ call _swapper
+ pop de
+ pop hl
+ ld a, (hl)
+
+not_swapped:
+ ; Pages please !
+ out (0), a
+
+ ; bear in mind that the stack will be switched now, so we can't use it
+ ; to carry values over this point
+
+ exx ; thank goodness for exx 8)
+ ld hl, #U_DATA_STASH
+ ld de, #U_DATA
+ ld bc, #U_DATA__TOTALSIZE
+ ldir
+ exx
+
+ ld a, #0x80 ; kernel back please
+ out (0), a
+
+ ; check u_data->u_ptab matches what we wanted
+ ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab
+ or a ; clear carry flag
+ sbc hl, de ; subtract, result will be zero if DE==IX
+ jr nz, switchinfail
+
+ ; wants optimising up a bit
+ ld ix, (U_DATA__U_PTAB)
+ ; next_process->p_status = P_RUNNING
+ ld P_TAB__P_STATUS_OFFSET(ix), #P_RUNNING
+
+ ; Fix the moved page pointers
+ ; Just do one byte as that is all we use on this platform
+ ld a, P_TAB__P_PAGE_OFFSET(ix)
+ ld (U_DATA__U_PAGE), a
+ ; runticks = 0
+ ld hl, #0
+ ld (_runticks), hl
+
+ ; restore machine state -- note we may be returning from either
+ ; _switchout or _dofork
+ ld sp, (U_DATA__U_SP)
+
+ pop iy
+ pop ix
+ pop hl ; return code
+
+ ; enable interrupts, if the ISR isn't already running
+ ld a, (_inint)
+ or a
+ ret z ; in ISR, leave interrupts off
+ ei
+ ret ; return with interrupts on
+
+switchinfail:
+ call outhl
+ ld hl, #badswitchmsg
+ call outstring
+ ; something went wrong and we didn't switch in what we asked for
+ jp _trap_monitor
+
+fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
+
+;
+; Called from _fork. We are in a syscall, the uarea is live as the
+; parent uarea. The kernel is the mapped object.
+;
+_dofork:
+ ; always disconnect the vehicle battery before performing maintenance
+ di ; should already be the case ... belt and braces.
+
+ pop de ; return address
+ pop hl ; new process p_tab*
+ push hl
+ push de
+
+ ld (fork_proc_ptr), hl
+
+ ; prepare return value in parent process -- HL = p->p_pid;
+ ld de, #P_TAB__P_PID_OFFSET
+ add hl, de
+ ld a, (hl)
+ inc hl
+ ld h, (hl)
+ ld l, a
+
+ ; Save the stack pointer and critical registers.
+ ; When this process (the parent) is switched back in, it will be as if
+ ; it returns with the value of the child's pid.
+ push hl ; HL still has p->p_pid from above, the return value in the parent
+ push ix
+ push iy
+
+ ; save kernel stack pointer -- when it comes back in the parent we'll be in
+ ; _switchin which will immediately return (appearing to be _dofork()
+ ; returning) and with HL (ie return code) containing the child PID.
+ ; Hurray.
+ ld (U_DATA__U_SP), sp
+
+ ; now we're in a safe state for _switchin to return in the parent
+ ; process.
+
+ ; Need to write a new 47.25K bank copy here, then copy the live uarea
+ ; into the stash of the new process
+
+ ; --------- copy process ---------
+
+ ld hl, (fork_proc_ptr)
+ ld de, #P_TAB__P_PAGE_OFFSET
+ add hl, de
+ ; load p_page
+ ld c, (hl)
+ ; load existing page ptr
+ push af
+ ld a, c
+ call outcharhex
+ pop af
+ ld a, (U_DATA__U_PAGE)
+
+ call bankfork ; do the bank to bank copy
+
+ ; Copy done
+
+ ld a, (U_DATA__U_PAGE) ; parent memory
+ out (0), a ; Switch context to parent
+
+ ; We are going to copy the uarea into the parents uarea stash
+ ; we must not touch the parent uarea after this point, any
+ ; changes only affect the child
+ ld hl, #U_DATA ; copy the udata from common into the
+ ld de, #U_DATA_STASH ; target process
+ ld bc, #U_DATA__TOTALSIZE
+ ldir
+ ; Return to the kernel mapping
+ ld a, #0x80
+ out (0), a
+ ; now the copy operation is complete we can get rid of the stuff
+ ; _switchin will be expecting from our copy of the stack.
+ pop bc
+ pop bc
+ pop bc
+
+ ; Make a new process table entry, etc.
+ ld hl, (fork_proc_ptr)
+ push hl
+ call _newproc
+ pop bc
+
+ ; runticks = 0;
+ ld hl, #0
+ ld (_runticks), hl
+ ; in the child process, fork() returns zero.
+ ;
+ ; And we exit, with the kernel mapped, the child now being deemed
+ ; to be the live uarea. The parent is frozen in time and space as
+ ; if it had done a switchout().
+ ret
+
+;
+; This is related so we will keep it here. Copy the process memory
+; for a fork. a is the page base of the parent, c of the child
+;
+; Assumption - fits into a fixed number of whole 256 byte blocks
+;
+bankfork:
+; ld bc, #(0xC000 - 768) ; 48K minus the uarea stash
+
+ ld b, #0xBD ; C0 x 256 minus 3 sets for the uarea stash
+ ld hl, #0 ; base of memory to fork (vectors included)
+bankfork_1:
+ push bc ; Save our counter and also child offset
+ push hl
+ out (0), a ; switch to parent bank
+ ld de, #bouncebuffer
+ ld bc, #256
+ ldir ; copy into the bounce buffer
+ pop de ; recover source of copy to bounce
+ ; as destination in new bank
+ pop bc ; recover child page number
+ push bc
+ ld b, a ; save the parent bank id
+ ld a, c ; switch to the child
+ out (0), a
+ push bc ; save the bank pointers
+ ld hl, #bouncebuffer
+ ld bc, #256
+ ldir ; copy into the child
+ pop bc ; recover the bank pointers
+ ex de, hl ; destination is now source for next bank
+ ld a, b ; parent bank is wanted in a
+ pop bc
+ djnz bankfork_1 ; rinse, repeat
+ ret
+
+;
+; For the moment
+;
+bouncebuffer:
+ .ds 256
+;
+; We can keep a stack in common because we will complete our
+; use of it before we switch common block. In this case we have
+; a true common so it's even easier. This can share with the bounce
+; buffer used by bankfork as we won't switchin mid way through the
+; banked fork() call.
+;
+_swapstack:
--- /dev/null
+-mwxuy
+-i uzi.ihx
+-b _CODE=0x0100
+-b _COMMONMEM=0xC000
+-l z80
+platform-mtx/crt0.rel
+platform-mtx/commonmem.rel
+platform-mtx/mtx.rel
+platform-mtx/main.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+usermem_std-z80.rel
+platform-mtx/tricks.rel
+timer.rel
+kdata.rel
+usermem.rel
+platform-mtx/devfd.rel
+platform-mtx/floppy.rel
+platform-mtx/devsil.rel
+platform-mtx/devices.rel
+devio.rel
+filesys.rel
+process.rel
+inode.rel
+syscall_exec.rel
+syscall_fs.rel
+syscall_fs2.rel
+syscall_proc.rel
+syscall_other.rel
+tty.rel
+vt.rel
+platform-mtx/vdp.rel
+mm.rel
+bankfixed.rel
+swap.rel
+devsys.rel
+platform-mtx/devlpr.rel
+platform-mtx/devtty.rel
+-e
--- /dev/null
+ .module vdp
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+ .include "../dev/vdp1.s"
+
+
+ .area _COMMONMEM
+
+;
+; FIXME: should use vdpport, but right now vdpport is in data not
+; common space.
+;
+platform_interrupt_all:
+ ret