From 28fcd3ec900c7e0ff8cfb5294c012b31e2a332ce Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 4 Feb 2019 18:37:49 +0000 Subject: [PATCH] yaz180: initial bits --- Kernel/platform-yaz180/Makefile | 48 ++++++ Kernel/platform-yaz180/README | 27 +++ Kernel/platform-yaz180/commonmem.s | 3 + Kernel/platform-yaz180/config.h | 51 ++++++ Kernel/platform-yaz180/crt0.s | 74 ++++++++ Kernel/platform-yaz180/devices.c | 34 ++++ Kernel/platform-yaz180/devsdspi.c | 232 ++++++++++++++++++++++++++ Kernel/platform-yaz180/devtty.c | 91 ++++++++++ Kernel/platform-yaz180/devtty.h | 9 + Kernel/platform-yaz180/discard.c | 43 +++++ Kernel/platform-yaz180/diskboot.s | 197 ++++++++++++++++++++++ Kernel/platform-yaz180/fuzix.lnk | 43 +++++ Kernel/platform-yaz180/kernel.def | 21 +++ Kernel/platform-yaz180/main.c | 59 +++++++ Kernel/platform-yaz180/monitor.s | 41 +++++ Kernel/platform-yaz180/platform_ide.h | 38 +++++ Kernel/platform-yaz180/ppide.c | 128 ++++++++++++++ Kernel/platform-yaz180/target.mk | 2 + Kernel/platform-yaz180/yaz180.s | 87 ++++++++++ Kernel/platform-yaz180/z180.s | 1 + 20 files changed, 1229 insertions(+) create mode 100644 Kernel/platform-yaz180/Makefile create mode 100644 Kernel/platform-yaz180/README create mode 100644 Kernel/platform-yaz180/commonmem.s create mode 100644 Kernel/platform-yaz180/config.h create mode 100644 Kernel/platform-yaz180/crt0.s create mode 100644 Kernel/platform-yaz180/devices.c create mode 100644 Kernel/platform-yaz180/devsdspi.c create mode 100644 Kernel/platform-yaz180/devtty.c create mode 100644 Kernel/platform-yaz180/devtty.h create mode 100644 Kernel/platform-yaz180/discard.c create mode 100644 Kernel/platform-yaz180/diskboot.s create mode 100644 Kernel/platform-yaz180/fuzix.lnk create mode 100644 Kernel/platform-yaz180/kernel.def create mode 100644 Kernel/platform-yaz180/main.c create mode 100644 Kernel/platform-yaz180/monitor.s create mode 100644 Kernel/platform-yaz180/platform_ide.h create mode 100644 Kernel/platform-yaz180/ppide.c create mode 100644 Kernel/platform-yaz180/target.mk create mode 100644 Kernel/platform-yaz180/yaz180.s create mode 100644 Kernel/platform-yaz180/z180.s diff --git a/Kernel/platform-yaz180/Makefile b/Kernel/platform-yaz180/Makefile new file mode 100644 index 00000000..75ad1bc6 --- /dev/null +++ b/Kernel/platform-yaz180/Makefile @@ -0,0 +1,48 @@ +ASRCS = crt0.s z180.s commonmem.s yaz180.s monitor.s +CSRCS += devices.c main.c devtty.c ppide.c +DISCARD_CSRCS = discard.c +DISCARD_DSRCS = ../dev/devide_discard.c +DSRCS = ../dev/devide.c ../dev/mbr.c ../dev/blkdev.c +DASRCS = ../dev/devrd_z180_hw.s + +AOBJS = $(ASRCS:.s=.rel) +COBJS = $(CSRCS:.c=.rel) +DISCARD_COBJS = $(DISCARD_CSRCS:.c=.rel) +DISCARD_DOBJS = $(patsubst ../dev/%.c,%.rel, $(DISCARD_DSRCS)) +DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS)) +DAOBJS = $(patsubst ../dev/%.s,%.rel, $(DASRCS)) + +OBJS = $(AOBJS) $(COBJS) $(DOBJS) $(DISCARD_DOBJS) $(DISCARD_COBJS) $(DAOBJS) + +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 $< + +$(DAOBJS): %.rel: ../dev/%.s + $(CROSS_AS) $(ASOPTS) $@ $< + +$(DISCARD_COBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(DISCARD_DOBJS): %.rel: ../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +clean: + rm -f $(OBJS) $(JUNK) core *~ fuzix.com makecpmloader diskboot.bin + +z180.rel: z180.s kernel.def ../cpu-z180/z180.s + +image: + ../cpm-loader/makecpmloader ../cpm-loader/cpmload.bin ../fuzix.bin 0x88 fuzix.com + ../cpm-loader/makecpmloader ../cpm-loader/fuzixload.bin ../fuzix.bin 0x88 fuzix diff --git a/Kernel/platform-yaz180/README b/Kernel/platform-yaz180/README new file mode 100644 index 00000000..ad7d457a --- /dev/null +++ b/Kernel/platform-yaz180/README @@ -0,0 +1,27 @@ +TODO +- Verify the Z80_IO_BASE setting is correct +- Verify the memory bases are right and it boots +- Worry about the fact the PPIDE port arrangement is different +- Maybe - add SD support if the CSIO pins are exposed + +This is Fuzix for the YAZ180 based upon the Z180 code by Will Sowerbutts + + +Supported hardware: + - PPIDE interface (TODO) + - SD interface + - RS232 serial port (ASCI channel 0, tty1) + - RS232 serial port (ASCI channel 1, tty2) + +The file "Kernel/platform-yaz180/fuzix.com" is a CP/M executable which +will load and boot the Fuzix kernel from within CP/M. + +When booting the system from CP/M you can specify the root filesystem device on +the command line after the command name or unit number. + +For example, with root filesystem on /dev/hdb1 (minor #17) at the CP/M command +prompt: + + A> FUZIX hdb1 +or A> FUZIX 17 + diff --git a/Kernel/platform-yaz180/commonmem.s b/Kernel/platform-yaz180/commonmem.s new file mode 100644 index 00000000..cd68577e --- /dev/null +++ b/Kernel/platform-yaz180/commonmem.s @@ -0,0 +1,3 @@ + .module commonmem + .area _COMMONMEM + .include "../cpu-z80/std-commonmem.s" diff --git a/Kernel/platform-yaz180/config.h b/Kernel/platform-yaz180/config.h new file mode 100644 index 00000000..500a166b --- /dev/null +++ b/Kernel/platform-yaz180/config.h @@ -0,0 +1,51 @@ +/* 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: 8 x 64K banks, top 4KB is shared with kernel, 60KB-62KB is user memory */ +#define CONFIG_BANK_FIXED +/* Permit large I/O requests to bypass cache and go direct to userspace */ +#define CONFIG_LARGE_IO_DIRECT(x) 1 +/* 8 60K banks, 1 is kernel */ +#define MAX_MAPS 8 +#define MAP_SIZE PROGTOP /* WRS: I feel this should be 60KB, but setting it so breaks pagemap_realloc() when exec calls it */ + +/* Banks as reported to user space */ +#define CONFIG_BANKS 1 + +#define TICKSPERSEC 40U /* Ticks per second */ +#define PROGBASE 0x0000 /* also data base */ +#define PROGLOAD 0x0100 /* also data base */ +#define PROGTOP 0xF800 /* Top of program, base of U_DATA copy */ +#define KERNTOP 0xF000 /* Kernel has lower 60KB */ +#define PROC_SIZE 64 /* Memory needed per process */ + +/* We need a tidier way to do this from the loader */ +#define CMDLINE (0x0081) /* Location of root dev name */ +#define BOOTDEVICENAMES "hd#" + +#define CONFIG_DYNAMIC_BUFPOOL /* we expand bufpool to overwrite the _DISCARD segment at boot */ +#define NBUFS 4 /* Number of block buffers, keep in line with space reserved in yaz180.s */ +#define NMOUNTS 4 /* Number of mounts at a time */ + +/* Hardware parameters */ +#define Z180_IO_BASE 0x00 + +#define MAX_BLKDEV 2 /* 2 IDE drives */ + +#define CONFIG_IDE +#define CONFIG_PPIDE + +#define NUM_DEV_TTY 2 +/* ASCI0 as the console */ +#define TTYDEV (512+1) /* System console (used by kernel, init) */ + +#define platform_copyright() diff --git a/Kernel/platform-yaz180/crt0.s b/Kernel/platform-yaz180/crt0.s new file mode 100644 index 00000000..28c4b1ce --- /dev/null +++ b/Kernel/platform-yaz180/crt0.s @@ -0,0 +1,74 @@ +; 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 _HOME ; compiler stores __mullong etc in here if you use them + .area _CODE2 + .area _CONST + .area _INITIALIZED + .area _DATA + .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 _BUFFERS ; _BUFFERS grows to consume all before it (up to KERNTOP) + .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__DATA + 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 diff --git a/Kernel/platform-yaz180/devices.c b/Kernel/platform-yaz180/devices.c new file mode 100644 index 00000000..71237cad --- /dev/null +++ b/Kernel/platform-yaz180/devices.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct devsw dev_tab[] = /* The device driver switch table */ +{ +/* open close read write ioctl */ + { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl }, /* 0: /dev/hd -- standard block device interface */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, /* 1: /dev/fd -- floppy disks */ + { tty_open, tty_close, tty_read, tty_write, tty_ioctl }, /* 2: /dev/tty -- serial ports */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, /* 3: /dev/lp -- printer ports */ + { no_open, no_close, sys_read, sys_write, sys_ioctl }, /* 4: /dev/mem etc System devices (one offs) */ +}; + +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; +} + +void device_init(void) +{ + devide_init(); +} diff --git a/Kernel/platform-yaz180/devsdspi.c b/Kernel/platform-yaz180/devsdspi.c new file mode 100644 index 00000000..18090827 --- /dev/null +++ b/Kernel/platform-yaz180/devsdspi.c @@ -0,0 +1,232 @@ +/*-----------------------------------------------------------------------*/ +/* N8VEM Mark IV Z180 CSI/O SPI SD driver */ +/* 2014-12-27 Will Sowerbutts */ +/* 2014-12-29 Optimised for size/speed */ +/*-----------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "config.h" +#include +#include + +#define CSIO_CNTR_TE (1<<4) /* transmit enable */ +#define CSIO_CNTR_RE (1<<5) /* receive enable */ +#define CSIO_CNTR_END_FLAG (1<<7) /* operation completed flag */ + +#define MARK4_SD_CS (1<<2) /* chip select */ +#define MARK4_SD_WRITE_PROTECT (1<<4) /* write protect */ +#define MARK4_SD_CARD_DETECT (1<<5) /* card detect */ +#define MARK4_SD_INT_ENABLE (1<<6) /* interrupt enable */ +#define MARK4_SD_INT_PENDING (1<<7) /* interrupt enable */ + +__sfr __at (MARK4_IO_BASE + 0x09) MARK4_SD; + +/* the CSI/O and SD card send the bits of each byte in opposite orders, so we need to flip them over */ +static uint8_t reverse_byte(uint8_t byte) __naked +{ + /* code by John Metcalf, from http://www.retroprogramming.com/2014/01/fast-z80-bit-reversal.html */ + __asm + ld hl, #2 + add hl, sp + ld a, (hl) +reverse_byte_a: + ; reverse bits in A + ld l,a ; a = 76543210 + rlca + rlca ; a = 54321076 + xor l + and #0xAA + xor l ; a = 56341270 + ld l,a + rlca + rlca + rlca ; a = 41270563 + rrc l ; l = 05634127 + xor l + and #0x66 + xor l ; a = 01234567 + ld l, a ; return value in L + ret + __endasm; + byte; /* squelch compiler warning */ +} + +void sd_spi_clock(bool go_fast) +{ + unsigned char c; + + c = CSIO_CNTR & 0xf8; /* clear low three bits, gives fastest rate (clk/20) */ + if(!go_fast) + c = c | 0x03; /* set low two bits, clk/160 (can go down to clk/1280, see data sheet) */ + CSIO_CNTR = c; +} + +void sd_spi_raise_cs(void) +{ + /* wait for idle */ + while(CSIO_CNTR & (CSIO_CNTR_TE | CSIO_CNTR_RE)); + MARK4_SD = MARK4_SD & (~MARK4_SD_CS); +} + +void sd_spi_lower_cs(void) +{ + /* wait for idle */ + while(CSIO_CNTR & (CSIO_CNTR_TE | CSIO_CNTR_RE)); + MARK4_SD = MARK4_SD | MARK4_SD_CS; +} + +void sd_spi_transmit_byte(unsigned char byte) +{ + unsigned char c; + + /* reverse the bits before we busywait */ + byte = reverse_byte(byte); + + /* wait for any current transmit operation to complete */ + do{ + c = CSIO_CNTR; + }while(c & CSIO_CNTR_TE); + + /* write the byte and enable transmitter */ + CSIO_TRDR = byte; + CSIO_CNTR = c | CSIO_CNTR_TE; +} + +uint8_t sd_spi_receive_byte(void) +{ + unsigned char c; + + /* wait for any current transmit or receive operation to complete */ + do{ + c = CSIO_CNTR; + }while(c & (CSIO_CNTR_TE | CSIO_CNTR_RE)); + + /* enable receive operation */ + CSIO_CNTR = c | CSIO_CNTR_RE; + + /* wait for receive to complete */ + while(CSIO_CNTR & CSIO_CNTR_RE); + + /* read byte */ + return reverse_byte(CSIO_TRDR); +} + +/****************************************************************************/ +/* 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. */ +/****************************************************************************/ +COMMON_MEMORY + +/* WRS: measured byte transfer time as approx 5.66us with Z180 @ 36.864MHz, + three times faster. Main change is to start the next receive operation + as soon as possible and overlap the loop housekeeping with the receive. */ +bool sd_spi_receive_sector(void) __naked +{ + __asm +waitrx: + in0 a, (_CSIO_CNTR) ; wait for any current transmit or receive operation to complete + tst a, #0x30 + jr nz, waitrx + set 5, a ; set CSIO_CNTR_RE, enable receive operation for first byte + out0 (_CSIO_CNTR), a + ld h, a ; stash value for reuse later + + ; load parameters + ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user + ld de, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr + ld bc, #512 ; sector size + or a + push af ; stash is_user flag now in Z bit (we cannot load it again after we remap) + call nz, map_process_always ; map user process +rxnextbyte: + dec bc ; length-- + ld a, b + or c + jr nz, waitrx2 + res 5, h ; final byte: clear CSIO_CNTR_RE bit in H +waitrx2: + ld l, c ; store C temporarily + ld c, #_CSIO_CNTR ; load IO port address +waitrx3: + tstio #0x20 ; test bits in IO port (C) + jr nz, waitrx3 ; wait for receive to complete + in0 a, (_CSIO_TRDR) ; load received byte + out0 (_CSIO_CNTR), h ; start next receive (or NOP, if this is the final byte) + ld c, l ; restore C + ; reverse bits in A + ld l,a ; a = 76543210 + rlca + rlca ; a = 54321076 + xor l + and #0xAA + xor l ; a = 56341270 + ld l,a + rlca + rlca + rlca ; a = 41270563 + rrc l ; l = 05634127 + xor l + and #0x66 + xor l ; a = 01234567 + ld (de), a ; store reversed byte value + inc de ; ptr++ + bit 5, h + jr nz, rxnextbyte ; go again if not yet done + jr transferdone ; we are done + __endasm; +} + +bool sd_spi_transmit_sector(void) __naked +{ + __asm + ; load parameters + ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user + ld de, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr + ld hl, #512 ; sector size + or a + push af ; stash is_user flag now in Z bit (we cannot load it again after we remap) + call nz, map_process_always ; map user process +gotransmit: + in0 a, (_CSIO_CNTR) + and #0xDF ; mask off RE bit + or #0x10 ; set TE bit + ld b, a ; B now contains CNTR register value to start transmission +txnextbyte: + ld a, (de) + ; reverse bits in A + ld c,a + rlca + rlca + xor c + and #0xAA + xor c + ld c,a + rlca + rlca + rlca + rrc c + xor c + and #0x66 + xor c + ld c, #_CSIO_CNTR ; load IO port address +waittx: + tstio #0x10 ; test bits in IO port (C) + jr nz, waittx ; wait for transmit to complete + out0 (_CSIO_TRDR), a ; write byte to transmit + out0 (_CSIO_CNTR), b ; start transmit + inc de ; ptr++ + dec hl ; length-- + ld a, h + or l + jr nz, txnextbyte ; length != 0, go again +transferdone: ; note this code is shared with sd_spi_receive_block + ld l, #1 ; return true + pop af ; recover is_user bit in Z flag + ret z ; return if kernel still mapped + jp map_kernel ; else map kernel and return + __endasm; +} diff --git a/Kernel/platform-yaz180/devtty.c b/Kernel/platform-yaz180/devtty.c new file mode 100644 index 00000000..9ab6c8bf --- /dev/null +++ b/Kernel/platform-yaz180/devtty.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include + +static char tbuf1[TTYSIZ]; +static 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 }, +}; + +static tcflag_t console_mask[4] = { + _ISYS, + _OSYS, + _CSYS, + _LSYS +}; + +/* TODO: stty support for the Z180 ports */ +tcflag_t *termios_mask[NUM_DEV_TTY + 1] = { + NULL, + console_mask, + console_mask, +}; + +void tty_setup(uint8_t minor, uint8_t flags) +{ + 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; + } +} + +void tty_sleeping(uint8_t minor) +{ + minor; +} + +void tty_data_consumed(uint8_t minor) +{ +} + +ttyready_t tty_writeready(uint8_t minor) +{ + minor; + return TTY_READY_NOW; +} + +/* kernel writes to system console -- never sleep! */ +void kputchar(char c) +{ + tty_putc(TTYDEV & 0xFF, c); + if(c == '\n') + tty_putc(TTYDEV & 0xFF, '\r'); +} diff --git a/Kernel/platform-yaz180/devtty.h b/Kernel/platform-yaz180/devtty.h new file mode 100644 index 00000000..12401d82 --- /dev/null +++ b/Kernel/platform-yaz180/devtty.h @@ -0,0 +1,9 @@ +#ifndef __DEVTTY_DOT_H__ +#define __DEVTTY_DOT_H__ +void tty_pollirq_asci0(void); +void tty_pollirq_asci1(void); + +#ifdef CONFIG_PROPIO2 +void tty_poll_propio2(void); +#endif +#endif diff --git a/Kernel/platform-yaz180/discard.c b/Kernel/platform-yaz180/discard.c new file mode 100644 index 00000000..94ca9afa --- /dev/null +++ b/Kernel/platform-yaz180/discard.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include "config.h" +#include + +void init_hardware_c(void) +{ + ramsize = 912; + procmem = 848; + /* zero out the initial bufpool */ + memset(bufpool, 0, (char*)bufpool_end - (char*)bufpool); +} + +void pagemap_init(void) +{ + int i; + + /* YAZ180 has RAM from 0C000 to EFFFF + * First 64K is used by the kernel. + * Each process gets the full 64K for now. + * Page size is 4KB. + * We don't do anything right now with C000-FFFF so that is + * a good place to load any additional tools + */ + for(i = 0x20; i < 0xF0; i+=0x10) + pagemap_add(i); +} + +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? */ +} + +uint8_t platform_param(char *p) +{ + used(p); + return 0; +} diff --git a/Kernel/platform-yaz180/diskboot.s b/Kernel/platform-yaz180/diskboot.s new file mode 100644 index 00000000..9b57d5e9 --- /dev/null +++ b/Kernel/platform-yaz180/diskboot.s @@ -0,0 +1,197 @@ +; 2015-01-21 Will Sowerbutts +; +; Boot sector for N8VEM Mark IV SBC with UNA BIOS, based on +; my UNA CP/M boot sector (2014-07-11) + + .module diskboot + + +; we are loaded at 0x8000 +himem = 0xFA00 ; our location in memory +buffer = 0xF800 ; disk buffer (512 bytes) +stacktop = 0xFE00 ; top of stack (512 bytes) + +; UNA BIOS constants +UNABIOS_STUB_ENTRY = 0xFFFD ; main UNA entry vector +UNABIOS_BOOTHISTORY = 0xFC ; C register (subfunction in B) +UNABIOS_BOOT_GET = 0x00 ; B register (BOOTHISTORY subfunction) +UNABIOS_GETINFO = 0xFA ; C regsister (subfunction in B) +UNABIOS_GET_USER_PAGES = 0x05 ; B register (GETINFO subfunction) +UNABIOS_BANKEDMEM = 0xFB ; C register (subfunction in B) +UNABIOS_BANK_GET = 0x00 ; B register (BANKEDMEM subfunction) +UNABIOS_BANK_SET = 0x01 ; B register (BANKEDMEM subfunction) +UNABIOS_GET_HMA = 0xF1 ; C register (subfunction in B) +UNABIOS_BLOCK_SETLBA = 0x41 ; C register (unit number in B, 28-bit LBA in DEHL) +UNABIOS_BLOCK_READ = 0x42 ; C register (unit number in B, buffer address in DE, sector count in L) +UNABIOS_OUTPUT_WRITE = 0x12 ; C register (unit number in B) + + .area _LOADER (ABS) + .org himem +start: + ; UNA BIOS loads us from disk sector 0 at 0x8000 + jr gocopy ; we must start with a JP or JR instruction. + .ds 0x40 - (.-start) ; must leave room for floppy or partition superblock information + + ; Copy us up into high memory +gocopy: ld hl, #0x8000 + ld de, #himem + ld bc, #512 + ldir + jp go + + ; Executes in high memory +go: ld sp, #stacktop ; set inital stack + ; write a character + ld e, #0x5B ; '[' + call printchar + + ; determine the boot unit + ld bc, #(UNABIOS_BOOT_GET << 8 | UNABIOS_BOOTHISTORY) + call #UNABIOS_STUB_ENTRY + ld a, l + ld (unit), a ; save boot unit + + ; get the page number for the user memory bank + ld bc, #(UNABIOS_GET_USER_PAGES << 8 | UNABIOS_GETINFO) + call #UNABIOS_STUB_ENTRY + ; returns EXEC_PAGE value in DE + + ; map in user memory bank + ld bc, #(UNABIOS_BANK_SET << 8 | UNABIOS_BANKEDMEM) + call #UNABIOS_STUB_ENTRY + + ; write unabios vector in user memory + ld hl, #UNABIOS_STUB_ENTRY + ld de, #0x0008 + ld bc, #3 + ldir + + ; wipe BDOS entry vector + ld a, #0x76 ; halt instruction + ld (0x0005), a ; this is used as a marker to detect cold boot versus warm reload + + ; wipe persistent memory pointer (persist_ptr, immediately below UNA UBIOS stub / HMA) + ld c, #UNABIOS_GET_HMA ; get pointer to lowest byte used by UNA BIOS stub + call #UNABIOS_STUB_ENTRY ; returns lowest used byte in HL. + xor a ; zero out the two bytes below that. + dec hl + ld (hl), a + dec hl + ld (hl), a + + ld a, (firstblock) + ld (block), a + +nextblock: + ; print a = character only every other block + ld a, (block) + and #1 + jr z, setlba + ld e, #0x3D ; '=' + call printchar + +setlba: ; set LBA + xor a + ld d, a + ld e, a + ld h, a + ld a, (block) + ld l, a + inc a ; setup for next block now + ld (block), a + ld c, #UNABIOS_BLOCK_SETLBA + ld a, (unit) + ld b, a + call #UNABIOS_STUB_ENTRY + jr nz, error + + ; read block into buffer + ld c, #UNABIOS_BLOCK_READ + ld a, (unit) + ld b, a + ld l, #1 + ld de, #buffer + call #UNABIOS_STUB_ENTRY + jr nz, error + + ; copy block into low memory + ld hl, #buffer + ld de, (copyaddr) + ld bc, #512 + ldir + + ld (copyaddr), de + ld a, (firstblock) + ld d, a + ld a, (count) + ld e, a + ld a, (block) + sub d + cp e + jr nz, nextblock + + ld e, #0x5D ; ']' + call printchar + ld e, #0x0D + call printchar + ld e, #0x0A + call printchar + + ; we're loaded. let's go. + ld hl, (entryaddr) + jp (hl) + + +; print a hex byte in A +byt_out: + push af ; save low nibble + rrca ; move high nibble into position + rrca ; ** + rrca + rrca + call nib_out ; put out the high nibble + pop af ; fall into nib_out to put out low nibble +; print a hex-nibble in A +nib_out: + and #0x0F ; mask the nibble + add #0 ; clear the AUX carry bit + daa ; decimal adjust the A + add #0xF0 ; move hi-nib into carry, hi-nib is 0 or F + adc #0x40 ; form ascii character + ld e, a + ; fall through into printchar +printchar: + ld bc, #UNABIOS_OUTPUT_WRITE + jp UNABIOS_STUB_ENTRY + +error: + ; print the error + ld a, c + call byt_out + ld e, a + call printchar + + ; sad face :( + ld e, #0x20 ; space + call printchar + ld e, #0x3A ; ':' + call printchar + ld e, #0x28 ; '(' + call printchar + + ; park the vehicle + halt + +block: .ds 1 +unit: .ds 1 +firstblock: .db 2 ; start loading at sector 2 +count: .db 124 ; max sectors we can load before we overwrite ourselves (62KB) +copyaddr: .dw 0x0088 ; load address +entryaddr: .dw 0x0088 ; entry address + + .ds 0x1BE - ( . -start) ; pad to start of partition tables + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; partition 1 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; partition 2 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; partition 3 + .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; partition 4 + .dw 0xAA55 ; DOS boot signature diff --git a/Kernel/platform-yaz180/fuzix.lnk b/Kernel/platform-yaz180/fuzix.lnk new file mode 100644 index 00000000..db459b73 --- /dev/null +++ b/Kernel/platform-yaz180/fuzix.lnk @@ -0,0 +1,43 @@ +-mwxuy +-i fuzix.ihx +-b _CODE=0x0088 +-b _COMMONMEM=0xF800 +-b _DISCARD=0xE000 +-l z180 +platform-yaz180/crt0.rel +platform-yaz180/commonmem.rel +platform-yaz180/z180.rel +platform-yaz180/yaz180.rel +platform-yaz180/main.rel +start.rel +version.rel +lowlevel-z180.rel +usermem_std-z180.rel +timer.rel +kdata.rel +usermem.rel +platform-yaz180/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 +bankfixed.rel +swap.rel +devsys.rel +platform-yaz180/discard.rel +platform-yaz180/devtty.rel +platform-yaz180/devide.rel +platform-yaz180/devide_discard.rel +platform-yaz180/ppide.rel +platform-yaz180/mbr.rel +platform-yaz180/blkdev.rel +platform-yaz180/monitor.rel +-e diff --git a/Kernel/platform-yaz180/kernel.def b/Kernel/platform-yaz180/kernel.def new file mode 100644 index 00000000..c36d8ae6 --- /dev/null +++ b/Kernel/platform-yaz180/kernel.def @@ -0,0 +1,21 @@ +; UZI mnemonics for memory addresses etc + +; Move down to 0xF600 to fit the monitor in +U_DATA .equ 0xF800 ; (this is struct u_data from kernel.h) +U_DATA__TOTALSIZE .equ 0x200 ; 256+256 bytes. +Z80_TYPE .equ 2 + +OS_BANK .equ 0x00 ; value from include/kernel.h + +; N8VEM Mark IV mnemonics +FIRST_RAM_BANK .equ 0x10 ; 10000 (actually C000-FFFF is avaiable) +Z180_IO_BASE .equ 0x00 + +; No standard clock speed for the Mark IV board, but this is a common choice. +USE_FANCY_MONITOR .equ 1 ; disabling this saves around approx 0.5KB +CPU_CLOCK_KHZ .equ 36864 ; 18.432MHz * 2 +Z180_TIMER_SCALE .equ 20 ; CPU clocks per timer tick +TICKSPERSEC .equ 40 ; timer interrupt rate (Hz) + +PROGBASE .equ 0x0000 +PROGLOAD .equ 0x0100 diff --git a/Kernel/platform-yaz180/main.c b/Kernel/platform-yaz180/main.c new file mode 100644 index 00000000..1303c6f7 --- /dev/null +++ b/Kernel/platform-yaz180/main.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include "config.h" +#include + +uint16_t ramtop = PROGTOP; +extern unsigned char irqvector; + +struct blkbuf *bufpool_end = bufpool + NBUFS; /* minimal for boot -- expanded after we're done with _DISCARD */ + +void platform_discard(void) +{ + while(bufpool_end < (struct blkbuf*)(KERNTOP - sizeof(struct blkbuf))){ + memset(bufpool_end, 0, sizeof(struct blkbuf)); +#if BF_FREE != 0 + bufpool_end->bf_busy = BF_FREE; /* redundant when BF_FREE == 0 */ +#endif + bufpool_end->bf_dev = NO_DEVICE; + bufpool_end++; + } +} + +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 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; + } +} diff --git a/Kernel/platform-yaz180/monitor.s b/Kernel/platform-yaz180/monitor.s new file mode 100644 index 00000000..bf9bca71 --- /dev/null +++ b/Kernel/platform-yaz180/monitor.s @@ -0,0 +1,41 @@ +; 2015-01-17 William R Sowerbutts + + .module monitor + .include "kernel.def" + .globl _platform_monitor + .globl _platform_reboot + .globl map_kernel + +; ----------------------------------------------------------------------------- +.ifne USE_FANCY_MONITOR ; ----------------------------------------------------- + .area _CODE ; actual monitor lives in kernel bank + .include "../lib/monitor-z80.s" + + .area _COMMONMEM ; just a stub goes in common memory +_platform_monitor: + di + call map_kernel + jp monitor_entry + + +; ----------------------------------------------------------------------------- +.else ; MICRO MONITOR --------------------------------------------------------- + .globl outchar + .globl outnewline + .globl outhl + + .area _COMMONMEM +_platform_monitor: di + call outnewline + ; just dump a few words from the stack + ld b, #50 +stacknext: pop hl + call outhl + ld a, #' ' + call outchar + djnz stacknext + halt +.endif + +_platform_reboot: ; TODO + jr _platform_monitor diff --git a/Kernel/platform-yaz180/platform_ide.h b/Kernel/platform-yaz180/platform_ide.h new file mode 100644 index 00000000..95d67e15 --- /dev/null +++ b/Kernel/platform-yaz180/platform_ide.h @@ -0,0 +1,38 @@ +#ifdef CONFIG_PPIDE +#define PPIDE_BASE 0x60 /* Base address of 8255A */ +#define IDE_REG_INDIRECT /* IDE registers are not directly connected to the CPU bus */ + +/* IDE control signal to 8255 port C mapping */ +#define PPIDE_A0_LINE 0x01 // Direct from 8255 to IDE interface +#define PPIDE_A1_LINE 0x02 // Direct from 8255 to IDE interface +#define PPIDE_A2_LINE 0x04 // Direct from 8255 to IDE interface +#define PPIDE_CS0_LINE 0x08 // Inverter between 8255 and IDE interface +#define PPIDE_CS1_LINE 0x10 // Inverter between 8255 and IDE interface +#define PPIDE_WR_LINE 0x20 // Inverter between 8255 and IDE interface +#define PPIDE_WR_BIT 5 // (1 << PPIDE_WR_BIT) = PPIDE_WR_LINE +#define PPIDE_RD_LINE 0x40 // Inverter between 8255 and IDE interface +#define PPIDE_RD_BIT 6 // (1 << PPIDE_RD_BIT) = PPIDE_RD_LINE +#define PPIDE_RST_LINE 0x80 // Inverter between 8255 and IDE interface + +/* 8255 configuration */ +#define PPIDE_PPI_BUS_READ 0x92 +#define PPIDE_PPI_BUS_WRITE 0x80 + +/* IDE register addresses */ +#define ide_reg_data (PPIDE_CS0_LINE) +#define ide_reg_error (PPIDE_CS0_LINE | PPIDE_A0_LINE) +#define ide_reg_features (PPIDE_CS0_LINE | PPIDE_A0_LINE) +#define ide_reg_sec_count (PPIDE_CS0_LINE | PPIDE_A1_LINE) +#define ide_reg_lba_0 (PPIDE_CS0_LINE | PPIDE_A1_LINE | PPIDE_A0_LINE) +#define ide_reg_lba_1 (PPIDE_CS0_LINE | PPIDE_A2_LINE) +#define ide_reg_lba_2 (PPIDE_CS0_LINE | PPIDE_A2_LINE | PPIDE_A0_LINE) +#define ide_reg_lba_3 (PPIDE_CS0_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE) +#define ide_reg_devhead (PPIDE_CS0_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE) +#define ide_reg_command (PPIDE_CS0_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE | PPIDE_A0_LINE) +#define ide_reg_status (PPIDE_CS0_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE | PPIDE_A0_LINE) +#define ide_reg_altstatus (PPIDE_CS1_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE) +#define ide_reg_control (PPIDE_CS1_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE | PPIDE_A0_LINE) +#endif /* CONFIG_PPIDE */ + +#define ide_select(x) +#define ide_deselect() diff --git a/Kernel/platform-yaz180/ppide.c b/Kernel/platform-yaz180/ppide.c new file mode 100644 index 00000000..f6d0ee10 --- /dev/null +++ b/Kernel/platform-yaz180/ppide.c @@ -0,0 +1,128 @@ +/* + * FIXME: this is wrong for the YAZ180 as the bit mappings are + * different. + */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PPIDE + +__sfr __at (PPIDE_BASE + 0x00) ppi_port_a; /* IDE bus LSB */ +__sfr __at (PPIDE_BASE + 0x01) ppi_port_b; /* IDE bus MSB */ +__sfr __at (PPIDE_BASE + 0x02) ppi_port_c; /* IDE bus control signals */ +__sfr __at (PPIDE_BASE + 0x03) ppi_control; /* 8255 command register */ + +void ppide_init(void) +{ + ppi_control = PPIDE_PPI_BUS_READ; + ppi_port_c = ide_reg_status; +} + +uint8_t devide_readb(uint8_t regaddr) +{ + uint8_t r; + + /* note: ppi_control should contain PPIDE_PPI_BUS_READ already */ + ppi_port_c = regaddr; + ppi_port_c = regaddr | PPIDE_RD_LINE; /* begin /RD pulse */ + r = ppi_port_a; + ppi_port_c = regaddr; /* end /RD pulse */ + return r; +} + +void devide_writeb(uint8_t regaddr, uint8_t value) +{ + ppi_control = PPIDE_PPI_BUS_WRITE; + ppi_port_c = regaddr; + ppi_port_a = value; + ppi_port_b = 0; + ppi_port_c = regaddr | PPIDE_WR_LINE; + /* FIXME: check timing */ + ppi_port_c = regaddr; + ppi_control = PPIDE_PPI_BUS_READ; +} + +/****************************************************************************/ +/* 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. */ +/****************************************************************************/ +COMMON_MEMORY + +void devide_read_data(void) __naked +{ + __asm + ld a, #ide_reg_data + ld c, #PPIDE_BASE+2 ; select control lines + out (c), a ; select IDE data register + ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr + ld d, #ide_reg_data ; register address + ld e, #ide_reg_data | PPIDE_RD_LINE ; register address with /RD asserted + ld b, #0 ; setup count + ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user + or a ; test is_user + push af ; save flags + ld a, #PPIDE_BASE+0 ; I will be needing this later + call nz, map_process_always ; map user memory first if required +goread: ; now we do the transfer + out (c), e ; assert /RD + ld c, a ; PPIDE_BASE + ini ; read byte from LSB + inc c ; up to MSB + ini ; read byte from MSB + inc c ; control lines + out (c), d ; de-assert /RD + inc b ; (delay) counteract second ini instruction + jr nz, goread ; (delay) next word + ; read completed + pop af ; recover is_user test result + ret z ; done if kernel memory transfer + jp map_kernel ; else map kernel then return + __endasm; +} + +void devide_write_data(void) __naked +{ + __asm + ld c, #PPIDE_BASE+2 ; select control lines + ld a, #ide_reg_data + out (c), a ; select data register + ld a, #PPIDE_PPI_BUS_WRITE + inc c ; up to 8255A command register + out (c), a ; 8255A ports A, B to output mode + dec c ; back down to the control lines + ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr + ld d, #ide_reg_data ; register address + ld e, #ide_reg_data | PPIDE_WR_LINE ; register address with /WR asserted + ld b, #0 ; setup count + ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user + or a ; test is_user + push af ; save flags + ld a, #PPIDE_BASE+0 ; I will be needing this later + call nz, map_process_always ; map user memory first if required +gowrite: ; now we do the transfer + out (c), d ; de-assert /WR + ld c, a ; PPIDE_BASE + outi ; write byte to LSB + inc c ; up to MSB + outi ; write byte to MSB + inc c ; up to control lines + out (c), e ; assert /WR + inc b ; (delay) offset to counteract second outi instruction + jr nz, gowrite ; (delay) next word + ; write completed + out (c), d ; de-assert /WR + ld a, #PPIDE_PPI_BUS_READ + inc c ; up to 8255A command register + out (c), a ; 8255A ports A, B to read mode + pop af ; recover is_user test result + ret z ; done if kernel memory transfer + jp map_kernel ; else map kernel then return + __endasm; +} + +#endif diff --git a/Kernel/platform-yaz180/target.mk b/Kernel/platform-yaz180/target.mk new file mode 100644 index 00000000..cacca5d4 --- /dev/null +++ b/Kernel/platform-yaz180/target.mk @@ -0,0 +1,2 @@ +export CPU = z180 +export USERCPU = z80 diff --git a/Kernel/platform-yaz180/yaz180.s b/Kernel/platform-yaz180/yaz180.s new file mode 100644 index 00000000..98da09f1 --- /dev/null +++ b/Kernel/platform-yaz180/yaz180.s @@ -0,0 +1,87 @@ +; 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 inchar + .globl outchar + .globl platform_interrupt_all + .globl _bufpool + + ; imported symbols + .globl z180_init_hardware + .globl z180_init_early + .globl outhl + .globl outnewline + + .include "kernel.def" + .include "../cpu-z180/z180.def" + .include "../kernel.def" + +; ----------------------------------------------------------------------------- +; Buffers +; ----------------------------------------------------------------------------- + .area _BUFFERS +_bufpool: + .ds (BUFSIZE * 4) ; adjust NBUFS in config.h in line with this + +; ----------------------------------------------------------------------------- +; Initialisation code +; ----------------------------------------------------------------------------- + .area _DISCARD + +init_early: + jp z180_init_early + +init_hardware: + ; 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 +; ----------------------------------------------------------------------------- + .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 + +; inchar: Wait for character on UART, return in A +; destroys: AF +inchar: + in0 a, (ASCI_STAT0) + rlca + jr nc, inchar + in0 a, (ASCI_RDR0) + ret + +platform_interrupt_all: + ret diff --git a/Kernel/platform-yaz180/z180.s b/Kernel/platform-yaz180/z180.s new file mode 100644 index 00000000..7371295b --- /dev/null +++ b/Kernel/platform-yaz180/z180.s @@ -0,0 +1 @@ +.include "../cpu-z180/z180.s" -- 2.34.1