--- /dev/null
+/*-----------------------------------------------------------------------*/
+/* IDE interface driver */
+/* 2014-11-02 Will Sowerbutts (unreleased UZI-mark4) */
+/* 2014-12-22 WRS ported to Fuzix */
+/* 2014-12-25 WRS updated to also support P112 GIDE */
+/*-----------------------------------------------------------------------*/
+
+/* Minor numbers
+
+ The kernel identifies our storage devices with an 8-bit minor number.
+
+ The top two bits of the minor identify the drive, allowing a maximum of four
+ drives, the current hardware supports only two but future controllers may
+ support more.
+
+ The bottom six bits of the minor identify the slice number. Slice 0
+ addresses "the whole drive", with no translation. Due to limitations within
+ the kernel only the first 32MB can currently be accessed through slice 0.
+
+ Slice 0 is intended to be used primarily for writing a partition table to
+ the drive, although it can also be used to store a single Fuzix filesystem
+ on an unpartitioned disk.
+
+ Slices 1+ are stored in a partition on the drive. The partition is stored in
+ a PC-style MBR partition table and must be a primary partition of type 0x5A.
+ Only the first suitable partition is found. Multiple filesystems are stored
+ in this partition by dividing it into 32MB slices.
+
+ To create the required device nodes, use:
+
+ mknod /dev/hda 60660 0
+ mknod /dev/hdb 60660 64
+ mknod /dev/hdc 60660 128
+ mknod /dev/hdd 60660 192
+
+ mknod /dev/hda1 60660 1
+ mknod /dev/hda2 60660 2
+ mknod /dev/hda3 60660 3
+ ...
+ mknod /dev/hda63 60660 63
+
+ mknod /dev/hdb1 60660 65
+ mknod /dev/hdb2 60660 66
+ mknod /dev/hdb3 60660 67
+ ...
+ mknod /dev/hdb63 60660 127
+
+ mknod /dev/hdc1 60660 129
+ mknod /dev/hdc2 60660 130
+ mknod /dev/hdc3 60660 131
+ ...
+ mknod /dev/hdc63 60660 191
+
+ mknod /dev/hdd1 60660 193
+ mknod /dev/hdd2 60660 194
+ mknod /dev/hdd3 60660 195
+ ...
+ mknod /dev/hdd63 60660 255
+*/
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <timer.h>
+#include "devide.h"
+
+typedef struct {
+ unsigned char status;
+ unsigned char chs_first[3];
+ unsigned char type;
+ unsigned char chs_last[3];
+ unsigned long lba_first;
+ unsigned long lba_count;
+} partition_table_entry_t;
+
+#define MBR_ENTRY_COUNT 4
+typedef struct {
+ unsigned char bootcode[446];
+ partition_table_entry_t partition[MBR_ENTRY_COUNT];
+ unsigned int signature;
+} master_boot_record_t;
+
+#define DRIVE_COUNT 2
+static unsigned char ide_drives_present; /* bitmap */
+static unsigned long ide_partition_start[DRIVE_COUNT];
+static unsigned int ide_slice_count[DRIVE_COUNT];
+
+__sfr __at IDE_REG_ALTSTATUS ide_reg_altstatus;
+__sfr __at IDE_REG_COMMAND ide_reg_command;
+__sfr __at IDE_REG_CONTROL ide_reg_control;
+__sfr __at IDE_REG_DATA ide_reg_data;
+__sfr __at IDE_REG_DEVHEAD ide_reg_devhead;
+__sfr __at IDE_REG_ERROR ide_reg_error;
+__sfr __at IDE_REG_FEATURES ide_reg_features;
+__sfr __at IDE_REG_LBA_0 ide_reg_lba_0;
+__sfr __at IDE_REG_LBA_1 ide_reg_lba_1;
+__sfr __at IDE_REG_LBA_2 ide_reg_lba_2;
+__sfr __at IDE_REG_LBA_3 ide_reg_lba_3;
+__sfr __at IDE_REG_SEC_COUNT ide_reg_sec_count;
+__sfr __at IDE_REG_STATUS ide_reg_status;
+
+static void devide_read_data(void *buffer, unsigned char ioport) __naked
+{
+ buffer; ioport; /* silence compiler warning */
+ __asm
+ pop de ; return address
+ pop hl ; buffer address
+ pop bc ; IO port number (in c)
+ push bc ; restore stack
+ push hl
+ push de
+
+ ld b, #0 ; setup count
+ inir ; first 256 bytes
+ inir ; second 256 bytes
+ ret
+ __endasm;
+}
+
+static void devide_write_data(void *buffer, unsigned char ioport) __naked
+{
+ buffer; ioport; /* silence compiler warning */
+ __asm
+ pop de ; return address
+ pop hl ; buffer address
+ pop bc ; IO port number (in c)
+ push bc ; restore stack
+ push hl
+ push de
+
+ ld b, #0 ; setup count
+ otir ; first 256 bytes
+ otir ; second 256 bytes
+ ret
+ __endasm;
+}
+
+static void devide_delay(void)
+{
+ timer_t timeout;
+
+ timeout = set_timer_ms(25);
+
+ while(!timer_expired(timeout))
+ platform_idle();
+}
+
+static bool devide_wait(unsigned char bits)
+{
+ unsigned char status;
+ timer_t timeout;
+
+ timeout = set_timer_ms(500);
+
+ while(true){
+ status = ide_reg_status;
+
+ if((status & (IDE_STATUS_BUSY | IDE_STATUS_ERROR | bits)) == bits)
+ return true;
+
+ if((status & (IDE_STATUS_BUSY | IDE_STATUS_ERROR)) == IDE_STATUS_ERROR){
+ kprintf("ide error, status=%x\n", status);
+ return false;
+ }
+
+ if(timer_expired(timeout)){
+ kprintf("ide timeout, status=%x\n", status);
+ return false;
+ }
+ };
+}
+
+static bool devide_read_sector(unsigned char *buffer)
+{
+ if(!devide_wait(IDE_STATUS_DATAREQUEST))
+ return false;
+
+ devide_read_data(buffer, IDE_REG_DATA);
+
+ return true;
+}
+
+static bool devide_write_sector(unsigned char *buffer)
+{
+ if(!devide_wait(IDE_STATUS_DATAREQUEST))
+ return false;
+
+ devide_write_data(buffer, IDE_REG_DATA);
+
+ if(!devide_wait(IDE_STATUS_READY))
+ return false;
+
+ return true;
+}
+
+static int devide_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ unsigned char *target, *p;
+ unsigned long lba;
+ unsigned char drive;
+
+ drive = minor >> 6;
+ minor = minor & 0x3F;
+
+ if(rawflag == 0) {
+ target = udata.u_buf->bf_data;
+ lba = udata.u_buf->bf_blk;
+ }else
+ goto xferfail;
+
+ /* minor 0 is the whole disk and requires no translation */
+ if(minor > 0){
+ /* minor 1+ are slices located within the partition */
+ lba += (ide_partition_start[drive]);
+ lba += ((unsigned long)(minor-1) << 16);
+ }
+
+#if 0
+ ide_reg_lba_3 = ((lba >> 24) & 0xF) | ((drive == 0) ? 0xE0 : 0xF0); // select drive, start loading LBA
+ ide_reg_lba_2 = (lba >> 16);
+ ide_reg_lba_1 = (lba >> 8);
+ ide_reg_lba_0 = lba;
+#else
+ /* sdcc sadly unable to figure this out for itself yet */
+ p = (unsigned char *)&lba;
+ ide_reg_lba_3 = (p[3] & 0x0F) | ((drive == 0) ? 0xE0 : 0xF0); // select drive, start loading LBA
+ ide_reg_lba_2 = p[2];
+ ide_reg_lba_1 = p[1];
+ ide_reg_lba_0 = p[0];
+#endif
+ ide_reg_sec_count = 1;
+
+ if(is_read){
+ ide_reg_command = IDE_CMD_READ_SECTOR;
+ if(!devide_read_sector(target))
+ goto xferfail;
+ }else{
+ ide_reg_command = IDE_CMD_WRITE_SECTOR;
+ if(!devide_write_sector(target))
+ goto xferfail;
+ }
+
+ return 1;
+xferfail:
+ udata.u_error = EIO;
+ return -1;
+}
+
+int devide_open(uint8_t minor, uint16_t flags)
+{
+ unsigned char drive;
+ flags; /* not used */
+
+ drive = minor >> 6;
+ minor = minor & 0x3F;
+
+ if(ide_drives_present & (1 << drive) && (minor == 0 || minor < ide_slice_count[drive]))
+ return 0;
+
+ udata.u_error = ENODEV;
+ return -1;
+}
+
+int devide_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag; /* not used */
+ return devide_transfer(minor, true, rawflag);
+}
+
+int devide_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag; /* not used */
+ return devide_transfer(minor, false, rawflag);
+}
+
+void devide_init_drive(unsigned char drive)
+{
+ unsigned char *buffer, i, select;
+ master_boot_record_t *mbr;
+
+ switch(drive){
+ case 0: select = 0xE0; break;
+ case 1: select = 0xF0; break;
+ default: return;
+ }
+
+ kprintf("hd%c: ", 'a' + drive);
+ ide_partition_start[drive] = 0;
+ ide_slice_count[drive] = 0;
+
+ /* reset the drive */
+ ide_reg_devhead = select;
+ ide_reg_control = 0x06; /* assert reset, no interrupts */
+ devide_delay();
+ ide_reg_control = 0x02; /* release reset, no interruptst */
+ devide_delay();
+ if(!devide_wait(IDE_STATUS_READY))
+ return;
+
+#ifdef IDE_8BIT_ONLY
+ /* set 8-bit mode -- mostly only supported by CF cards */
+ ide_reg_devhead = select;
+ if(!devide_wait(IDE_STATUS_READY))
+ return;
+ ide_reg_features = 0x01;
+ ide_reg_command = IDE_CMD_SET_FEATURES;
+#endif
+
+ /* confirm drive has LBA support */
+ if(!devide_wait(IDE_STATUS_READY))
+ return;
+ ide_reg_command = IDE_CMD_IDENTIFY;
+
+ /* allocate temporary sector buffer memory */
+ buffer = (unsigned char *)tmpbuf();
+
+ if(!devide_read_sector(buffer))
+ goto failout;
+
+ if(!(buffer[99] & 0x02)){
+ kputs("LBA unsupported.\n");
+ goto failout;
+ }
+
+ /* read first sector (looking for the partition table) */
+ if(!devide_wait(IDE_STATUS_READY))
+ goto failout;
+
+ ide_reg_devhead = select;
+ ide_reg_lba_2 = 0;
+ ide_reg_lba_1 = 0;
+ ide_reg_lba_0 = 0;
+ ide_reg_sec_count = 1;
+ ide_reg_command = IDE_CMD_READ_SECTOR;
+
+ if(!devide_read_sector(buffer))
+ goto failout;
+
+ /* if we get this far the drive is apparently present and functioning */
+ ide_drives_present |= (1 << drive);
+
+ /* check for MBR table signature */
+ mbr = (master_boot_record_t*)buffer;
+ if(mbr->signature != 0xaa55){
+ kputs("no partition table\n");
+ goto failout;
+ }
+
+ /* look for a fuzix partition (type 0x5A) */
+ for(i=0; i<MBR_ENTRY_COUNT; i++){
+ if(mbr->partition[i].type == 0x5A){
+ ide_partition_start[drive] = mbr->partition[i].lba_first;
+ ide_slice_count[drive] = mbr->partition[i].lba_count >> 16;
+ break;
+ }
+ }
+
+ kprintf("%d slices\n", ide_slice_count[drive]);
+
+failout:
+ brelse((bufptr)buffer);
+}
+
+void devide_init(void)
+{
+ unsigned char d;
+
+ ide_drives_present = 0;
+
+ for(d=0; d<DRIVE_COUNT; d++)
+ devide_init_drive(d);
+}
--- /dev/null
+#ifndef __DEVIDE_DOT_H__
+#define __DEVIDE_DOT_H__
+
+#include "config.h"
+
+/* IDE Drive Configuration (in config.h)
+
+ Define DEVICE_IDE if IDE hardware is present on your platform.
+
+ Define IDE_8BIT_ONLY if the system implements only half of the 16-bit data
+ bus (eg N8VEM Mark IV).
+
+ If the IDE registers appear in one contiguous block then define IDE_REG_BASE
+ and either IDE_REG_CS0_FIRST or IDE_REG_CS1_FIRST.
+
+ If the IDE registers appear in two non-contiguous blocks then define both
+ IDE_REG_CS0_BASE and IDE_REG_CS1_BASE.
+
+ If neither of these is suitable just define the address of each register
+ ie IDE_REG_DATA, IDE_REG_ERROR, etc.
+*/
+
+void devide_init(void);
+int devide_open(uint8_t minor, uint16_t flags);
+int devide_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int devide_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+
+#ifdef IDE_REG_BASE
+#ifdef IDE_REG_CS0_FIRST
+#define IDE_REG_CS0_BASE (IDE_REG_BASE+0x00)
+#define IDE_REG_CS1_BASE (IDE_REG_BASE+0x08)
+#endif
+#ifdef IDE_REG_CS1_FIRST
+#define IDE_REG_CS0_BASE (IDE_REG_BASE+0x08)
+#define IDE_REG_CS1_BASE (IDE_REG_BASE+0x00)
+#endif
+#endif
+
+#ifdef IDE_REG_CS0_BASE
+#define IDE_REG_ALTSTATUS (IDE_REG_CS0_BASE + 0x06)
+#define IDE_REG_CONTROL (IDE_REG_CS0_BASE + 0x06)
+#endif
+#ifdef IDE_REG_CS1_BASE
+#define IDE_REG_DATA (IDE_REG_CS1_BASE + 0x00)
+#define IDE_REG_ERROR (IDE_REG_CS1_BASE + 0x01)
+#define IDE_REG_FEATURES (IDE_REG_CS1_BASE + 0x01)
+#define IDE_REG_SEC_COUNT (IDE_REG_CS1_BASE + 0x02)
+#define IDE_REG_LBA_0 (IDE_REG_CS1_BASE + 0x03)
+#define IDE_REG_LBA_1 (IDE_REG_CS1_BASE + 0x04)
+#define IDE_REG_LBA_2 (IDE_REG_CS1_BASE + 0x05)
+#define IDE_REG_LBA_3 (IDE_REG_CS1_BASE + 0x06)
+#define IDE_REG_DEVHEAD (IDE_REG_CS1_BASE + 0x06)
+#define IDE_REG_STATUS (IDE_REG_CS1_BASE + 0x07)
+#define IDE_REG_COMMAND (IDE_REG_CS1_BASE + 0x07)
+#endif
+
+/* IDE status register bits */
+#define IDE_STATUS_BUSY 0x80
+#define IDE_STATUS_READY 0x40
+#define IDE_STATUS_DEVFAULT 0x20
+#define IDE_STATUS_SEEKCOMPLETE 0x10 // not important
+#define IDE_STATUS_DATAREQUEST 0x08
+#define IDE_STATUS_CORRECTED 0x04 // not important
+#define IDE_STATUS_INDEX 0x02 // not important
+#define IDE_STATUS_ERROR 0x01
+
+/* IDE command codes */
+#define IDE_CMD_READ_SECTOR 0x20
+#define IDE_CMD_WRITE_SECTOR 0x30
+#define IDE_CMD_IDENTIFY 0xEC
+#define IDE_CMD_SET_FEATURES 0xEF
+
+#endif
--- /dev/null
+CSRCS += devices.c main.c devtty.c
+DSRCS = ../dev/devide.c
+
+ASRCS = crt0.s z180.s mark4.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 *.map *.ihx *.bin
+
+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 *~ fuzix.com makecpmloader
+
+z180.rel: z180.s kernel.def ../cpu-z180/z180.s
+
+image:
+ ../cpm-loader/makecpmloader ../cpm-loader/cpmload.bin ../fuzix.bin 0x88 fuzix.com
--- /dev/null
+This is UZI for the N8VEM Mark IV SBC
+By Will Sowerbutts <will@sowerbutts.com>
+
+Assumes an N8VEM Mark IV SBC fitted with 512KB RAM, with the RS232 port as
+tty1, and the RS422 port as tty2. Does not yet support any ECB peripheral
+boards.
+
+To build, edit ../Makefile to read:
+
+export TARGET= n8vem-mark4
+export CPU = z180
--- /dev/null
+;
+; Common on z80pack is at 0xF000 as defined by hardware.
+;
+
+ .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) */
+#undef CONFIG_PROFIL
+/* Multiple processes in memory at once */
+#define CONFIG_MULTI
+/* Single tasking */
+#undef CONFIG_SINGLETASK
+/* CP/M emulation */
+#undef CONFIG_CPM_EMU
+/* Fixed banking */
+#define CONFIG_BANK_FIXED
+/* 8 60K banks, 1 is kernel */
+#define MAX_MAPS 8
+#define MAP_SIZE 0xF000U
+
+/* Banks as reported to user space */
+#define CONFIG_BANKS 1
+
+#define TICKSPERSEC 100U /* Ticks per second */
+#define PROGBASE 0x0000 /* also data base */
+#define PROGLOAD 0x0100 /* also data base */
+#define PROGTOP 0xF000 /* Top of program, base of U_DATA copy */
+#define PROC_SIZE 64 /* Memory needed per process */
+
+/* WRS: this is probably wrong -- we want to swap the full 64K minus the common code */
+/* For now let's just use something and fix this up later when we have a swap device */
+#define SWAP_SIZE 0x7F /* 63.5K in blocks (which is the wrong number) */
+#define SWAPBASE 0x0000 /* start at the base of user mem */
+#define SWAPTOP 0xFF00 /* can we stop at the top? not sure how. let's stop short. */
+#define MAX_SWAPS 10 /* Well, that depends really, hmmmmmm. Pick a number, any number. */
+
+#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 (0x0081) /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 2
+
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+//#define SWAPDEV (256 + 1) /* Device for swapping. (z80pack drive J) */
+#define NBUFS 10 /* Number of block buffers */
+#define NMOUNTS 4 /* Number of mounts at a time */
+
+/* Hardware parameters */
+#define Z180_IO_BASE 0x40
+
+#define DEVICE_IDE /* enable if IDE interface present */
+#define IDE_REG_BASE 0x80
+#define IDE_8BIT_ONLY
+#define IDE_REG_CS1_FIRST
--- /dev/null
+.module cpmload
+.area _LOADER (ABS)
+
+ ; CP/M will load us at 0x0100
+ ; We want to relocate our payload (the kernel) and jump into it.
+ ; We put a small stub at the very bottom of memory, copy the kernel into
+ ; place above us, then jump into it.
+
+ .org 0x100
+ di
+ ; copy ourselves to the very bottom of memory -- 0x00 upwards
+ ld de, #0
+ ld hl, #(doload) ; start of loader code
+ ld bc, #(endloader-doload) ; length of our loader
+ ldir ; copy copy copy!
+ ld hl, (load_address)
+ ld sp, hl ; stash copy of entry vector in SP for now
+ ex de, hl ; load_address to de
+ ld hl, #payload_start
+ ld bc, (load_length)
+ jp 0 ; jump and perform copy in low memory
+
+ ; this code gets copied to .org 0
+doload:
+ ldir ; copy image into correct place
+ ld hl, #0
+ add hl, sp ; recover entry vector
+ jp (hl) ; run image
+endloader: ; end of code to copy
+
+ ; the data is in a trailer, with a 6-byte header:
+load_address:
+ .ds 2
+load_length:
+ .ds 2
+payload_start:
--- /dev/null
+; 2013-12-18 William R Sowerbutts
+
+ .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__COMMONMEM
+ .globl l__COMMONMEM
+ .globl s__DISCARD
+ .globl l__DISCARD
+ .globl s__DATA
+ .globl l__DATA
+ .globl kstack_top
+
+ ; startup code
+ .area _CODE
+init:
+ di
+ ld sp, #kstack_top
+
+ ; move the common memory where it belongs
+ ld hl, #s__INITIALIZER
+ ld de, #s__COMMONMEM
+ ld bc, #l__COMMONMEM
+ ldir
+ ; 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
+
+ ; Configure memory map
+ call init_early
+
+ ; Hardware setup
+ call init_hardware
+
+ ; Call the C main routine
+ call _fuzix_main
+
+ ; fuzix_main() shouldn't return, but if it does...
+ di
+stop: halt
+ jr stop
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <tty.h>
+#include <devsys.h>
+#include <devtty.h>
+#include <devide.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+// minor open close read write ioctl
+// -----------------------------------------------------------------
+ /* 0: /dev/hd IDE-8 interface */
+ { devide_open, no_close, devide_read, devide_write, no_ioctl },
+ /* 1: /dev/sd SD interface */
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 2: /dev/tty serial ports */
+ { tty_open, tty_close, tty_read, tty_write, tty_ioctl },
+ /* 3: /dev/lpr Unused slot (pad to keep system devices at index 4) */
+ { 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 },
+ /* 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)
+{
+ devide_init();
+}
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <devtty.h>
+#include "config.h"
+#include <z180.h>
+
+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 },
+};
+
+void tty_setup(uint8_t minor)
+{
+ minor;
+}
+
+/* For the moment */
+int tty_carrier(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+void tty_pollirq_asci0(void)
+{
+ while(ASCI_STAT0 & 0x80){
+ tty_inproc(1, ASCI_RDR0);
+ }
+}
+
+void tty_pollirq_asci1(void)
+{
+ while(ASCI_STAT1 & 0x80){
+ tty_inproc(2, ASCI_RDR1);
+ }
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ switch(minor){
+ case 1:
+ while(!(ASCI_STAT0 & 2));
+ ASCI_TDR0 = c;
+ break;
+ case 2:
+ while(!(ASCI_STAT1 & 2));
+ ASCI_TDR1 = c;
+ break;
+ }
+}
+
+bool tty_writeready(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+/* kernel writes to system console -- never sleep! */
+void kputchar(char c)
+{
+ tty_putc(1, c);
+ if(c == '\n')
+ tty_putc(1, '\r');
+}
+
--- /dev/null
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+void tty_putc(uint8_t minor, unsigned char c);
+bool tty_writeready(uint8_t minor);
+void tty_pollirq_asci0(void);
+void tty_pollirq_asci1(void);
+#endif
--- /dev/null
+; UZI mnemonics for memory addresses etc
+
+U_DATA .equ 0xF000 ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes.
+NMOS_Z80 .equ 0
+
+OS_BANK .equ 0x00 ; value from include/kernel.h
+
+; N8VEM Mark IV mnemonics
+FIRST_RAM_BANK .equ 0x80 ; low 512K of physical memory is ROM/ECB window.
+RAM_KB .equ 512
+Z180_IO_BASE .equ 0x40
+MARK4_IDE_BASE .equ 0x80
+MARK4_IDE_DATA .equ MARK4_IDE_BASE
+
+; No standard clock speed for the Mark IV board, but this is a common choice.
+CPU_CLOCK_KHZ .equ 36864 ; 18.432MHz * 2
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include "config.h"
+#include <z180.h>
+
+uint16_t ramtop = PROGTOP;
+extern unsigned char irqvector;
+
+void z180_timer_interrupt(void)
+{
+ unsigned char a;
+
+ /* we have to read both of these registers in order to reset the timer */
+ a = TIME_TMDR0L;
+ a = TIME_TCR;
+
+ timer_interrupt();
+}
+
+
+void pagemap_init(void)
+{
+ int i;
+
+ /* N8VEM SBC Mark IV has RAM in the top 512K of physical memory.
+ * First 64K is used by the kernel.
+ * Each process gets the full 64K for now.
+ * Page size is 4KB. */
+ for(i = 0x90; i < 0x100; i+=0x10)
+ pagemap_add(i);
+}
+
+void platform_idle(void)
+{
+ /* Let's go to sleep while we wait for something to interrupt us;
+ * Makes the Mark IV's run LED go red, which amuses me greatly. */
+ __asm
+ halt
+ __endasm;
+}
+
+void platform_interrupt(void)
+{
+ switch(irqvector){
+ case Z180_INT_TIMER0:
+ z180_timer_interrupt();
+ return;
+ case Z180_INT_ASCI0:
+ tty_pollirq_asci0();
+ return;
+ case Z180_INT_ASCI1:
+ tty_pollirq_asci1();
+ return;
+ default:
+ return;
+ }
+}
+
+void map_init(void)
+{
+ /* clone udata and stack into a regular process bank, return with common memory
+ for the new process loaded */
+ copy_and_map_process(&init_process->p_page);
+ /* kernel bank udata (0x300 bytes) is never used again -- could be reused? */
+}
--- /dev/null
+; 2014-12-16 William R Sowerbutts
+; N8VEM Mark IV SBC hardware specific code
+
+ .module mark4
+ .z180
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl outchar
+ .globl outcharhex
+ .globl platform_interrupt_all
+ .globl _trap_monitor
+
+ .globl map_kernel
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+ ;.globl map_process_only
+
+ ; imported symbols
+ .globl z180_init_hardware
+ .globl z180_init_early
+ .globl _ramsize
+ .globl _procmem
+ .globl outhl
+ .globl outnewline
+
+ .include "kernel.def"
+ .include "../cpu-z180/z180.def"
+ .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; Initialisation code
+; -----------------------------------------------------------------------------
+ .area _DISCARD
+
+init_early:
+ jp z180_init_early
+
+init_hardware:
+ ; set system RAM size
+ ld hl, #RAM_KB
+ ld (_ramsize), hl
+ ld hl, #(RAM_KB-64) ; 64K for kernel
+ ld (_procmem), hl
+
+ ; enable ASCI interrupts
+ in0 a, (ASCI_STAT0)
+ or #0x08 ; enable ASCI0 receive interrupts
+ out0 (ASCI_STAT0), a
+ in0 a, (ASCI_ASEXT0)
+ and #0x7f ; disable RDRF interrupt inhibit
+ out0 (ASCI_ASEXT0), a
+ in0 a, (ASCI_STAT1)
+ or #0x08 ; enable ASCI1 receive interrupts
+ out0 (ASCI_STAT1), a
+ in0 a, (ASCI_ASEXT1)
+ and #0x7f ; disable RDRF interrupt inhibit
+ out0 (ASCI_ASEXT1), a
+
+ jp z180_init_hardware
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0xF000 upwards)
+; -----------------------------------------------------------------------------
+ .area _COMMONMEM
+
+; outchar: Wait for UART TX idle, then print the char in A
+; destroys: AF
+outchar:
+ push bc
+ ld b, a
+ ; wait for transmitter to be idle
+ocloop: in0 a, (ASCI_STAT0)
+ bit 1, a ; and 0x02
+ jr z, ocloop ; loop while busy
+ ; now output the char to serial port
+ ld a, b
+ out0 (ASCI_TDR0), a
+ pop bc
+ ret
+
+platform_interrupt_all:
+ ret
+
+map_kernel: ; map the kernel into the low 60K, leaves common memory unchanged
+ push af
+.if DEBUGBANK
+ ld a, #'K'
+ call outchar
+.endif
+ ld a, #(OS_BANK + FIRST_RAM_BANK)
+ out0 (MMU_BBR), a
+ pop af
+ ret
+
+; this "map_process" business makes no sense on mark4 since we'd switch stacks
+; and the RET would thus lose its return address. oh damn. I suppose we could
+; pop the stack address into hl, then jp (hl) or whatever. let's try and get by
+; without it.
+;
+; map_process: ; if HL=0 call map_kernel, else map the full 64K in bank pointed to by HL
+; ld a, h
+; or l
+; jr z, map_kernel
+; ld a, (hl)
+; out0 (MMU_BBR), a
+; out0 (MMU_CBR), a
+; ret
+
+; map_process_only: ; as map_process, but does not modify common memory
+; ld a, h
+; or l
+; jr z, map_kernel
+; ld a, (hl)
+; out0 (MMU_BBR), a
+; ret
+
+map_process_always: ; map the process into the low 60K based on current common mem (which is unchanged)
+ push af
+.if DEBUGBANK
+ ld a, #'='
+ call outchar
+.endif
+ ld a, (U_DATA__U_PAGE)
+ out0 (MMU_BBR), a
+.if DEBUGBANK
+ call outcharhex
+.endif
+ ; MMU_CBR is left unchanged
+ pop af
+ ret
+
+map_save: ; save the current process/kernel mapping
+ push af
+ in0 a, (MMU_BBR)
+ ld (map_store), a
+ pop af
+ ret
+
+map_restore: ; restore the saved process/kernel mapping
+ push af
+.if DEBUGBANK
+ ld a, #'-'
+ call outchar
+.endif
+ ld a, (map_store)
+ out0 (MMU_BBR), a
+.if DEBUGBANK
+ call outcharhex
+.endif
+ pop af
+ ret
+
+_trap_monitor:
+ di
+ call outnewline
+ pop hl
+ call outhl
+ call outnewline
+ pop hl
+ call outhl
+ call outnewline
+ pop hl
+ call outhl
+ call outnewline
+ pop hl
+ call outhl
+ call outnewline
+ pop hl
+ call outhl
+ call outnewline
+ halt
+ jr _trap_monitor
+
+map_store: ; storage for map_save/map_restore
+ .db 0
--- /dev/null
+-mwxuy
+-i uzi.ihx
+-b _CODE=0x0088
+-b _COMMONMEM=0xF000
+-b _DISCARD=0xE000
+-l z180
+platform-n8vem-mark4/crt0.rel
+platform-n8vem-mark4/commonmem.rel
+platform-n8vem-mark4/z180.rel
+platform-n8vem-mark4/mark4.rel
+platform-n8vem-mark4/main.rel
+start.rel
+version.rel
+lowlevel-z180.rel
+usermem_std-z180.rel
+timer.rel
+kdata.rel
+usermem.rel
+platform-n8vem-mark4/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
+mm.rel
+bankfixed.rel
+swap.rel
+devsys.rel
+platform-n8vem-mark4/devtty.rel
+platform-n8vem-mark4/devide.rel
+-e
--- /dev/null
+.include "../cpu-z180/z180.s"
--- /dev/null
+CSRCS += devices.c main.c devtty.c
+DSRCS = ../dev/devide.c
+
+ASRCS = crt0.s z180.s p112.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 *.map *.ihx *.bin
+
+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 *~ fuzix.com
+
+z180.rel: z180.s kernel.def ../cpu-z180/z180.s
+
+image:
+ ../cpm-loader/makecpmloader ../cpm-loader/cpmload.bin ../fuzix.bin 0x88 fuzix.com
--- /dev/null
+This is UZI for the DX-Designs P112
+By Will Sowerbutts <will@sowerbutts.com>
+
+Assumes a P112 fitted with 1024KB RAM and G-IDE
+
+Only supported storage device at this time is the G-IDE
+
+To build, edit ../Makefile to read:
+
+export TARGET= p112
+export CPU = z180
--- /dev/null
+;
+; Common on z80pack is at 0xF000 as defined by hardware.
+;
+
+ .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) */
+#undef CONFIG_PROFIL
+/* Multiple processes in memory at once */
+#define CONFIG_MULTI
+/* Single tasking */
+#undef CONFIG_SINGLETASK
+/* CP/M emulation */
+#undef CONFIG_CPM_EMU
+/* Fixed banking */
+#define CONFIG_BANK_FIXED
+/* 8 60K banks, 1 is kernel */
+#define MAX_MAPS 16
+#define MAP_SIZE 0xF000U
+
+/* Banks as reported to user space */
+#define CONFIG_BANKS 1
+
+#define TICKSPERSEC 100U /* Ticks per second */
+#define PROGBASE 0x0000 /* also data base */
+#define PROGLOAD 0x0100 /* also data base */
+#define PROGTOP 0xF000 /* Top of program, base of U_DATA copy */
+#define PROC_SIZE 64 /* Memory needed per process */
+
+/* WRS: this is probably wrong -- we want to swap the full 64K minus the common code */
+/* For now let's just use something and fix this up later when we have a swap device */
+#define SWAP_SIZE 0x7F /* 63.5K in blocks (which is the wrong number) */
+#define SWAPBASE 0x0000 /* start at the base of user mem */
+#define SWAPTOP 0xFF00 /* can we stop at the top? not sure how. let's stop short. */
+#define MAX_SWAPS 10 /* Well, that depends really, hmmmmmm. Pick a number, any number. */
+
+#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 (0x0081) /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 4
+
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+//#define SWAPDEV (256 + 1) /* Device for swapping. (z80pack drive J) */
+#define NBUFS 10 /* Number of block buffers */
+#define NMOUNTS 4 /* Number of mounts at a time */
+
+/* Hardware parameters */
+#define Z180_IO_BASE 0x00
+
+#define DEVICE_IDE /* enable if IDE interface present */
+#define IDE_REG_BASE 0x50
+#define IDE_REG_CS0_FIRST
+#define IDE_REG_CS0_BASE (IDE_REG_BASE+0x00)
+#define IDE_REG_CS1_BASE (IDE_REG_BASE+0x08)
--- /dev/null
+; 2013-12-18 William R Sowerbutts
+
+ .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__COMMONMEM
+ .globl l__COMMONMEM
+ .globl s__DISCARD
+ .globl l__DISCARD
+ .globl s__DATA
+ .globl l__DATA
+ .globl kstack_top
+
+ ; startup code
+ .area _CODE
+init:
+ di
+ ld sp, #kstack_top
+
+ ; move the common memory where it belongs
+ ld hl, #s__INITIALIZER
+ ld de, #s__COMMONMEM
+ ld bc, #l__COMMONMEM
+ ldir
+ ; 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
+
+ ; Configure memory map
+ call init_early
+
+ ; Hardware setup
+ call init_hardware
+
+ ; Call the C main routine
+ call _fuzix_main
+
+ ; fuzix_main() shouldn't return, but if it does...
+ di
+stop: halt
+ jr stop
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <tty.h>
+#include <devsys.h>
+#include <devtty.h>
+#include <devide.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+// minor open close read write ioctl
+// -----------------------------------------------------------------
+ /* 0: /dev/hd IDE-8 interface */
+ { devide_open, no_close, devide_read, devide_write, no_ioctl },
+ /* 1: /dev/sd SD interface */
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 2: /dev/tty serial ports */
+ { tty_open, tty_close, tty_read, tty_write, tty_ioctl },
+ /* 3: /dev/lpr Unused slot (pad to keep system devices at index 4) */
+ { 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 },
+ /* 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)
+{
+ devide_init();
+}
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <devtty.h>
+#include "config.h"
+#include <z180.h>
+
+char tbuf1[TTYSIZ];
+char tbuf2[TTYSIZ];
+char tbuf3[TTYSIZ];
+char tbuf4[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 },
+ { tbuf3, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ/2 },
+ { tbuf4, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ/2 },
+};
+
+void tty_setup(uint8_t minor)
+{
+ minor;
+}
+
+/* For the moment */
+int tty_carrier(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+void tty_pollirq_asci0(void)
+{
+ while(ASCI_STAT0 & 0x80){
+ tty_inproc(1, ASCI_RDR0);
+ }
+}
+
+void tty_pollirq_asci1(void)
+{
+ while(ASCI_STAT1 & 0x80){
+ tty_inproc(2, ASCI_RDR1);
+ }
+}
+
+void tty_pollirq_escc(void)
+{
+ unsigned char rr3;
+ ESCC_CTRL_A = 0x03; /* select read register 3 */
+ rr3 = ESCC_CTRL_A;
+
+ if(rr3 & 0x20){ /* channel A RX pending */
+ tty_inproc(1, ESCC_DATA_A);
+ }
+ if(rr3 & 0x04){ /* channel B RX pending */
+ tty_inproc(2, ESCC_DATA_B);
+ }
+ if(rr3 & (~0x24)){
+ kputs("[escc hurts]");
+ }
+
+ ESCC_CTRL_A = 0x38; /* reset interrupt under service */
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ switch(minor){
+ case 1:
+ while(!(ESCC_CTRL_A & 4));
+ ESCC_DATA_A = c;
+ break;
+ case 2:
+ while(!(ESCC_CTRL_B & 4));
+ ESCC_DATA_B = c;
+ break;
+ case 3:
+ while(!(ASCI_STAT0 & 2));
+ ASCI_TDR0 = c;
+ break;
+ case 4:
+ while(!(ASCI_STAT1 & 2));
+ ASCI_TDR1 = c;
+ break;
+ }
+}
+
+bool tty_writeready(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+/* kernel writes to system console -- never sleep! */
+void kputchar(char c)
+{
+ tty_putc(1, c);
+ if(c == '\n')
+ tty_putc(1, '\r');
+}
--- /dev/null
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+void tty_putc(uint8_t minor, unsigned char c);
+bool tty_writeready(uint8_t minor);
+void tty_pollirq_escc(void);
+void tty_pollirq_asci0(void);
+void tty_pollirq_asci1(void);
+#endif
--- /dev/null
+; UZI mnemonics for memory addresses etc
+
+U_DATA .equ 0xF000 ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes.
+NMOS_Z80 .equ 0
+
+OS_BANK .equ 0x00 ; value from include/kernel.h
+
+; P112
+FIRST_RAM_BANK .equ 0x00 ; all memory is RAM on P112
+RAM_KB .equ 1024
+Z180_IO_BASE .equ 0x00
+P112_IDE_BASE .equ 0x50
+P112_IDE_DATA .equ (P112_IDE_BASE+0x08)
+
+; Believe most P112 kits shipped with 16MHz oscillators, I have tried faster
+; (18.432MHz) but this made the machine unstable.
+CPU_CLOCK_KHZ .equ 16000 ; 16MHz is the stock crystal
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <z180.h>
+#include "config.h"
+
+uint16_t ramtop = PROGTOP;
+extern unsigned char irqvector;
+
+void z180_timer_interrupt(void)
+{
+ unsigned char a;
+
+ /* we have to read both of these registers in order to reset the timer */
+ a = TIME_TMDR0L;
+ a = TIME_TCR;
+
+ timer_interrupt();
+}
+
+
+void pagemap_init(void)
+{
+ int i;
+
+ /* N8VEM SBC Mark IV has RAM in the top 512K of physical memory.
+ * First 64K is used by the kernel.
+ * Each process gets the full 64K for now.
+ * Page size is 4KB. */
+ for(i = 0x10; i < 0x100; i+=0x10){
+ pagemap_add(i);
+ }
+}
+
+void platform_idle(void)
+{
+ /* Let's go to sleep while we wait for something to interrupt us;
+ * Makes the Mark IV's run LED go red, which amuses me greatly. */
+ __asm
+ halt
+ __endasm;
+}
+
+void platform_interrupt(void)
+{
+ switch(irqvector){
+ case Z180_INT0:
+ tty_pollirq_escc();
+ return;
+ case Z180_INT_TIMER0:
+ z180_timer_interrupt();
+ return;
+ case Z180_INT_ASCI0:
+ tty_pollirq_asci0();
+ return;
+ case Z180_INT_ASCI1:
+ tty_pollirq_asci1();
+ return;
+ default:
+ kprintf("[int:%d?]", irqvector);
+ return;
+ }
+}
+
+void map_init(void)
+{
+ /* clone udata and stack into a regular process bank, return with common memory
+ for the new process loaded */
+ copy_and_map_process(&init_process->p_page);
+ /* kernel bank udata (0x300 bytes) is never used again -- could be reused? */
+}
--- /dev/null
+; 2014-12-24 William R Sowerbutts
+; P112 hardware specific code
+
+ .module p112
+ .z180
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl outchar
+ .globl outstring
+ .globl outcharhex
+ .globl platform_interrupt_all
+ .globl _trap_monitor
+
+ .globl map_kernel
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+ ;.globl map_process_only
+
+ ; imported symbols
+ .globl z180_init_hardware
+ .globl z180_init_early
+ .globl _ramsize
+ .globl _procmem
+ .globl outhl
+ .globl outnewline
+
+ .include "kernel.def"
+ .include "../cpu-z180/z180.def"
+ .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; Initialisation code
+; -----------------------------------------------------------------------------
+ .area _DISCARD
+
+init_early:
+ ; P112: stop the floppy motor in case it is running
+ ld a, #0x0c
+ out0 (0x92), a
+
+ ; unmap ROM
+ xor a
+ out0 (Z182_ROMBR), a
+
+ in0 a, (Z182_SYSCONFIG)
+ or #0x08 ; disable ROM chip select (is this required as well as setting Z182_ROMBR?)
+ out0 (Z182_SYSCONFIG), a
+
+ jp z180_init_early
+
+init_hardware:
+ ; set system RAM size
+ ld hl, #RAM_KB
+ ld (_ramsize), hl
+ ld hl, #(RAM_KB-64) ; 64K for kernel
+ ld (_procmem), hl
+
+ ; enable ASCI interrupts
+ ; in0 a, (ASCI_STAT0)
+ ; or #0x08 ; enable ASCI0 receive interrupts
+ ; out0 (ASCI_STAT0), a
+ ; in0 a, (ASCI_ASEXT0)
+ ; and #0x7f ; disable RDRF interrupt inhibit
+ ; out0 (ASCI_ASEXT0), a
+ ; in0 a, (ASCI_STAT1)
+ ; or #0x08 ; enable ASCI1 receive interrupts
+ ; out0 (ASCI_STAT1), a
+ ; in0 a, (ASCI_ASEXT1)
+ ; and #0x7f ; disable RDRF interrupt inhibit
+ ; out0 (ASCI_ASEXT1), a
+
+ ; enable ESCC interrupts
+ ld bc, #0x0114 ; write register 1, 0x14: enable receive interrupts only
+ call write_escc
+ ld bc, #0x0908 ; write register 9, 0x08: master interrupt enable
+ call write_escc
+
+ jp z180_init_hardware
+
+write_escc:
+ out0 (ESCC_CTRL_A), b
+ out0 (ESCC_CTRL_A), c
+ out0 (ESCC_CTRL_B), b
+ out0 (ESCC_CTRL_B), c
+ ret
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0xF000 upwards)
+; -----------------------------------------------------------------------------
+ .area _COMMONMEM
+
+; outchar: Wait for UART TX idle, then print the char in A
+; destroys: AF
+outchar:
+ push bc
+ ld b, a
+ ; wait for transmitter to be idle
+ocloop: in0 a, (ESCC_CTRL_A)
+ and #0x04 ; test transmit buffer empty
+ jr z, ocloop
+ out0 (ESCC_DATA_A), b
+ pop bc
+ ret
+
+platform_interrupt_all:
+ ret
+
+map_kernel: ; map the kernel into the low 60K, leaves common memory unchanged
+ push af
+.if DEBUGBANK
+ ld a, #'K'
+ call outchar
+.endif
+ ld a, #(OS_BANK + FIRST_RAM_BANK)
+ out0 (MMU_BBR), a
+ pop af
+ ret
+
+; this "map_process" business makes no sense on mark4 since we'd switch stacks
+; and the RET would thus lose its return address. oh damn. I suppose we could
+; pop the stack address into hl, then jp (hl) or whatever. let's try and get by
+; without it.
+;
+; map_process: ; if HL=0 call map_kernel, else map the full 64K in bank pointed to by HL
+; ld a, h
+; or l
+; jr z, map_kernel
+; ld a, (hl)
+; out0 (MMU_BBR), a
+; out0 (MMU_CBR), a
+; ret
+
+; map_process_only: ; as map_process, but does not modify common memory
+; ld a, h
+; or l
+; jr z, map_kernel
+; ld a, (hl)
+; out0 (MMU_BBR), a
+; ret
+
+map_process_always: ; map the process into the low 60K based on current common mem (which is unchanged)
+ push af
+.if DEBUGBANK
+ ld a, #'='
+ call outchar
+.endif
+ ld a, (U_DATA__U_PAGE)
+ out0 (MMU_BBR), a
+.if DEBUGBANK
+ call outcharhex
+.endif
+ ; MMU_CBR is left unchanged
+ pop af
+ ret
+
+map_save: ; save the current process/kernel mapping
+ push af
+ in0 a, (MMU_BBR)
+ ld (map_store), a
+ pop af
+ ret
+
+map_restore: ; restore the saved process/kernel mapping
+ push af
+.if DEBUGBANK
+ ld a, #'-'
+ call outchar
+.endif
+ ld a, (map_store)
+ out0 (MMU_BBR), a
+.if DEBUGBANK
+ call outcharhex
+.endif
+ pop af
+ ret
+
+_trap_monitor:
+ di
+ call outnewline
+ pop hl
+ call outhl
+ call outnewline
+ pop hl
+ call outhl
+ call outnewline
+ pop hl
+ call outhl
+ call outnewline
+ pop hl
+ call outhl
+ call outnewline
+ pop hl
+ call outhl
+ call outnewline
+ halt
+ jr _trap_monitor
+
+map_store: ; storage for map_save/map_restore
+ .db 0
--- /dev/null
+-mwxuy
+-i uzi.ihx
+-b _CODE=0x0088
+-b _COMMONMEM=0xF000
+-b _DISCARD=0xE000
+-l z180
+platform-p112/crt0.rel
+platform-p112/commonmem.rel
+platform-p112/z180.rel
+platform-p112/p112.rel
+platform-p112/main.rel
+start.rel
+version.rel
+lowlevel-z180.rel
+usermem_std-z180.rel
+timer.rel
+kdata.rel
+usermem.rel
+platform-p112/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
+mm.rel
+bankfixed.rel
+swap.rel
+devsys.rel
+platform-p112/devtty.rel
+platform-p112/devide.rel
+-e
--- /dev/null
+.include "../cpu-z180/z180.s"