--- /dev/null
+CROSS_CCOPTS += -I../dev/
+
+
+CSRCS = devtty.c
+CSRCS += devices.c main.c
+
+DISCSRCS = discard.c
+
+ASRCS = z280rc.s crt0.s
+ASRCS += tricks.s commonmem.s
+
+DISCARD_DSRCS = ../dev/devide_discard.c
+DSRCS = ../dev/blkdev.c ../dev/devide.c ../dev/mbr.c
+
+NSRCS =
+
+COBJS = $(CSRCS:.c=.rel)
+AOBJS = $(ASRCS:.s=.rel)
+NOBJS = $(patsubst ../dev/net/%.c,%.rel, $(NSRCS))
+DISCOBJS = $(DISCSRCS:.c=.rel)
+DISCARD_DOBJS = $(patsubst ../dev/%.c,%.rel, $(DISCARD_DSRCS))
+DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS))
+DAOBJS = $(patsubst ../dev/%.s,%.rel, $(DASRCS))
+
+OBJS = $(COBJS) $(AOBJS) $(NOBJS) $(DISCOBJS) $(DOBJS) $(DISCARD_DOBJS) $(DAOBJS)
+
+JUNK = *.lst *.asm *.sym *.rst *.lst
+
+all: $(OBJS)
+
+$(COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DISCOBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+$(DOBJS): %.rel: ../dev/%.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DISCARD_DOBJS): %.rel: ../dev/%.c
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+$(NOBJS): %.rel: ../dev/net/%.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(AOBJS): %.rel: %.s
+ $(CROSS_AS) $(ASOPTS) $<
+
+$(DAOBJS): %.rel: ../dev/%.s
+ $(CROSS_AS) $(ASOPTS) $@ $<
+
+clean:
+ rm -f $(OBJS) $(JUNK) core *~
+
+image:
--- /dev/null
+Minimal Initial Support for Bill Shen's Z280RC
+
+This is a port in progress. It doesn't work yet.
+
+
+Z280RC is a Z280 based SBC with limited support for RC2014 peripherals. Only
+8bit I/O port access to the bus is supported and IM2 cannto be used. In
+practice this isn't much of a limitation as the Z280RC is self contained for
+all things that matter.
+
+Requirements
+
+Z280RC SBC
+
+Supported
+
+Z280 on board UART as console
+Z280 on board timers for clock tick and baud rate (NEITHER DONE YET)
+16bit IDE/CF adapter on the board
+
+Not Supported (initially)
+
+Z280 processor modes
+Virtual memory
+Proper paging
+Split I/D
+
+
+Memory Map
+
+We fake up a Z80 with banked memory and use the MMU to create 8 (for now)
+user banks with the top 8K common and the rest per bank, plus a kernel bank
+that is the full 64K identity mapped.
+
+User Mode
+
+0000-00FF Z80 vectors and RST
+0100-DDFF User code
+DE00-DFFF udata stash
+E000-EFFF Unused
+F000-FFFF Common space (lots of room left)
+
+Kernel Mode
+
+0000-00FF Z80 vectors and RST
+0100-???? Kernel
+????-EFFF Buffers
+F000-FFFF Common space (lots of room left)
+
+No external peripherals are currently supported.
--- /dev/null
+;
+; The common memory area traditionally starts with the udata and the
+; interrupt stacks. As this is standard in almost all cases you can
+; just include the standard implementation.
+;
+ .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
+
+/* This is a quick hack before we worry about the real MMU and all the rest
+ of it */
+
+/* Select a banked memory set up */
+#define CONFIG_BANK_FIXED
+/* This is the number of banks of user memory available (maximum) */
+#define MAX_MAPS 15 /* 512 KByte... minus the high one */
+/* How big is each bank - in our case 32K, 48K is actually more common. This
+ is hardware dependant */
+#define MAP_SIZE 0xE000
+/* How many banks do we have in our address space */
+#define CONFIG_BANKS 1
+
+/*
+ * Define the program loading area (needs to match kernel.def)
+ */
+#define PROGBASE 0x0000 /* Base of user */
+#define PROGLOAD 0x0100 /* Load and run here */
+#define PROGTOP 0xDE00 /* Top of program, base of U_DATA stash */
+#define PROC_SIZE 48 /* Memory needed per process including stash */
+/*
+ * Definitions for swapping.
+ */
+#define SWAPDEV (swap_dev) /* A variable for dynamic, or a device major/minor */
+extern unsigned int swap_dev;
+#define SWAP_SIZE 0x70 /* 56K in 512 byte blocks */
+#define SWAPBASE 0x0000 /* We swap the lot in one, include the */
+#define SWAPTOP 0xE000 /* vectors so its a round number of sectors */
+
+#define MAX_SWAPS 16 /* Maximum number of swapped out processes.
+ As we use the default 15 process max this
+ is definitely sufficient (14 would do) */
+/*
+ * When the kernel swaps something it needs to map the right page into
+ * memory using map_for_swap and then turn the user address into a
+ * physical address. For a simple banked setup there is no conversion
+ * needed so identity map it.
+ */
+#define swap_map(x) ((uint8_t *)(x))
+
+/* Set these two for networking - no point right now */
+//#define CONFIG_NET
+//#define CONFIG_NET_NATIVE
+
+
+/* What is the maximum number of /dev/hd devices we have. In theory right now
+ it's actually 3 - two in the IDE and one on the SD interface */
+#define MAX_BLKDEV 4
+/* Select IDE disk support, and PPIDE (parallel port IDE) as the interface */
+#define CONFIG_IDE
+#define CONFIG_PPIDE /* PPIDE is present */
+
+/* We will resize the buffers available after boot. This is the normal setting */
+#define CONFIG_DYNAMIC_BUFPOOL
+/* Swap will be set up when a suitably labelled partition is seen */
+#define CONFIG_DYNAMIC_SWAP
+/* Larger transfers (including process execution) should go directly not via
+ the buffer cache. For all small (eg bit) systems this is the right setting
+ as it avoids polluting the small cache with data when it needs to be full
+ of directory and inode information */
+#define CONFIG_LARGE_IO_DIRECT
+
+/* Specify this if there is a real time clock capable of reporting seconds. It
+ will be used to lock the kernel time better to reality. Other details like
+ Y2K support, or even supporting dates as well don't matter */
+#undef CONFIG_RTC
+/* Specify that there is a full real time clock that can supply the date and
+ time to the system. */
+#undef CONFIG_RTC_FULL
+/* Set this if the system has no proper real time clock (or has configurations
+ where it lacks one). This is not usually needed but for platforms it is also
+ see platform-sbcv2/main.c on what is needed */
+#undef CONFIG_NO_CLOCK
+/*
+ * How fast does the clock tick (if present), or how many times a second do
+ * we simulate if not. For a machine without video 10 is a good number. If
+ * you have video you probably want whatever vertical sync/blank interrupt
+ * rate the machine has. For many systems it's whatever the hardware gives
+ * you.
+ *
+ * Note that this needs to be divisible by 10 and at least 10. If your clock
+ * is a bit slower you may need to fudge things somewhat so that the kernel
+ * gets 10 timer interrupt calls per second.
+ */
+#define TICKSPERSEC 10 /* Ticks per second */
+
+/*
+ * The device (major/minor) for the console and boot up tty attached to
+ * init at start up. 512 is the major 2, so all the tty devices are
+ * 512 + n where n is the tty.
+ */
+#define BOOT_TTY (512 + 1) /* Set this to default device for stdio, stderr */
+ /* In this case, the default is the first TTY device */
+/*
+ * If you have a mechanism to pass in a root device configuration then
+ * this holds the address of the buffer (eg a CP/M command line or similar).
+ * If the configuration is fixed then this can be a string holding the
+ * configuration. NULL means 'prompt the user'.
+ */
+#define CMDLINE NULL /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 1 /* How many tty devices does the platform support */
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+#define NBUFS 5 /* Number of block buffers. Must be 4+ and must match
+ kernel.def */
+#define NMOUNTS 4 /* Number of mounts at a time */
+
+/* This can optionally be set to force a default baud rate, eg if the system
+ console should match a firmware set rate */
+#define TTY_INIT_BAUD B38400 /* To match ROMWBW */
--- /dev/null
+ ; Ordering of segments for the linker.
+ .area _CODE
+ .area _CODE2
+ .area _HOME
+ .area _CONST
+ .area _INITIALIZED
+ .area _DATA
+ .area _BSEG
+ .area _BSS
+ .area _HEAP
+ .area _GSINIT
+ .area _GSFINAL
+ .area _BUFFERS
+ .area _DISCARD
+ .area _VECTORS
+ .area _COMMONMEM
+ ; note that areas below here may be overwritten by the heap at runtime, so
+ ; put initialisation stuff in here
+ .area _INITIALIZER
+
+ ; imported symbols
+ .globl _fuzix_main
+ .globl init_early
+ .globl init_hardware
+ .globl s__DATA
+ .globl l__DATA
+ .globl s__DISCARD
+ .globl l__DISCARD
+ .globl s__BUFFERS
+ .globl l__BUFFERS
+ .globl s__COMMONMEM
+ .globl l__COMMONMEM
+ .globl s__INITIALIZER
+ .globl kstack_top
+ .globl map_kernel
+
+ ; startup code
+ .area _CODE
+
+ ; Load at 0x0100
+ ; We are executed as a CP/M task identity mapped into the
+ ; low 64K. Nice and simple. We just expand ourselves all
+ ; over CP/M and take over.
+start:
+ di
+ ld sp, #kstack_top
+ ; move the common memory where it belongs
+ ld hl, #s__DATA
+ ld de, #s__COMMONMEM
+ ld bc, #l__COMMONMEM
+ ldir
+ ; then the discard
+ ; Discard can just be linked in but is next to the buffers
+ 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
+; Zero buffers area
+ ld hl, #s__BUFFERS
+ ld de, #s__BUFFERS + 1
+ ld bc, #l__BUFFERS - 1
+ ld (hl), #0
+ ldir
+ call init_early
+ call init_hardware
+ call _fuzix_main
+ di
+stop: halt
+ jr stop
--- /dev/null
+#include <kernel.h>
+#include <tty.h>
+#include <version.h>
+#include <kdata.h>
+#include <devsys.h>
+#include <devtty.h>
+#include <blkdev.h>
+#include <devide.h>
+#include <printf.h>
+#include <devfd.h>
+
+/*
+ * This table is the glue that holds all the kernel device driver
+ * logic together. Each device driver provides methods for
+ * open, close, read, write and ioctl, although it can opt to use
+ * defaults as well.
+ *
+ * The validdev function is the same for all platforms but has to live
+ * in the same file as the table. Just paste it into each.
+ */
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+ /* 0: /dev/hd Hard disc block devices */
+ { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl },
+ /* 1: /dev/fd Floppy disc block devices */
+ { 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 */
+ { nxio_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) - 1)
+ return false;
+ else
+ return true;
+}
--- /dev/null
+/*
+ * This file implements the serial ports for the platform. Fuzix implements
+ * a reasonable subset of the System 5 termios. Certain things that are
+ * rarely relevant like XCASE, delay fills and parity are left to the
+ * driver if desired.
+ *
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <tty.h>
+#include <devtty.h>
+#include <z280.h>
+
+/* Remember these are in I/O FExxxx range */
+__sfr __at 0x12 uart_tsr;
+__sfr __at 0x14 uart_rsr;
+__sfr __at 0x16 uart_rdr;
+__sfr __at 0x18 uart_tdr;
+
+/*
+ * One buffer for each tty. For now just the console
+ */
+static char tbuf1[TTYSIZ];
+
+/*
+ * One entry per tty. The 0th entry is never used as tty minor 0 is
+ * special (/dev/tty) and it's cheaper to waste a few bytes that keep
+ * doing subtractions.
+ */
+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},
+};
+
+/* Write to system console. This is the backend to all the kernel messages,
+ kprintf(), panic() etc. */
+
+void kputchar(char c)
+{
+ if (c == '\n')
+ tty_putc(1, '\r');
+ tty_putc(1, c);
+}
+
+/*
+ * See if the given tty is able to transmit data without blocking. This
+ * may be done by checking the hardware, or if there is a software
+ * transmit queue by checking the queue is full.
+ *
+ * There are three possible returns
+ * TTY_READY_NOW means fire away
+ * TTY_READY_SOON means we will spin trying until pre-empted. As the
+ * 8bit processors are slow relative to baud rates it's often
+ * more efficient to do this
+ * TTY_READY_LATER means we will give up the CPU. This is best if the
+ * baud rate is low, the link is blocked by flow control signals
+ * or the CPU is fast.
+ *
+ * If TTY_READY_LATER is returned then the kernel will also call
+ * tty_sleeping(minor) before sleeping on the tty so that the driver
+ * can turn on or off tx complete interrupts.
+ *
+ * A video display that never blocks will just return TTY_READY_NOW
+ */
+uint8_t tty_writeready(uint8_t minor)
+{
+ uint8_t r, iob;
+ used(minor);
+ iob = io_bank_set(0xFE);
+ r = uart_tsr & 0x01 ? TTY_READY_NOW : TTY_READY_SOON;
+ io_bank_set(iob);
+ return r;
+}
+
+/*
+ * Write a character to a tty. This is the normal user space path for
+ * each outbound byte. It gets called in the normal tty flow, but may
+ * also be called from an interrupt to echo characters even if the
+ * tty is busy. This one reason to implement a small transmit queue.
+ *
+ * If the character echo doesn't fit just drop it. It should pretty much
+ * never occur and there is nothing else to do.
+ */
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ uint8_t iob;
+ used(minor);
+ iob = io_bank_set(0xFE);
+ uart_tdr = c;
+ io_bank_set(iob);
+}
+
+/*
+ * 16x50 conversion betwen a Bxxxx speed rate (see tty.h) and the values
+ * to stuff into the chip.
+ */
+static uint16_t clocks[] = {
+ 12, /* Not a real rate */
+ 2304,
+ 1536,
+ 1047,
+ 857,
+ 768,
+ 384,
+ 192,
+ 96,
+ 48,
+ 24,
+ 12,
+ 6,
+ 3,
+ 2,
+ 1
+};
+
+/*
+ * This function is called whenever the terminal interface is opened
+ * or the settings changed. It is responsible for making the requested
+ * changes to the port if possible. Strictly speaking it should write
+ * back anything that cannot be implemented to the state it selected.
+ *
+ * That needs tidying up in many platforms and we also need a proper way
+ * to say 'this port is fixed config' before making it so.
+ */
+void tty_setup(uint8_t minor)
+{
+ used(minor);
+}
+
+/*
+ * This function is called when the kernel is about to sleep on a tty.
+ * We don't care about this.
+ */
+void tty_sleeping(uint8_t minor)
+{
+ used(minor);
+}
+
+/*
+ * Return 1 if the carrier on the terminal is raised. If the port has
+ * no carrier signal always return 1. It is used to block a port on open
+ * until carrier.
+ */
+int tty_carrier(uint8_t minor)
+{
+ used(minor);
+ return 1;
+}
+
+/*
+ * When the input queue is part drained this method is called from the
+ * kernel so that hardware flow control signals can be updated.
+ */
+void tty_data_consumed(uint8_t minor)
+{
+ used(minor);
+}
+
+/*
+ * Our platform specific code so we have a function to call to poll the
+ * serial ports for activity.
+ */
+void tty_poll(void)
+{
+ uint8_t iob = io_bank_set(0xFE);
+ /* Should be IRQ driven but we might not be so poll anyway if
+ pending. IRQs are off here so this is safe */
+ if (uart_rsr & 0x10)
+ tty_inproc(1, uart_rdr);
+ io_bank_set(iob);
+}
--- /dev/null
+#ifndef _DEVTTY_H
+#define _DEVTTY_H
+
+extern void tty_poll(void);
+extern uint8_t timermsr;
+
+
+#endif
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <blkdev.h>
+#include <devide.h>
+#include <propio2.h>
+
+extern int strcmp(const char *, const char *);
+
+/*
+ * Everything in this file ends up in discard which means the moment
+ * we try and execute init it gets blown away. That includes any
+ * variables declared here so beware!
+ */
+
+/*
+ * We get passed each kernel command line argument. if we return 1 then
+ * we claim it, if not it gets passed to init. It's perfectly acceptable
+ * to act on a match and return to also pass it to init if you need to.
+ */
+uint8_t platform_param(unsigned char *p)
+{
+ used(p);
+ return 0;
+}
+
+/*
+ * Set up our memory mappings. This is not needed for simple banked memory
+ * only more complex setups such as 16K paging.
+ */
+void map_init(void)
+{
+}
+
+/*
+ * Add all the available pages to the list of pages we an use. If this
+ * is runtime dynamic check to make sure you don't add more than MAX_MAPS
+ * of them. On some machines with a lot of RAM the implementation turns
+ * the excess into a RAM disc
+ *
+ * The mapping can be logical numbers 1-n, or can be physical values to
+ * write into registers. Whatever works best. The 0 value is however
+ * reserved throughout to indicate kernel mode in calls, and also
+ * to mean swapped out for processes. If your bank 0 is user space you
+ * might need to just dec/inc before using it in an I/O port or similar
+ * to avoid confusion.
+ *
+ * Kernel in bank 0, user in banks 1-8. That's enough to get us going
+ * until we do real Z280 management
+ */
+void pagemap_init(void)
+{
+ uint8_t i;
+ for (i = 1; i <= 8; i++)
+ pagemap_add(i);
+}
+
+/*
+ * This function is called for partitioned devices if a partition is found
+ * and marked as swap type. The first one found will be used as swap. We
+ * only support one swap device.
+ */
+void platform_swap_found(uint8_t letter, uint8_t m)
+{
+ blkdev_t *blk = blk_op.blkdev;
+ uint16_t n;
+ if (swap_dev != 0xFFFF)
+ return;
+ letter -= 'a';
+ kputs("(swap) ");
+ swap_dev = letter << 4 | m;
+ n = blk->lba_count[m - 1] / SWAP_SIZE;
+ if (n > MAX_SWAPS)
+ n = MAX_SWAPS;
+ while(n)
+ swapmap_add(n--);
+}
+
+/*
+ * Called after interrupts are enabled in order to enumerate and set up
+ * any devices. In our case we simply need to probe the IDE and SD card.
+ */
+void device_init(void)
+{
+ devide_init();
+}
--- /dev/null
+-mwxuy
+-i fuzix.ihx
+-b _CODE=0x0100
+-b _VECTORS=0xF000
+-b _INITIALIZER=0xFD00
+-l z80
+platform-z280rc/crt0.rel
+platform-z280rc/commonmem.rel
+platform-z280rc/z280rc.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+usermem.rel
+usermem_std-z80.rel
+platform-z280rc/tricks.rel
+platform-z280rc/main.rel
+platform-z280rc/discard.rel
+timer.rel
+kdata.rel
+platform-z280rc/devices.rel
+devio.rel
+filesys.rel
+process.rel
+inode.rel
+syscall_exec16.rel
+syscall_fs.rel
+syscall_fs2.rel
+syscall_fs3.rel
+syscall_proc.rel
+syscall_other.rel
+tty.rel
+mm.rel
+swap.rel
+bankfixed.rel
+devsys.rel
+platform-z280rc/devtty.rel
+platform-z280rc/blkdev.rel
+platform-z280rc/mbr.rel
+platform-z280rc/devide.rel
+platform-z280rc/devide_discard.rel
+-e
--- /dev/null
+; FUZIX mnemonics for memory addresses etc
+;
+;
+; The U_DATA address. If we are doing a normal build this is the start
+; of common memory. We do actually have a symbol for udata so
+; eventually this needs to go away
+;
+U_DATA .equ 0xE000 ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE .equ 0x200 ; 256+256 bytes)
+;
+; Space for the udata of a switched out process within the bank of
+; memory that it uses. Normally placed at the very top
+;
+U_DATA_STASH .equ 0xDE00 ; DE00-DFFF
+;
+; Z80 systems start program space at 0, and load at 0x100 so that the
+; low 256 bytes are free for syscall vectors and the like, with some
+; also used as a special case by the CP/M emulator.
+;
+PROGBASE .equ 0x0000
+PROGLOAD .equ 0x0100
+;
+; CPU type
+; 0 = CMOS Z80 (set this for Z280 for now)
+; 1 = NMOS Z80 (also works with CMOS)
+; 2 = Z180
+;
+; If either NMOS or CMOS may be present pick NMOS as the NMOS build
+; contains extra code to work around an erratum n the NUMS Z80
+;
+Z80_TYPE .equ 0
+;
+; For special platforms that have external memory protectuon hardware
+; Just say 0.
+;
+Z80_MMU_HOOKS .equ 0
+;
+; Set this if the platform has swap enabled in config.h
+;
+CONFIG_SWAP .equ 1
+;
+; The number of disk buffers. Must match config.h
+;
+NBUFS .equ 5
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <blkdev.h>
+#include <devide.h>
+#include <z280.h>
+
+uint16_t ramtop = PROGTOP;
+uint16_t swap_dev = 0xFFFF;
+
+/*
+ * This routine is called continually when the machine has nothing else
+ * it needs to execute. On a machine with entirely interrupt driven
+ * hardware this could just halt for interrupt.
+ */
+void platform_idle(void)
+{
+ /* Disable interrupts so we don't accidentally process a polled tty
+ and interrupt call at once and make a mess */
+ irqflags_t irq = di();
+ tty_poll();
+ /* Restore prior state. */
+ irqrestore(irq);
+}
+
+/*
+ * This routine is called from the interrupt handler code to process
+ * interrupts. All of the nasty stuff (register saving, bank switching,
+ * reti instructions) is dealt with for you.
+ */
+void platform_interrupt(void)
+{
+ tty_poll();
+ timer_interrupt();
+}
+
+/* This points to the last buffer in the disk buffers. There must be at least
+ four buffers to avoid deadlocks. */
+struct blkbuf *bufpool_end = bufpool + NBUFS;
+
+/*
+ * We pack discard into the memory image is if it were just normal
+ * code but place it at the end after the buffers. When we finish up
+ * booting we turn everything from the buffer pool to common into
+ * buffers. This blows away the _DISCARD segment.
+ */
+void platform_discard(void)
+{
+ /* We start our common block with the vectors as they must be 4K
+ aligned */
+ uint16_t discard_size = (uint16_t)vectors - (uint16_t)bufpool_end;
+ bufptr bp = bufpool_end;
+
+ discard_size /= sizeof(struct blkbuf);
+
+ kprintf("%d buffers added\n", discard_size);
+
+ bufpool_end += discard_size;
+
+ memset( bp, 0, discard_size * sizeof(struct blkbuf) );
+
+ for( bp = bufpool + NBUFS; bp < bufpool_end; ++bp ){
+ bp->bf_dev = NO_DEVICE;
+ bp->bf_busy = BF_FREE;
+ }
+}
+
+/****************************************************************************/
+/* The innermost part of the transfer routines has to live in common memory */
+/* since it must be able to bank switch to the user memory bank. */
+/****************************************************************************/
+
+/* IDE Port I/O: Z280 edition. We know the bank is the usual default 0 here */
+
+COMMON_MEMORY
+
+void devide_read_data(void) __naked
+{
+ __asm
+ ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user
+ ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr
+ ld bc, #IDE_REG_DATA ; setup port number
+ ; and count
+#ifdef SWAPDEV
+ cp #2
+ jr nz, not_swapin
+ ld a, (_blk_op+BLKPARAM_SWAP_PAGE) ; blkparam.swap_page
+ call map_for_swap
+ jr swapin
+not_swapin:
+#endif
+ or a ; test is_user
+ call nz, map_process_always ; map user memory first if required
+swapin:
+;; inirw ; transfer 256 words
+ .db 0xED, 0x92
+ or a ; test is_user
+ ret z ; done if kernel memory transfer
+ jp map_kernel ; else map kernel then return
+ __endasm;
+}
+
+void devide_write_data(void) __naked
+{
+ __asm
+ ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user
+ ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr
+ ld bc, #IDE_REG_DATA ; setup port number
+ ; and count
+#ifdef SWAPDEV
+ cp #2
+ jr nz, not_swapout
+ ld a, (_blk_op+BLKPARAM_SWAP_PAGE) ; blkparam.swap_page
+ call map_for_swap
+ jr swapout
+not_swapout:
+#endif
+ or a ; test is_user
+ call nz, map_process_always ; else map user memory first if required
+swapout:
+;; otirw ; transfer 256 words
+ .db 0xED, 0x93
+ or a ; test is_user
+ ret z ; done if kernel memory transfer
+ jp map_kernel ; else map kernel then return
+ __endasm;
+}
--- /dev/null
+#define ide_select(x)
+#define ide_deselect()
+
+/*16bit, no altstatus/control */
+#define IDE_REG_DATA 0xC0
+#define IDE_REG_ERROR 0xC2
+#define IDE_REG_FEATURES 0xC2
+#define IDE_REG_SEC_COUNT 0xC5
+#define IDE_REG_LBA_0 0xC7
+#define IDE_REG_LBA_1 0xC9
+#define IDE_REG_LBA_2 0xCB
+#define IDE_REG_LBA_3 0xCD
+#define IDE_REG_DEVHEAD 0xCD
+#define IDE_REG_STATUS 0xCF
+#define IDE_REG_COMMAND 0xCF
+
+/* The data register should be accessed using inirw/otirw and similar */
+#define IDE_NONSTANDARD_XFER
--- /dev/null
+#
+# Tell the build system what processor type we are using
+# For now pretend to be a Z80
+#
+export CPU = z80
+export USERCPU = z80
--- /dev/null
+;
+; For simple banked systems there is a standard implementation. The
+; only reason to do otherwise is for speed. A custom bank logic aware
+; bank to bank copier will give vastly better fork() performance.
+;
+; As this is meant to be a simple reference port we use the standard
+; approach. The morbidly curious can read the TRS80 model 1 bank to
+; bank copier.
+;
+ .include "../kernel.def"
+ .include "kernel.def"
+
+ .include "../lib/z80fixedbank.s"
--- /dev/null
+extern uint8_t io_bank_set(uint8_t bank) __z88dk_fastcall;
+extern uint8_t vectors[];
--- /dev/null
+;
+; Z280 RC support.
+;
+; Minimal for now to get us up and running
+;
+
+ .module sbcv2
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl interrupt_handler
+ .globl _program_vectors
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_a
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+ .globl map_for_swap
+ .globl platform_interrupt_all
+ .globl _kernel_flag
+
+ ; exported debugging tools
+ .globl _platform_monitor
+ .globl _platform_reboot
+ .globl outchar
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+ .globl istack_top
+ .globl istack_switched_sp
+ .globl unix_syscall_entry
+ .globl trap_illegal
+ .globl outcharhex
+ .globl outstring
+ .globl outhl
+ .globl null_handler
+ .globl nmi_handler
+ .globl _inint
+ .globl kstack_top
+
+ .globl s__COMMONMEM
+ .globl l__COMMONMEM
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+;
+; Buffers (we use asm to set this up as we need them in a special segment
+; so we can recover the discard memory into the buffer pool
+;
+
+ .globl _bufpool
+ .area _BUFFERS
+
+_bufpool:
+ .ds BUFSIZE * NBUFS
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (kept even when we task switch)
+; -----------------------------------------------------------------------------
+ .area _COMMONMEM
+
+;
+; This method is invoked early in interrupt handling before any
+; complex handling is done. It's useful on a few platforms but
+; generally a ret is all that is needed
+;
+platform_interrupt_all:
+ ret
+
+;
+; If you have a ROM monitor you can get back to then do so, if not
+; fall into reboot.
+;
+_platform_monitor:
+;
+; Reboot the system if possible, halt if not. On a system where the
+; ROM promptly wipes the display you may want to delay or wait for
+; a keypress here (just remember you may be interrupts off, no kernel
+; mapped so hit the hardware).
+;
+_platform_reboot:
+ jr _platform_reboot
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (may be below 0x8000, only accessible when the kernel is
+; mapped)
+; -----------------------------------------------------------------------------
+ .area _CODE
+
+;
+; This routine is called very early, before the boot code shuffles
+; things into place. We assume the bootstrap already set up
+;
+; - bus timing and initialization
+; - bus timing and control
+; - cache
+;
+init_early:
+ ld c,#0x16
+ ld hl,#0x0000
+; ldctl (c),hl ; set interrupt status
+ .byte 0xED, 0x6E
+ ld c,#0x06
+ ld hl,#_vectors ; Vectors on 4K boundary
+ ld l,h ; Work around crappy linker
+ ld h,#0
+; ldctl (c),hl ; into int trap/vector ptr
+ .byte 0xED, 0x6E
+ ld c,#0x08
+ ld l,#0
+; ldctl (c),hl ; clear I/O page
+ .byte 0xED, 0x6E
+ ld c,#0x10
+ ld l,#0x04 ; no user I/O no EPU
+; ldctl (c),hl ; set trap control (can't set
+ ; stack trap until have user mode
+ ; done)
+ .byte 0xED, 0x6E
+ ret
+
+; -----------------------------------------------------------------------------
+; DISCARD is memory that will be recycled when we exec init
+; -----------------------------------------------------------------------------
+ .area _DISCARD
+;
+; After the kernel has shuffled things into place this code is run.
+; It's the best place to breakpoint or trace if you are not sure your
+; kernel is loading and putting itself into place properly.
+;
+; It's required jobs are to set up the vectors, ramsize (total RAM),
+; and procmem (total memory free to processs), as well as setting the
+; interrupt mode but *not* enabling interrupts. Many platforms also
+; program up support hardware like PIO and CTC devices here.
+;
+init_hardware:
+ call map_kernel
+ ld l,#0xFF
+ call _io_bank_set
+ push hl
+ ld hl,#0xBBE0 ; MMU on S and U, no split I/D
+ ; We may want to consider MMU off in
+ ; supervisor later on - is there a perf
+ ; gain versus MMU on ??
+ ld c,#0xF0
+; outw (c),hl
+ .byte 0xED,0xBF
+ pop hl
+ call _io_bank_set
+
+ ld hl,#2048
+ ld (_ramsize), hl
+ ld hl,#1984
+ 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
+
+ ; Our CTC is fed from the system clock / 4 which gives us
+ ; 7.372800Mhz input. We want a semsible multiple of 10Hz and
+ ; for a fast CPU 50 or 100 isn't a bad value. On a single CTC
+ ; counter our best shot is 120Mhz (61440 divider exactly)
+
+ ld l,#0xFE
+ call _io_bank_set
+ push hl
+ ld a,#0xA0 ; Timer, interrupting
+ out (0xE0),a
+ ld hl,#61440 ; 120Hz
+ ld c,#0xE2
+; outw (c),hl
+ .byte 0xED,0xBF
+ ld a,#0xC0 ; Enable, gate
+ out (0xE1),a
+ pop hl
+ call _io_bank_set
+
+
+; im 3 ; set CPU interrupt mode
+ .byte 0xED, 0x4E
+
+ ret
+
+;
+; Bank switching unsurprisingly must be in common memory space so it's
+; always available. This is a simple hack to get us going. The Z280
+; has a real MMU and virtual memory. We just treat it like a bunch of
+; banks.
+;
+ .area _COMMONMEM
+
+mapreg: .db 0 ; Our map register is write only so keep a copy
+mapsave: .db 0 ; Saved copy of the previous map (see map_save)
+
+_kernel_flag:
+ .db 1 ; We start in kernel mode
+
+;
+; This is invoked with a NULL argument at boot to set the kernel
+; vectors and then elsewhere in the kernel when the kernel knows
+; a bank may need vectors writing to it.
+;
+; Will need rewriting when we do things properly
+;
+_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
+
+ ; set restart vector for FUZIX system calls
+ ; once we do real Z280 mode we'll need this to syscall
+ ld (0x0030), a ; (rst 30h is unix function call vector)
+ ld hl, #unix_syscall_entry
+ ld (0x0031), hl
+
+ ld (0x0000), a
+ ld hl, #null_handler ; to our trap handler
+ ld (0x0001), hl
+ ; Fall into map_kernel
+
+;
+; Mapping set up for the Z280. This is hack to get us going
+;
+; We map the low 64K 1:1 in kernel mode, keep the top 8K fixed and
+; map the others by 'bank' for user mode. We don't actually use the
+; hardware user mode yet. It's all just a bodge to get us up and
+; running
+;
+map_kernel:
+ push af
+ xor a
+ call map_process_a ; do all the logic in one place with
+ pop af ; kernel as entry 0 in the table
+ ret
+ ; map_process is called with HL either NULL or pointing to the
+ ; page mapping. Unlike the other calls it's allowed to trash AF
+map_process:
+ ld a, h
+ or l
+ jr z, map_kernel
+map_process_hl:
+ ld a, (hl) ; and fall through
+ ;
+ ; With a simple bank switching system you need to provide a
+ ; method to switch to the bank in A without corrupting any
+ ; other registers. The stack is safe in common memory.
+ ; For swap you need to provide what for simple banking is an
+ ; identical routine.
+map_for_swap:
+map_process_a: ; used by bankfork
+ push bc
+ push de
+ push hl
+ ld (mapreg), a ; bank
+ ld c,#0x08
+;; ldctl hl,(c)
+ .byte 0xED, 0x66
+ push hl
+ ld l,#0xff ; MMU is I/O bank FF
+;; ldctl (c),hl
+ .byte 0xED, 0x6E
+ ld l,a
+ ld h,#0
+ add hl,hl ; 16 words per bank
+ add hl,hl
+ add hl,hl
+ add hl,hl
+ add hl,hl
+ ld de,#frames ; Lazy - look it up it's only a hack for now
+ ld a,#0x10
+ out (0x44),a ; pointer to 0x10 (system pages)
+ ld bc,#0x0FF4
+;; otirw
+ .db 0xED, 0x93
+ pop hl
+ ld c,#0x08
+;; ldctl (c),hl ; previous bank register
+ .byte 0xED, 0x6E
+ pop hl
+ pop de
+ pop bc
+ ret
+
+ ;
+ ; Map the current process into memory. We do this by extracting
+ ; the bank value from u_page.
+ ;
+map_process_always:
+ push af
+ push hl
+ ld hl, #U_DATA__U_PAGE
+ call map_process_hl
+ pop hl
+ pop af
+ ret
+
+ ;
+ ; Save the existing mapping. The place you save it to needs to
+ ; be in common memory as you have no idea what bank is live
+ ;
+map_save: push af
+ ld a, (mapreg)
+ ld (mapsave), a
+ pop af
+ ret
+ ;
+ ; Restore the saved bank. Note that you don't need to deal with
+ ; stacking of banks (we never recursively use save/restore), and
+ ; that we may well call save and decide not to call restore.
+ ;
+map_restore:
+ push af
+ ld a, (mapsave)
+ call map_process_a
+ pop af
+ ret
+ ;
+ ; Used for low level debug. Output the character in A without
+ ; corrupting other registers. May block. Interrupts and memory
+ ; state are undefined
+ ;
+outchar:
+ push af
+ push bc
+ push de
+ push hl
+ ld l,#0xFE
+ call _io_bank_set
+twait: in a,(0x12)
+ bit 0,a
+ jr z, twait
+ pop af
+ out (0x18),a
+ call _io_bank_set
+ pop hl
+ pop de
+ pop bc
+ pop af
+ ret
+
+;
+; These belong in core Z280 code eventually
+;
+ .globl _io_bank_set
+ .globl _flush_cpu_cache
+
+
+_io_bank_set:
+ ld c,#8
+ ex de,hl ; save the new value in DE
+;; ldctl hl,(c) ; read old into HL
+ .byte 0xED, 0x66
+ ex de,hl ; switch bank
+;; ldctl (c),hl ; new into HL
+ .byte 0xED, 0x6E
+ ex de,hl ; and back again to return old
+ ret
+
+_flush_cpu_cache:
+ .byte 0xED, 0x65
+ ret
+
+
+;
+; Mapping tables for speed
+;
+frames:
+ .word 0x0000 ; Identity map kernel
+ .word 0x0010
+ .word 0x0020
+ .word 0x0030
+ .word 0x0040
+ .word 0x0050
+ .word 0x0060
+ .word 0x0070
+ .word 0x0080
+ .word 0x0090
+ .word 0x00A0
+ .word 0x00B0
+ .word 0x00C0
+ .word 0x00D0
+ .word 0x00E0 ; Common
+ .word 0x00F0 ;
+
+ .word 0x0100
+ .word 0x0110
+ .word 0x0120
+ .word 0x0130
+ .word 0x0140
+ .word 0x0150
+ .word 0x0160
+ .word 0x0170
+ .word 0x0180
+ .word 0x0190
+ .word 0x01A0
+ .word 0x01B0
+ .word 0x01C0
+ .word 0x01D0
+ .word 0x00E0 ; Common
+ .word 0x00F0 ;
+
+ .word 0x0200
+ .word 0x0210
+ .word 0x0220
+ .word 0x0230
+ .word 0x0240
+ .word 0x0250
+ .word 0x0260
+ .word 0x0270
+ .word 0x0280
+ .word 0x0290
+ .word 0x02A0
+ .word 0x02B0
+ .word 0x02C0
+ .word 0x02D0
+ .word 0x00E0 ; Common
+ .word 0x00F0 ;
+
+ .word 0x0300
+ .word 0x0310
+ .word 0x0320
+ .word 0x0330
+ .word 0x0340
+ .word 0x0350
+ .word 0x0360
+ .word 0x0370
+ .word 0x0380
+ .word 0x0390
+ .word 0x03A0
+ .word 0x03B0
+ .word 0x03C0
+ .word 0x03D0
+ .word 0x00E0 ; Common
+ .word 0x00F0 ;
+
+ .word 0x0400
+ .word 0x0410
+ .word 0x0420
+ .word 0x0430
+ .word 0x0440
+ .word 0x0450
+ .word 0x0460
+ .word 0x0470
+ .word 0x0480
+ .word 0x0490
+ .word 0x04A0
+ .word 0x04B0
+ .word 0x04C0
+ .word 0x04D0
+ .word 0x00E0 ; Common
+ .word 0x00F0 ;
+
+ .word 0x0500
+ .word 0x0510
+ .word 0x0520
+ .word 0x0530
+ .word 0x0540
+ .word 0x0550
+ .word 0x0560
+ .word 0x0570
+ .word 0x0580
+ .word 0x0590
+ .word 0x05A0
+ .word 0x05B0
+ .word 0x05C0
+ .word 0x05D0
+ .word 0x00E0 ; Common
+ .word 0x00F0 ;
+
+ .word 0x0600
+ .word 0x0610
+ .word 0x0620
+ .word 0x0630
+ .word 0x0640
+ .word 0x0650
+ .word 0x0660
+ .word 0x0670
+ .word 0x0680
+ .word 0x0690
+ .word 0x06A0
+ .word 0x06B0
+ .word 0x06C0
+ .word 0x06D0
+ .word 0x00E0 ; Common
+ .word 0x00F0 ;
+
+ .word 0x0700
+ .word 0x0710
+ .word 0x0720
+ .word 0x0730
+ .word 0x0740
+ .word 0x0750
+ .word 0x0760
+ .word 0x0770
+ .word 0x0780
+ .word 0x0790
+ .word 0x07A0
+ .word 0x07B0
+ .word 0x07C0
+ .word 0x07D0
+ .word 0x00E0 ; Common
+ .word 0x00F0 ;
+
+ .word 0x0700
+ .word 0x0710
+ .word 0x0720
+ .word 0x0730
+ .word 0x0740
+ .word 0x0750
+ .word 0x0760
+ .word 0x0770
+ .word 0x0780
+ .word 0x0790
+ .word 0x07A0
+ .word 0x07B0
+ .word 0x07C0
+ .word 0x07D0
+ .word 0x00E0 ; Common
+ .word 0x00F0 ;
+
+
+;
+; Interrupt vectors. Kernel and identity mapped
+;
+ .area _VECTORS
+
+SUPER .equ 0x0000 ; Supervisor, all ints masked
+
+ .globl _vectors
+
+_vectors:
+ .word #SUPER ; 00 Reserved
+ .word invalid
+ .word #SUPER ; 04 NMI
+ .word nmi_handler
+ .word #SUPER ; 08 External Line A
+ .word external
+ .word #SUPER ; 0C External Line B
+ .word external
+ .word #SUPER ; 10 External Line C
+ .word external
+ .word #SUPER ; 14 CTC 0
+ .word ctc0
+ .word #SUPER ; 18 CTC 1
+ .word ctc1
+ .word #SUPER ; 1C Reserved
+ .word invalid ; The broken undocumented CTC
+ .word #SUPER ; 20 CTC 2
+ .word invalid
+ .word #SUPER ; 24 DMA 0
+ .word invalid
+ .word #SUPER ; 28 DMA 1
+ .word invalid
+ .word #SUPER ; 2C DMA 2
+ .word invalid
+ .word #SUPER ; 30 DMA 3
+ .word invalid
+ .word #SUPER ; 34 UART rx
+ .word ttyint
+ .word #SUPER ; 38 UART tx
+ .word invalid
+ .word #SUPER ; 3C Single-step
+ .word sstep
+ .word #SUPER ; 40 Breakpoint
+ .word sigtrap
+ .word #SUPER ; 44 Divison by zero
+ .word sigfpe
+ .word #SUPER ; 48 Stack overflow
+ .word stackover
+ .word #SUPER ; 4C Access violation
+ .word sigsegv
+ .word #SUPER ; 50 System call
+ .word invalid
+ .word #SUPER ; 54 Privileged instruction
+ .word sigill
+ .word #SUPER ; 58 EPU
+ .word sigill
+ .word #SUPER ; 5C EPU
+ .word sigill
+ .word #SUPER ; 60 EPU
+ .word sigill
+ .word #SUPER ; 64 EPU
+ .word invalid
+ .word #SUPER ; 68 Reserved
+ .word invalid
+ .word #SUPER ; 6C Reserved
+ .word invalid
+
+ ; What can follow then is 384 word size vectors for INT A B C
+ ; for IM2 style external I/O which we don't use
+
+ .area _COMMONMEM
+
+SIGILL .equ 4
+SIGTRAP .equ 5
+SIGFPE .equ 8
+SIGSEGV .equ 11
+
+ .globl _irq_source
+
+_irq_source:
+ .byte 0
+
+invalid:
+ ld hl,#invalidint
+ call outstring
+ jr trapexit
+
+external:
+ ; Interrupt off the RC2014 bus
+ push af
+ xor a
+irqcall:
+ ld (_irq_source),a
+ ; This bit belongs in a future Z280 lowlevel not here
+ push de
+ push hl
+ xor a
+ call _io_bank_set
+ push hl
+ call interrupt_handler
+ pop hl
+ call _io_bank_set
+ pop hl
+ pop de
+ pop af
+ jr trapexit
+ctc0:
+ push af
+ ld a,#1
+ jr irqcall
+ctc1:
+ push af
+ ld a,#2
+ jr irqcall
+
+ttyint:
+ push af
+ ld a,#3
+ jr irqcall
+
+sstep:
+sigtrap:
+ push af
+ ld a,#SIGTRAP
+ jr sig_or_die
+sigfpe:
+ push af
+ ld a,#SIGFPE
+ jr sig_or_die
+sigsegv:
+ push af
+ ld a,#SIGSEGV
+ jr sig_or_die
+sigill:
+ push af
+ ld a,#SIGILL
+sig_or_die:
+ ; Once we have proper supervisor/user we can just check the pushed
+ ; status to see what to do. For now fudge it roughly.
+ push af
+ ld a,(_inint)
+ or a
+ jr nz, diediedie
+ ld a,(U_DATA__U_INSYS)
+ or a
+ jr nz, diediedie
+ ; TODO - we need to go via the syscall signal path as we need this
+ ; to be synchronous. Will need to be in the lowlevel-z280 code when
+ ; we get there
+ jr trapexit
+
+stackover:
+ ld sp,#kstack_top
+ ld hl,#stackfault
+ call outstring
+ jp _platform_monitor
+
+trapexit:
+ ; Discard the data
+ inc sp
+ inc sp
+ ; retil
+ .byte 0xed, 0x55
+
+invalidint:
+ .asciz 'INVIRQ'
+stackfault:
+ .asciz 'STKFLT'
+
+diediedie:
+ call outcharhex
+ pop af ; discard
+ pop hl ; cause
+ call outhlcolon
+ pop hl ; status
+ call outhlcolon
+ pop hl ; pc
+ call outhlcolon
+ jp _platform_monitor
+
+outhlcolon:
+ call outcharhex
+ ld a,#':'
+ jp outhl