-TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-z80pack32 platform-dragon platform-tgl6502
+TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-z80pack32 platform-dragon platform-tgl6502 platform-plus3
#export TARGET = 8086test
#export TARGET = atarist
#export TARGET = nc100
#export TARGET = p112
#export TARGET = pcw8256
+export TARGET = plus3
#export TARGET = px4plus
#export TARGET = tgl6502
#export TARGET = trs80
#export TARGET = ubee
#export TARGET = z80pack
#export TARGET = z80pack-lite
-export TARGET= zx128
+#export TARGET= zx128
export VERSION = "0.1"
export SUBVERSION = "ac1"
--- /dev/null
+An FUZIX target for ZX Spectrum +2A/+3
+
+The +2A and +3 have the following choice of memory configurations on top of
+the standard 128K spectrum
+
+ 00 40 80 C0
+conf0 [0] [1] [2] [3]
+conf1 [4] [5] [6] [7]
+conf2 [4] [5] [6] [3]
+conf3 [4] [7] [6] [3]
+
+
+That gives us a conventional low 0/1 and 4/5 for user space with the kernel
+using 2/3/6/7.
+
+Kernel maps are then
+
+3 = common (always mapped high)
+2/6 = banked (at 0x8000)
+7 = banked with screen (at 0x4000)
+
+although 7 does not appear to be part of the banks we cannot map 2 and 7
+together so it's effectively banked.
+
+Alternatively we could go with a single 64K swapping user space with
+kernel mapped normally at 4-7 (with screen hole at C000) and user at 0-3
+and a small copied common in bank 3 and 7
+
+
--- /dev/null
+;
+; Multiple app sizes and the fact the kernel and apps share the same banks
+; means we need to put this somewhere low
+;
+ .module commonmem
+ .area _COMMONDATA
+
+ .include "../cpu-z80/std-commonmem.s"
--- /dev/null
+/* Simple IDE interface */
+#define CONFIG_IDE
+#define IDE_REG_DATA 0xA3
+#define IDE_REG_ERROR 0xA7
+#define IDE_REG_FEATURES 0xA7
+#define IDE_REG_SEC_COUNT 0xAB
+#define IDE_REG_LBA_0 0xAF
+#define IDE_REG_LBA_1 0xB3
+#define IDE_REG_LBA_2 0xB7
+#define IDE_REG_LBA_3 0xBB
+#define IDE_REG_DEVHEAD 0xBB
+#define IDE_REG_STATUS 0xBF
+#define IDE_REG_COMMAND 0xBF
+
+/* Enable to make ^Z dump the inode table for debug */
+#undef CONFIG_IDUMP
+/* Enable to make ^A drop back into the monitor */
+#undef CONFIG_MONITOR
+/* Profil syscall support (not yet complete) */
+#undef CONFIG_PROFIL
+/* Multiple processes in memory at once */
+#define CONFIG_MULTI
+/* Single tasking */
+#undef CONFIG_SINGLETASK
+/* Swap only : swap via IDE or banked low RAM or similar */
+#define CONFIG_SWAP_ONLY
+/* Banks as reported to user space */
+#define CONFIG_BANKS 1
+
+/* Keyboard contains not ascii symbols */
+#define CONFIG_UNIKEY
+
+/* Video terminal, not a serial tty */
+#define CONFIG_VT
+/* 8x8 font for the moment */
+#define CONFIG_FONT8X8
+/* Vt definitions */
+#define VT_WIDTH 32
+#define VT_HEIGHT 24
+#define VT_RIGHT 31
+#define VT_BOTTOM 23
+
+#define TICKSPERSEC 50 /* Ticks per second */
+#define PROGBASE 0x0000 /* also data base */
+#define PROGLOAD 0x0100 /* also data base */
+#define PROGTOP 0xF800 /* Top of program */
+#define PROC_SIZE 64 /* Memory needed per process */
+
+#define BOOT_TTY (513) /* Set this to default device for stdio, stderr */
+ /* In this case, the default is the first TTY device */
+
+/* We need a tidier way to do this from the loader */
+#define CMDLINE NULL /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 1
+
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+
+#define SWAPDEV (swap_dev) /* Swap device (dynamic) */
+#define SWAP_SIZE 0x7C /* 64K minus the common in blocks */
+#define SWAPBASE 0x0000 /* We swap the lot in one, include the */
+#define SWAPTOP 0xF800 /* vectors so its a round number of sectors */
+#define MAX_SWAPS 64
+
+
+#define NBUFS 9 /* Number of block buffers */
+#define NMOUNTS 4 /* Number of mounts at a time */
+#define MAX_BLKDEV 2 /* 2 IDE drives, 1 SD drive */
--- /dev/null
+ .module crt0
+
+ .module crt0
+
+ ;
+ ; Bank 4-7
+ ;
+ .area _CODE
+ .area _CODE2
+ .area _CODE3
+ .area _CONST
+ .area _VIDEO
+ .area _DATA
+ .area _INITIALIZED
+ .area _BSEG
+ .area _BSS
+ .area _HEAP
+ ; note that areas below here may be overwritten by the heap at runtime, so
+ ; put initialisation stuff in here
+ .area _INITIALIZER
+ .area _GSINIT
+ .area _GSFINAL
+ .area _DISCARD
+ .area _FONT
+ ;
+ ; Above 0xC000 in the top of bank 3
+ ;
+ .area _COMMONMEM
+ .area _COMMONDATA
+
+ ; imported symbols
+ .globl _fuzix_main
+ .globl init_early
+ .globl init_hardware
+ .globl _sysconfig
+ .globl s__INITIALIZER
+ .globl s__DATA
+ .globl l__DATA
+ .globl s__FONT
+ .globl l__FONT
+ .globl s__DISCARD
+ .globl l__DISCARD
+ .globl s__COMMONMEM
+ .globl l__COMMONMEM
+
+ .globl kstack_top
+
+ ; startup code
+ .area _CODE
+
+ .include "kernel.def"
+
+;
+; The bootloader has executed and now enters our code. HL points
+; to a table of properties extracted from the 3DOS and BASIC
+; environment. Do the memory shuffle and get going
+;
+init1:
+ di
+ ld sp, #kstack_top
+ push hl
+
+ ; Configure memory map
+ call init_early
+
+ ; move the common memory where it belongs
+ ld hl, #s__INITIALIZER
+ ld de, #s__COMMONMEM
+ ld bc, #l__COMMONMEM
+ ldir
+ ; font
+ ld de, #s__FONT
+ ld bc, #l__FONT
+ ldir
+ ; and the discard
+ ld de, #s__DISCARD
+ ld bc, #l__DISCARD
+ ldir
+ ; then zero the data area
+ ld hl, #s__DATA
+ ld de, #s__DATA + 1
+ ld bc, #l__DATA - 1
+ ld (hl), #0
+ ldir
+
+ pop hl
+ ld (_sysconfig), hl ; We keep this safe until we wiped data
+
+ ; Hardware setup
+ call init_hardware
+
+ ; Call the C main routine
+ call _fuzix_main
+
+ ; main shouldn't return, but if it does...
+ di
+stop: halt
+ jr stop
+
+
+ .area _DATA
+_sysconfig:
+ .word 0
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+#include <sysconfig.h>
+
+#define MAX_FD 2
+
+extern uint8_t fdc_user;
+extern uint16_t fdc_addr;
+extern uint8_t rwcmd[9];
+extern uint8_t seekcmd[3];
+
+extern void fdc_motoron(void);
+extern void fdc_motoroff(void);
+extern uint8_t fdc_read(void);
+extern uint8_t fdc_write(void);
+extern uint8_t fdc_recal(void);
+extern uint8_t fdc_seek(void);
+
+uint8_t track[MAX_FD] = { 0xFF, 0xFF };
+
+static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ blkno_t block;
+ uint16_t dptr;
+ int ct = 0;
+ int tries;
+ uint8_t err = 0;
+ uint8_t nblock;
+
+ if(rawflag == 2)
+ goto bad2;
+
+ if (rawflag == 0) {
+ dptr = (uint16_t)udata.u_buf->bf_data;
+ block = udata.u_buf->bf_blk;
+ nblock = 1;
+ } else {
+ if (((uint16_t)udata.u_offset|udata.u_count) & BLKMASK)
+ goto bad2;
+ dptr = (uint16_t)udata.u_base;
+ block = udata.u_offset >> 9;
+ nblock = udata.u_count >> 9;
+ }
+
+ rwcmd[1] = 1 << minor;
+ rwcmd[2] = block / 9;
+ rwcmd[3] = 0; /* Single sided only for now */
+ rwcmd[4] = 1 + block % 9; /* Sector */
+ rwcmd[6] = rwcmd[4];
+
+ fdc_user = rawflag;
+
+ while (ct < nblock) {
+ fdc_addr = dptr;
+ if (track[minor] != rwcmd[2]) {
+ seekcmd[1] = 1 << minor;
+ seekcmd[2] = rwcmd[2];
+ fdc_seek();
+ }
+ for (tries = 0; tries < 4 ; tries++) {
+ /* FIXME: need to return status properly and mask it */
+ if (is_read)
+ err = fdc_read();
+ else
+ err = fdc_write();
+ if (err == 0)
+ break;
+ }
+ if (tries > 1) /* FIXME: set drive */
+ fdc_recal();
+ if (tries == 4)
+ goto bad;
+
+ dptr += 0x200; /* Move on 512 bytes in the buffer */
+
+ rwcmd[4]++;
+ /* Step a track */
+ if (rwcmd[4] > 10) {
+ rwcmd[4] = 1;
+ rwcmd[2]++;
+ }
+ rwcmd[6] = rwcmd[4];
+ ct++;
+ }
+ return 1;
+bad:
+ kprintf("fd%d: error %x\n", minor, err);
+bad2:
+ udata.u_error = EIO;
+ return -1;
+}
+
+int fd_open(uint8_t minor, uint16_t flag)
+{
+ flag;
+ /* Check we have a floppy */
+ if(!(sysconfig & CONF_PLUS3) || minor >= MAX_FD) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return 0;
+}
+
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return fd_transfer(minor, true, rawflag);
+}
+
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return fd_transfer(minor, false, rawflag);
+}
--- /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 *driveptr);
+
+#endif /* __DEVFD_DOT_H__ */
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <tty.h>
+#include <devsys.h>
+#include <vt.h>
+#include <devfd.h>
+#include <devide.h>
+#include <blkdev.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+ /* 0: /dev/fd Floppy disc block devices: disciple */
+ { fd_open, no_close, fd_read, fd_write, no_ioctl },
+#ifdef CONFIG_IDE
+ /* 1: /dev/hd Hard disc block devices */
+ { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl },
+#else
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+#endif
+ /* 2: /dev/tty TTY devices */
+ { tty_open, tty_close, tty_read, tty_write, vt_ioctl },
+ /* 3: /dev/lpr Printer devices */
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 4: /dev/mem etc System devices (one offs) */
+ { no_open, no_close, sys_read, sys_write, sys_ioctl }
+};
+
+bool validdev(uint16_t dev)
+{
+ /* This is a bit uglier than needed but the right hand side is
+ a constant this way */
+ if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255)
+ return false;
+ else
+ return true;
+}
+
+void device_init(void)
+{
+#ifdef CONFIG_IDE
+ devide_init();
+#endif
+}
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <devtty.h>
+#include <keycode.h>
+#include <vt.h>
+#include <tty.h>
+
+char tbuf1[TTYSIZ];
+
+/* buffer for port scan procedure */
+uint8_t keybuf[8];
+/* Previous state */
+uint8_t keymap[8];
+
+static uint8_t keybyte, keybit;
+static uint8_t newkey;
+static int keysdown = 0;
+
+uint8_t keyboard[8][5] = {
+ {' ', 0 , 'm', 'n', 'b'},
+ {13 , 'l', 'k', 'j', 'h'},
+ {'p', 'o', 'i', 'u', 'y'},
+ {'0', '9', '8', '7', '6'},
+ {'1', '2', '3', '4', '5'},
+ {'q', 'w', 'e', 'r', 't'},
+ {'a', 's', 'd', 'f', 'g'},
+ {0 , 'z', 'x', 'c', 'v'}
+};
+
+/* SYMBOL SHIFT MODE */
+uint8_t shiftkeyboard[8][5] = {
+ {' ', 0 , '.', ',', '*'},
+ {13 , '=', '+', '-', '^'},
+ {'"', ';', '@', ']', '['},
+ {'_', ')', '(', '\'','&'},
+ {'!', '@', '#', '$', '%'},
+ {'`', 0 , 0 , '<', '>'},
+ {'~' ,'|', '\\','{', '}'},
+ {0 , ':', KEY_POUND , '?', '/'}
+};
+
+
+static uint8_t shiftmask[8] = { 0x02, 0, 0, 0, 0, 0, 0, 0x01 };
+
+struct s_queue ttyinq[NUM_DEV_TTY + 1] = { /* ttyinq[0] is never used */
+ {NULL, NULL, NULL, 0, 0, 0},
+ {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2},
+};
+
+/* tty1 is the screen */
+
+/* Output for the system console (kprintf etc) */
+void kputchar(char c)
+{
+ if (c == '\n')
+ tty_putc(0, '\r');
+ tty_putc(0, c);
+}
+
+/* Both console and debug port are always ready */
+ttyready_t tty_writeready(uint8_t minor)
+{
+ minor;
+ return TTY_READY_NOW;
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ minor;
+ vtoutput(&c, 1);
+}
+
+int tty_carrier(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+void tty_setup(uint8_t minor)
+{
+ minor;
+}
+
+void tty_sleeping(uint8_t minor)
+{
+ minor;
+}
+
+void update_keyboard(void)
+{
+ /* We need this assembler code because SDCC __sfr cannot handle 16-bit addresses. And because it is much faster, of course */
+ /* TODO: make it naked? */
+ __asm
+ ld hl,#_keybuf
+ ld c, #0xFE
+ ld b, #0x7f
+ ld e, #8 ; 8 keyboard ports, 7FFE, BFFE, DFFE and so on
+ read_halfrow:
+ in a, (c)
+; and #0
+ cpl
+ ld(hl), a
+ rrc b
+ inc hl
+ dec e
+ jr nz, read_halfrow
+ __endasm;
+}
+
+void tty_pollirq(void)
+{
+ int i;
+
+ update_keyboard();
+
+ newkey = 0;
+
+ for (i = 0; i < 8; i++) {
+ int n;
+ uint8_t key = keybuf[i] ^ keymap[i];
+ if (key) {
+ uint8_t m = 0x10;
+ for (n = 4; n >= 0; n--) {
+ if ((key & m) && (keymap[i] & m))
+ if (!(shiftmask[i] & m))
+ keysdown--;
+
+ if ((key & m) && !(keymap[i] & m)) {
+ if (!(shiftmask[i] & m))
+ keysdown++;
+ keybyte = i;
+ keybit = n;
+ newkey = 1;
+ }
+ m >>= 1;
+ }
+ }
+ keymap[i] = keybuf[i];
+ }
+
+ if (keysdown < 3 && newkey)
+ keydecode();
+}
+
+static uint8_t cursor[4] = { KEY_LEFT, KEY_DOWN, KEY_UP, KEY_RIGHT };
+
+static void keydecode(void)
+{
+ uint8_t c;
+
+ uint8_t ss = keymap[0] & 0x02; /* SYMBOL SHIFT */
+ uint8_t cs = keymap[7] & 0x01; /* CAPS SHIFT */
+
+ if (ss) {
+ c = shiftkeyboard[keybyte][keybit];
+ } else {
+ c = keyboard[keybyte][keybit];
+ if (cs) {
+ if (c >= 'a' && c <= 'z')
+ c -= 'a' - 'A';
+ else if (c == '0') /* CS + 0 is backspace) */
+ c = 0x08;
+ else if (c == ' ')
+ c = KEY_STOP; /* ^C map for BREAK */
+ else if (c >= '5' && c <= '8')
+ c = cursor[c - '5'];
+ }
+ }
+
+
+ if (c != 0)
+ tty_inproc(1, c);
+}
+
+
+/* This is used by the vt asm code, but needs to live in the kernel */
+uint16_t cursorpos;
--- /dev/null
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+
+void tty_pollirq(void);
+static void keydecode(void);
+
+#define KEY_ROWS 8
+#define KEY_COLS 5
+extern uint8_t keymap[8];
+extern uint8_t keyboard[8][5];
+extern uint8_t shiftkeyboard[8][5];
+
+#endif
--- /dev/null
+;
+; ROM timings are used for the drives.
+;
+; 0x0A, motor on
+; 0x32, motor off
+; 0xAF, write off
+; 0x1E, head settle ms
+; 0x0C, step rate ms
+; 0x0F, head unload
+; 0x03, head load x 2 + 1
+;
+;
+ .area _COMMONMEM
+
+ .globl _fdc_motoron, _fdc_motoroff
+ .globl _fdc_read, _fdc_write, _fdc_recal, _fdc_seek
+ .globl _fdc_user, _fdc_addr
+ .globl _fdc_statbuf
+ .globl _seekcmd
+ .globl _rwcmd
+ .globl _recalcmd
+ .globl port_map
+ .globl map_kernel, map_process_always
+
+fdc_cmd:
+ dec b
+ jr z, fdclast
+fdc_cmdl:
+ call fdc_outcmd
+ inc hl
+ ret c ; Error
+ djnz fdc_cmdl
+fdclast:
+ di
+fdc_outcmd:
+ push bc
+ ld bc, #0x2ffd
+ ld e, (hl)
+fdc_outcmdw: ; Wait non busy
+ in a, (c)
+ add a, a
+ jr c, fdc_outcmdw
+ add a, a ; FIXME: can we use ret m or similar here ?
+ ret c ; Failed
+ ld b, #0x3F
+ out (c), e ; Output the byte
+ ex (sp), hl
+ ex (sp), hl ; Nap
+ pop bc
+ ret ; NC
+
+fdc_cmdq: ; Quick (non data) command
+ call fdc_outcmd
+ djnz fdc_cmdq
+ ret
+
+fdc_status:
+ ld bc, #0x2ffd
+ ld hl, #_fdc_statbuf
+fdc_statw:
+ in a, (c)
+ add a, a
+ jr c, fdc_statw
+ add a, a
+ ret nc ; Finished copying block
+ ld b, #0x3F
+ ini
+ ld b, #0x2F
+ ex (sp), hl
+ ex (sp), hl
+ jr fdc_statw
+
+fdc_statusa:
+ call fdc_status
+ ld a, (_fdc_statbuf)
+ ret
+
+fdc_read:
+ ld hl, #_rwcmd
+ ld (hl), #66
+ ld b, #9
+ call fdc_cmd ; Will return with DI
+fdc_readgo:
+ ld bc, #0x2ffd
+ ld d, #0x20
+ jr fdc_rwait
+;
+; This would be faster using EXX. May be worth looking at depending
+; upon clock counts.
+;
+fdc_rbyte:
+ ld b, #0x3F ; 3FFD
+ ini
+ ld b, #0x2f
+fdc_rwait: ; wait for ready
+ in a, (c)
+ jp p, fdc_rwait
+ and d
+ jp nz, fdc_rbyte
+ ; now get status
+ jr fdc_statusa
+
+fdc_write:
+ ld hl, #_rwcmd
+ ld (hl), #65
+ ld b, #9
+ call fdc_cmd ; Will return with DI
+fdc_writego:
+ ld bc, #0x2ffd
+ ld d, #0x20
+ jr fdc_wwait
+fdc_wbyte:
+ ld b, #0x40
+ outi
+ ld b, #0x2f
+fdc_wwait:
+ in a, (c)
+ jp p, fdc_wwait
+ ; now read status
+ jr fdc_statusa
+
+;
+; Seek is interesting as we have to second guess the delays
+;
+fdc_seek:
+ ld (_seekcmd + 2), a ; Cylinder we want
+fdc_seek2:
+ ld b, a
+ ld b, #3
+ call fdc_cmdq
+ ld bc, (_seekcmd + 2)
+ ld a, (curtrack) ; Current track FIXME
+ sub c
+ bit 7, a
+ jr z, seekin
+ neg
+seekin: ; milliseconds of time for the seek
+ add a, a
+ ld b, a
+ add a, a
+ add a, b
+ add a, a ; 12ms step rate assumed
+ add a, #0x1e ; head settle
+ call msdelay
+ ; We should now get a sense interrupt
+ call fdc_sense
+ ; Check if it worked
+ bit 6, a
+ jr nz, fdc_seekfail
+ ld a, (_seekcmd + 2)
+ ld (curtrack), a ; FIXME per drive
+ ret ; Z
+fdc_seekfail:
+ ld a, #0xFF
+ ld (curtrack), a ; Mark as busted
+ ret ; NZ
+
+fdc_recalibrate:
+ ld hl, #_recalcmd
+ ld b, #2 ; recalibrate, unit
+ call fdc_cmdq
+ ld a, #80 ; or 40 if 40 track !
+ ld b, #12 ; 12ms
+fdc_recald:
+ ld a, #80 ; or 40 if 40 track !
+ call msdelay
+ djnz fdc_recald
+ ld a, #0x1e ; settle
+ call msdelay
+ call fdc_status
+ bit 6, a
+ jr nz, fdc_seekfail
+ xor a
+ ld (curtrack), a
+ ret ; Z
+
+fdc_sense:
+ ld hl, #sensecmd
+ call fdc_outcmd
+ call fdc_statusa
+ bit 7, a
+ jr nz, fdc_sense
+ ld a, (_fdc_statbuf)
+ ret
+
+fdc_cmdwait:
+ call fdc_sense
+ and #0xC0
+ cp #0x80
+ jr nz, fdc_cmdwait
+ ret
+
+;fdc_getunit:
+; ld hl, #fdc_guscmd
+; ld b, #2
+; call fdc_cmdq
+; jr fdc_statusa
+
+;
+; Delay 'a' milliseconds. Trashes A, C.
+;
+msdelay: ; For uncontended RAM
+ ld c, #0xDC
+ dec c
+ jr nz, msdelay
+ dec a
+ jr nz, msdelay
+ ret
+
+
+;
+; Must be called with the motor timeout stopped
+;
+_fdc_motoron:
+ ld a, (port_map)
+ bit 3, a
+ ret nz
+ or #9
+ ld (port_map), a
+ ld bc, #0x1ffd
+ out (c), a
+_fdc_mwait:
+ ld bc, #0x3548
+ dec bc
+ ld a, b
+ or c
+ jr nz, _fdc_mwait
+ ret
+
+_fdc_motoroff:
+ ld a, (port_map)
+ and #0xF7
+ ld (port_map), a
+ ld bc, #0x1ffd
+ out (c), a
+ ret
+
+_fdc_read:
+ ld hl, (_fdc_addr)
+ ld a, (_fdc_user)
+ or a
+ push af
+ call nz, map_process_always
+ call fdc_read
+ pop af
+ call nz, map_kernel
+ ret
+
+_fdc_write:
+ ld hl, (_fdc_addr)
+ ld a, (_fdc_user)
+ or a
+ push af
+ call nz, map_process_always
+ call fdc_write
+ pop af
+ call nz, map_kernel
+ ret
+
+_fdc_recal:
+ call fdc_recalibrate
+ ld l, a
+ ret
+
+_fdc_seek:
+ call fdc_seek2
+ ret
+
+ .area _COMMONDATA
+
+curtrack:
+ .byte 0 ; FIXME
+
+_rwcmd:
+ .byte 0x66 ; MFM read / 0x65 for write
+ .byte 0 ; Drive 0, head 0 (for now)
+ .byte 0 ; Track
+ .byte 0 ; Head
+ .byte 0 ; Sector
+ .byte 2 ; 512 byte blocks
+ .byte 0 ; Last sector (== sector)
+ .byte 0x2A ; Gap length
+ .byte 0xFF
+
+sensecmd:
+ .byte 0x08
+
+_seekcmd:
+ .byte 0x0F
+ .byte 0x00
+ .byte 0x00 ; Cylinder
+
+_recalcmd:
+ .byte 0x07
+ .byte 0
+
+_fdc_user:
+ .byte 0x00
+_fdc_addr:
+ .word 0x0000
+_fdc_statbuf:
+ .word 0,0,0,0
--- /dev/null
+-mwxuy
+-r
+-i fuzix.ihx
+-b _COMMONDATA=0xF800
+-b _CODE=0x0000
+-b _DISCARD=0xC000
+-l z80
+platform-plus3/crt0.rel
+platform-plus3/commonmem.rel
+platform-plus3/plus3.rel
+platform-plus3/zxvideo.rel
+platform-plus3/main.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+usermem_std-z80.rel
+platform-plus3/tricks.rel
+timer.rel
+kdata.rel
+usermem.rel
+platform-plus3/devices.rel
+platform-plus3/devfd.rel
+platform-plus3/floppy.rel
+devio.rel
+filesys.rel
+process.rel
+inode.rel
+syscall_exec16.rel
+syscall_fs.rel
+syscall_fs2.rel
+syscall_proc.rel
+syscall_other.rel
+tty.rel
+vt.rel
+font8x8.rel
+mm.rel
+simple.rel
+swap.rel
+devsys.rel
+platform-plus3/devtty.rel
+platform-plus3/devide.rel
+platform-plus3/devide_discard.rel
+platform-plus3/mbr.rel
+platform-plus3/blkdev.rel
+-e
--- /dev/null
+; UZI mnemonics for memory addresses etc
+
+U_DATA .equ 0xF800 ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes.
+
+Z80_TYPE .equ 1
+
+PROGBASE .equ 0x0000
+PROGLOAD .equ 0x0100
+
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+uint16_t ramtop = PROGTOP;
+uint16_t swap_dev;
+
+void platform_idle(void)
+{
+ __asm
+ halt
+ __endasm;
+}
+
+void platform_interrupt(void)
+{
+ tty_pollirq();
+//FIXME floppy_timer();
+ timer_interrupt();
+}
+
+/* Nothing to do for the map of init */
+void map_init(void)
+{
+}
--- /dev/null
+;
+; ZX Spectrum Plus 2A and Plus 3 hardware support
+;
+
+ .module plus3
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl _program_vectors
+ .globl platform_interrupt_all
+ .globl interrupt_handler
+ .globl unix_syscall_entry
+ .globl null_handler
+ .globl nmi_handler
+
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+ .globl map_process_save
+ .globl map_kernel_restore
+ .globl map_video
+ .globl unmap_video
+
+ .globl _kernel_flag
+ .globl port_map
+
+ ; exported debugging tools
+ .globl _trap_monitor
+ .globl outchar
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+
+ .globl outcharhex
+ .globl outhl, outde, outbc
+ .globl outnewline
+ .globl outstring
+ .globl outstringhex
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (above 0xC000 in page 3)
+; -----------------------------------------------------------------------------
+ .area _COMMONMEM
+
+_trap_monitor:
+ ;
+ ; Not so much a monitor as wait for space
+ ;
+ ld a, #0x7F
+ in a, (0xFE)
+ rra
+ jr c, _trap_monitor
+
+_trap_reboot:
+ di
+ im 1
+ ld bc, #0x7ffd
+ xor a ; 128K ROM, initial banks, low screen
+ out (c), a
+ rst 0 ; Into the ROM
+
+platform_interrupt_all:
+ ret
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (below 0xC000, only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+ .area _CODE
+
+init_early:
+ ld bc, #0x7ffd
+ ld a, #3 + 8 ; Screen into page 7
+ ret
+
+ .area _VIDEO
+
+init_hardware:
+ ; set system RAM size
+ ld hl, #128
+ ld (_ramsize), hl
+ ld hl, #(128 - 64) ; 64K for kernel/screen/etc
+ ld (_procmem), hl
+
+ call map_video
+ ; screen initialization
+ ; clear
+ ld hl, #0xC000
+ ld de, #0xC001
+ ld bc, #0x1800 ; There should be 0x17FF, but we are going
+ xor a ; to copy additional byte to avoid need of
+ ld (hl), a ; DE and HL increment before attribute
+ ldir ; initialization (2 bytes of RAM economy)
+
+ ; set color attributes
+ ld a, #7 ; black paper, white ink
+ ld bc, #0x300 - #1
+ ld (hl), a
+ ldir
+
+ call unmap_video
+ ret
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+ .area _COMMONMEM
+
+_program_vectors:
+ di
+ pop de
+ pop hl
+ push hl
+ push de
+
+ call map_process
+
+ ; write zeroes across all vectors
+ ld hl, #0
+ ld de, #1
+ ld bc, #0x007f ; program first 0x80 bytes only
+ ld (hl), #0x00
+ ldir
+
+ ; now install the interrupt vector at 0x0038
+ ld a, #0xC3 ; JP instruction
+ ld (0x0038), a
+ ld hl, #interrupt_handler
+ ld (0x0039), hl
+
+ ; set restart vector for UZI system calls
+ ld (0x0030), a ; (rst 30h is unix function call vector)
+ ld hl, #unix_syscall_entry
+ ld (0x0031), hl
+
+ ; Set vector for jump to NULL
+ ld (0x0000), a
+ ld hl, #null_handler ; to Our Trap Handler
+ ld (0x0001), hl
+
+ ld (0x0066), a ; Set vector for NMI
+ ld hl, #nmi_handler
+ ld (0x0067), hl
+ jr map_kernel
+
+switch_kernel:
+ ld a, (port_map)
+ and #0xF8 ; Preserve the other bits
+ or #0x05 ; Map 4,5,6,3
+switchit:
+ push bc
+ ld (port_map), a
+ ld bc, #0x1FFD
+ out (c), a
+ pop bc
+ ret
+
+switch_user:
+ ld a, (port_map)
+ and #0xF8 ; Preserve the other bits
+ and #0x01 ; Map 0,1,2,3
+ jr switch_kernel
+
+switch_video:
+ ld a, (port_map)
+ or #0x7 ; Map 4,7,6,3
+ jr switchit
+
+map_process:
+ ld a, h
+ or l
+ jr z, map_kernel
+map_process_save:
+map_process_always:
+ push af
+ call switch_user
+ pop af
+ ret
+
+unmap_video:
+map_kernel:
+map_kernel_restore:
+ push af
+ call switch_kernel
+ pop af
+ ret
+
+map_video:
+ push af
+ call switch_video
+ pop af
+ ret
+
+map_save:
+ push af
+ ld a, (port_map)
+ and #7
+ ld (map_store), a
+ pop af
+ ret
+
+map_restore:
+ push af
+ push hl
+ ld a, (port_map)
+ and #0xF8
+ ld hl, #map_store
+ or (hl)
+ call switchit
+ pop hl
+ pop af
+ ret
+;
+; We have no easy serial debug output instead just breakpoint this
+; address when debugging.
+;
+outchar:
+ ret
+
+ .area _COMMONDATA
+_kernel_flag:
+ .db 1
+port_map: ; place to store current map register values
+ .db 0 ; because we have no ability to read 1ffd port
+ ; to detect what page is mapped currently
+map_store:
+ .db 0
+ksave_map:
+ .db 0
--- /dev/null
+#
+# ZXSpectrum +3 uses unbanked kernel images (for now anyway)
+#
--- /dev/null
+#ifndef _SYSCONFIG_H
+#define _SYSCONFIG_H
+
+extern uint16_t sysconfig;
+
+#define CONF_PLUS3 0x0001 /* Plus3 (has 3DOS) */
+
+#endif
\ No newline at end of file
--- /dev/null
+export CPU = z80
--- /dev/null
+ .module tricks
+
+;
+; Standardised implementation of tricks.s for pure swapping Z80
+; systems. Wants moving from here to lib/
+;
+; FIXME: All of this can be moved into CODE ?
+;
+ .globl _ptab_alloc
+ .globl _newproc
+ .globl _chksigs
+ .globl _getproc
+ .globl _trap_monitor
+ .globl trap_illegal
+ .globl _inint
+ .globl _switchout
+ .globl _switchin
+ .globl _doexec
+ .globl _dofork
+ .globl _runticks
+ .globl unix_syscall_entry
+ .globl interrupt_handler
+ .globl _swapper
+ .globl _swapout
+
+ ; imported debug symbols
+ .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+ .area _COMMONMEM
+
+; Switchout switches out the current process, finds another that is READY,
+; possibly the same process, and switches it in. When a process is
+; restarted after calling switchout, it thinks it has just returned
+; from switchout().
+;
+; FIXME: make sure we optimise the switch to self case higher up the stack!
+;
+; This function can have no arguments or auto variables.
+_switchout:
+ di
+ call _chksigs
+ ; save machine state
+
+ ld hl, #0 ; return code set here is ignored, but _switchin can
+ ; return from either _switchout OR _dofork, so they must both write
+ ; U_DATA__U_SP with the following on the stack:
+ push hl ; return code
+ push ix
+ push iy
+ ld (U_DATA__U_SP), sp ; this is where the SP is restored in _switchin
+
+ ; set inint to false
+ xor a
+ ld (_inint), a
+
+ ; find another process to run (may select this one again)
+ call _getproc
+
+ push hl
+ call _switchin
+
+ ; we should never get here
+ call _trap_monitor
+
+badswitchmsg: .ascii "_switchin: FAIL"
+ .db 13, 10, 0
+swapped: .ascii "_switchin: SWAPPED"
+ .db 13, 10, 0
+
+_switchin:
+ di
+ pop bc ; return address
+ pop de ; new process pointer
+;
+; FIXME: do we actually *need* to restore the stack !
+;
+ push de ; restore stack
+ push bc ; restore stack
+
+ push de
+ ld hl, #P_TAB__P_PAGE_OFFSET
+ add hl, de ; process ptr
+ pop de
+
+ ld a, (hl)
+
+ or a
+ jr nz, not_swapped
+
+ ;
+ ; We are still on the departing processes stack, which is
+ ; fine for now.
+ ;
+ ld sp, #_swapstack
+ push hl
+ ; We will always swap out the current process
+ ld hl, (U_DATA__U_PTAB)
+ push hl
+ call _swapout
+ pop hl
+ pop hl
+ push de
+ call _swapper
+ pop de
+ pop hl
+ ld a, (hl)
+
+not_swapped:
+ ; check u_data->u_ptab matches what we wanted
+ ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab
+ or a ; clear carry flag
+ sbc hl, de ; subtract, result will be zero if DE==IX
+ jr nz, switchinfail
+
+ ; wants optimising up a bit
+ ld ix, (U_DATA__U_PTAB)
+ ; next_process->p_status = P_RUNNING
+ ld P_TAB__P_STATUS_OFFSET(ix), #P_RUNNING
+
+ ; Fix the moved page pointers
+ ; Just do one byte as that is all we use on this platform
+ ld a, P_TAB__P_PAGE_OFFSET(ix)
+ ld (U_DATA__U_PAGE), a
+ ; runticks = 0
+ ld hl, #0
+ ld (_runticks), hl
+
+ ; restore machine state -- note we may be returning from either
+ ; _switchout or _dofork
+ ld sp, (U_DATA__U_SP)
+
+ pop iy
+ pop ix
+ pop hl ; return code
+
+ ; enable interrupts, if the ISR isn't already running
+ ld a, (_inint)
+ or a
+ ret z ; in ISR, leave interrupts off
+ ei
+ ret ; return with interrupts on
+
+switchinfail:
+ call outhl
+ ld hl, #badswitchmsg
+ call outstring
+ ; something went wrong and we didn't switch in what we asked for
+ jp _trap_monitor
+
+fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
+
+;
+; Called from _fork. We are in a syscall, the uarea is live as the
+; parent uarea. The kernel is the mapped object.
+;
+_dofork:
+ ; always disconnect the vehicle battery before performing maintenance
+ di ; should already be the case ... belt and braces.
+
+ pop de ; return address
+ pop hl ; new process p_tab*
+ push hl
+ push de
+
+ ld (fork_proc_ptr), hl
+
+ ; prepare return value in parent process -- HL = p->p_pid;
+ ld de, #P_TAB__P_PID_OFFSET
+ add hl, de
+ ld a, (hl)
+ inc hl
+ ld h, (hl)
+ ld l, a
+
+ ; Save the stack pointer and critical registers.
+ ; When this process (the parent) is switched back in, it will be as if
+ ; it returns with the value of the child's pid.
+ push hl ; HL still has p->p_pid from above, the return value in the parent
+ push ix
+ push iy
+
+ ; save kernel stack pointer -- when it comes back in the parent we'll be in
+ ; _switchin which will immediately return (appearing to be _dofork()
+ ; returning) and with HL (ie return code) containing the child PID.
+ ; Hurray.
+ ld (U_DATA__U_SP), sp
+
+ ; now we're in a safe state for _switchin to return in the parent
+ ; process.
+
+ ld hl, (U_DATA__U_PTAB)
+ push hl
+ call _swapout
+ pop hl
+
+ ; now the copy operation is complete we can get rid of the stuff
+ ; _switchin will be expecting from our copy of the stack.
+ pop bc
+ pop bc
+ pop bc
+
+ ; Make a new process table entry, etc.
+ ld hl, (fork_proc_ptr)
+ push hl
+ call _newproc
+ pop bc
+
+ ; runticks = 0;
+ ld hl, #0
+ ld (_runticks), hl
+ ; in the child process, fork() returns zero.
+ ;
+ ; And we exit, with the kernel mapped, the child now being deemed
+ ; to be the live uarea. The parent is frozen in time and space as
+ ; if it had done a switchout().
+ ret
+;
+; We can keep a stack in common because we will complete our
+; use of it before we switch common block. In this case we have
+; a true common so it's even easier.
+;
+ .ds 128
+_swapstack:
--- /dev/null
+;
+; zx128 vt primitives
+;
+; We load this high on the Plus3 so we can page in page 7 for the
+; video overlay and frame buffer space. Any font will also go in page
+; 7. These routines must not call any code outside of common and their
+; own bank.
+;
+
+ .module zxvideo
+
+ ; exported symbols
+ .globl _plot_char
+ .globl _scroll_down
+ .globl _scroll_up
+ .globl _cursor_on
+ .globl _cursor_off
+ .globl _clear_lines
+ .globl _clear_across
+ .globl _do_beep
+
+ .area _VIDEO
+
+ ; colors are ignored everywhere for now
+
+videopos:
+ ld a,e
+ and #7
+ rrca
+ rrca
+ rrca
+ add a,d
+ ld d,e
+ ld e,a
+ ld a,d
+ and #0x18
+ or #0xC0 ; not 0x40 as in screen 7
+ ld d,a
+ ret
+
+_plot_char:
+ pop iy
+ pop hl
+ pop de ; D = x E = y
+ pop bc
+ push bc
+ push de
+ push hl
+ push iy
+
+ call videopos
+
+ ;
+ ; TODO: Map char 0x60 to a grave accent bitmap rather
+ ; than fudging with a quote
+ ;
+
+ ld b, #0 ; calculating offset in font table
+ ld a, c
+ cp #0x60
+ jr nz, nofiddle
+ ld a, #0x27
+nofiddle:
+ or a ; clear carry
+ rla
+ rl b
+ rla
+ rl b
+ rla
+ rl b
+ ld c, a
+
+ ld hl, #0x3C00 ; ROM font
+ add hl, bc ; hl points to first byte of char data
+
+
+ ; printing
+ ld c, #8
+plot_char_loop:
+ ld a, (hl)
+ ld (de), a
+ inc hl ; next byte of char data
+ inc d ; next screen line
+ dec c
+ jr nz, plot_char_loop
+ ret
+
+
+_clear_lines:
+ pop bc
+ pop hl
+ pop de ; E = line, D = count
+ push de
+ push hl
+ push bc
+
+clear_next_line:
+ push de
+ ld d, #0 ; from the column #0
+ ld b, d ; b = 0
+ ld c, #32 ; clear 32 cols
+ push bc
+ push de
+ call _clear_across
+
+ pop hl ; clear stack
+ pop hl
+
+ pop de
+ inc e
+ dec d
+ jr nz, clear_next_line
+
+ ret
+
+
+_clear_across:
+ pop iy
+ pop hl
+ pop de ; DE = coords
+ pop bc ; C = count
+ push bc
+ push de
+ push hl
+ push iy
+ call videopos ; first pixel line of first character in DE
+ push de
+ pop hl ; copy to hl
+ xor a
+
+ ; no boundary checks. Assuming that D + C < SCREEN_WIDTH
+
+clear_line:
+ ld b, #8 ; 8 pixel lines to clear for this char
+clear_char:
+ ld (de), a
+ inc d
+ dec b
+ jr nz, clear_char
+
+ ex de, hl
+ inc de
+ push de
+ pop hl
+
+ dec c
+ jr nz, clear_line
+ ret
+
+copy_line:
+ ; HL - source, DE - destination
+
+ ; convert line coordinates to screen coordinates both for DE and HL
+ push de
+ ex de, hl
+ call videopos
+ ex de, hl
+ pop de
+ call videopos
+
+ ld c, #8
+
+copy_line_nextchar:
+ push hl
+ push de
+
+ ld b, #32
+
+copy_pixel_line:
+ ld a, (hl)
+ ld (de), a
+ inc e
+ inc l
+ dec b
+ jr nz, copy_pixel_line
+
+ pop de
+ pop hl
+ inc d
+ inc h
+ dec c
+ jr nz, copy_line_nextchar
+ ret
+
+ ; TODO: the LDIR way should be much faster
+
+_scroll_down:
+ ; set HL = (0,22), DE = (0, 23)
+ xor a
+ ld d, a
+ ld h, a
+ ld l, #22
+ ld e, #23
+ ld c, #23 ; 23 lines to move
+
+loop_scroll_down:
+ push hl
+ push de
+ push bc
+
+ call copy_line
+
+ pop bc
+ pop de
+ pop hl
+
+ dec l
+ dec e
+ dec c
+ jr nz, loop_scroll_down
+
+ ret
+
+
+_scroll_up:
+ ; set HL = (0,1), DE = (0, 0)
+ xor a
+ ld d, a
+ ld e, a
+ ld h, a
+ ld l, #1
+ ld c, #23 ; 23 lines to move
+
+loop_scroll_up:
+ push hl
+ push de
+ push bc
+
+ call copy_line
+
+ pop bc
+ pop de
+ pop hl
+
+ inc l
+ inc e
+ dec c
+ jr nz, loop_scroll_up
+
+ ret
+
+_cursor_on:
+ pop bc
+ pop hl
+ pop de
+ push de
+ push hl
+ push bc
+ ld (cursorpos), de
+
+ call videopos
+ ld a, #7
+ add a, d
+ ld d, a
+ ld a, #0xFF
+ ld (de), a
+ ret
+_cursor_off:
+ ld de, (cursorpos)
+ call videopos
+ ld a, #7
+ add a, d
+ ld d, a
+ xor a
+ ld (de), a
+ ret
+
+ ; FIXME: now this is_do_silent_click actually
+_do_beep:
+ ld e, #0xFF ; length
+ ld c, #0xFE ; beeper port
+ ld l, #0x10 ; beeper bit
+loop_beep:
+ ld a, l
+ out (c), a
+ xor a
+ out (c), a
+ dec bc
+ ld a, b
+ or c
+ jr nz, loop_beep
+ ret
+
+;
+; Must live in video or common so we are sure we can get at it with
+; page 7 mapped
+;
+cursorpos:
+ .dw 0