From: Alan Cox Date: Sat, 26 Jan 2019 23:32:03 +0000 (+0000) Subject: z80sbc64: another day another platform X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=3ff9caba9179566ba8d3a87b647495b4c0ceb49a;p=FUZIX.git z80sbc64: another day another platform --- diff --git a/Kernel/platform-rc2014-sbc64/Makefile b/Kernel/platform-rc2014-sbc64/Makefile new file mode 100644 index 00000000..a5da4a0b --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/Makefile @@ -0,0 +1,65 @@ +ASRCS = crt0.s tricks.s commonmem.s rc2014.s +CSRCS = devices.c main.c devtty.c +DISCARD_CSRCS = discard.c +DISCARD_DSRCS = ../dev/devide_discard.c ../dev/ds1302_discard.c ../dev/mbr.c +DSRCS = ../dev/devide.c ../dev/blkdev.c +DSRCS += ../dev/ds1302.c +DASRCS = ../dev/ds1302_rc2014.s +NSRCS = + +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)) +NOBJS = $(patsubst ../dev/net/%.c,%.rel, $(NSRCS)) + +OBJS = $(AOBJS) $(COBJS) $(DOBJS) $(DAOBJS) $(DISCARD_DOBJS) $(DISCARD_COBJS) $(NOBJS) + +CROSS_CCOPTS += -I../dev/ -I../dev/net/ + +CROSS_CC_HIGH = --codeseg COMMONMEM + +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 $< + +$(NOBJS): %.rel: ../dev/net/%.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +clean: + rm -f $(OBJS) $(JUNK) core *~ diskstrap fuzix.rom boot loader.inc + +image: boot loader + +boot: boot.s + sdasz80 -o boot.s + sdldz80 -i boot.rel + makebin -s 4096 boot.ihx boot + od -v -w1 -tx1 boot | cut -b 9- | head -4096 | \ + sed -e 's/^/ .db 0x/' >loader.inc + +loader: loader.s loader.inc + sdasz80 -o loader.s + sdldz80 -i loader.rel + +loader.inc: boot.s diff --git a/Kernel/platform-rc2014-sbc64/README b/Kernel/platform-rc2014-sbc64/README new file mode 100644 index 00000000..e1439feb --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/README @@ -0,0 +1,78 @@ +Fuzix for the Z80SBC64 and in theory Z80MB64 platforms + +Supported Hardware + + RC2014 SBC64 mainboard or MB64 (see notes) + DS1302 RTC card (required) + + (CTC is not yet supported but will be a far faster/nicer option once + it is. At that point you will need CTC or RTC or both) + + Options + + RC2014 SIO/2 board (or compatible). ACIA is not supported. + + RC2014 Joystick + +In Progress + CTC at 0x88 for SIO timing and for system clock + + Most other RC2014 cards supported by other platforms should also be + possible to add if anyone needs them. + + Suspend to RAM/Resume + +Things That Don't Work (yet) + + Flow control + SIO speed changing + +Notes + The Z80MB64 is 22MHz which means there are not yet RTC or CTC for it! + +Setting It Up + +Put the jumpers into serial download mode and send Z80SBCLD.BIN as per normal + +When it prompts 'Z80BC64 Loader 0.2' +- Send Kernel/platform/loader.ihx +- Type G B000 (only the G is echoed) + +You should see + + Configuring booter...Done + + and then the message + + SBC64 Boot Loader + Loading image from disk...done + +You can now power off and change the jumper. + +At this point it will report a bad image unless you've created a suitable Fuzix +image on the CF. Fuzix is looking for a standard Fuzix style PC partitioned CF +card with a filesystem partition (7e) and a swap partition (7f). Make sure the +partition table is a modern style one (starting partitions at block 2048 not +block 63). + +The kernel image goes at block 2+ on the CF card. With Linux it's a simple matter +of using dd (carefully) + +dd if=Kernel/fuzix.bin of=/dev/sdxx bs=512 seek=2 conv=notrunc + +Something similar should work on MacOS. People have reported problems with Cygwin +dd on windows but I'm not clear why. + +To get back to the CP/M image put the jumper back into serial mode and +follow the download instructions. You will need a different CF card for CP/M as +unfortunately like RC2014 CP/M in general the supplied CP/M does not honour +partition tables. + +Emulation + +Apart from remembering the emulated hard disk has a 1K header all is the same + + ./rc2014 -m z80sbc64 -r sbc64.ram -i sbc64.ide -R + +(and to bootstrap initially copy Z80SBCLD.BIN to sbc64.ram) + diff --git a/Kernel/platform-rc2014-sbc64/boot.s b/Kernel/platform-rc2014-sbc64/boot.s new file mode 100644 index 00000000..751acae1 --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/boot.s @@ -0,0 +1,172 @@ +; +; BOOT and restart image +; +; We are entered at power on with bank = 3 +; + +resume_tag .equ 0x0084 + + .area _BOOT(ABS) + + .org 0x0000 +start: + jp init + + .org 0x0080 +init: + di + ld sp,#0x1000 ; safe spot + + ld hl,#signon + call outstr + + ; See if we are loading an image or resuming from memory + or a + ld hl,(resume_tag) + ld de,#0xC0DE + sbc hl,de + jr nz, disk_load + ld a,(0x1003) + cp #0xC3 + jr nz, disk_load + ld hl,#resuming + call outstr + jp 0x1003 + +signon: + .ascii 'SBC64 Boot Loader' + .db 13,10,0 +resuming: + .ascii 'Resuming from memory' + .db 13,10,0 + + +disk_load: + ld hl,#loading + call outstr + ; Init the ATA CF + ld a,#0xE0 + out (0x16),a + xor a + out (0x14),a + out (0x15),a + ; Set 8bit mode + call wait_ready + ld a, #1 ; 8bit PIO + out (0x11),a + ld a, #0xEF ; SET FEATURES (8bit PIO) + out (0x17),a + call wait_ready + + ; Load the kernel + ld d,#1 + ld bc,#0x10 ; c = data port b = 0 + ld hl,#0x1000 ; Load 1000-D000 (should be sufficient) +loader: + inc d + call load_sector + ld a,#0xD0 + cp h + jr nz, loader + + ld a,(0x1003) + cp #0xC3 + jr nz, badimage + ld a,(0x1003) + cp #0xC3 + jr nz, badimage + ld hl,(0x1006) + ld de,#0x10AD + or a + sbc hl,de + jr nz, badimage + + ld hl,#gogogo + call outstr + jp 0x1000 + +loading: + .asciz 'Loading image from disk...' +gogogo: + .ascii 'done' + .db 13,10,0 + +badimage: + ld hl,#badbadbad + call outstr + halt + +badbadbad: + .ascii 'Invalid image or CF problem' + .db 13,10,0 + +; +; Load sector d from disk into HL and advance HL accordingly +; +load_sector: + ld a,d + out (0x13),a ; LBA + ld a,#1 + out (0x12),a ; 1 sector + ld a,#0x20 + out (0x17),a ; command + ; Wait +wait_drq: + in a,(0x17) + bit 3,a + jr z, wait_drq + ; Get data, leave HL pointing to next byte, leaves B as 0 again + inir + inir + ret +wait_ready: + in a,(0x17) + bit 6,a + jr z,wait_ready + ret + +outstr: + ld a,(hl) + or a + ret z + call outchar + inc hl + jr outstr + +; +; Based on the ROM code but slightly tighter +; - use ld a,#0 so 0 and 1 bits are same length +; - don't duplicate excess code in the hi/lo bit paths +; - use conditional calls to keep 0/1 timing identical +; +; FIXME: my math says it's still slightly off timing. +; +outchar: + push bc + ld c,a + ld b,#8 + call lobit + ld a,c +txbit: + rrca + call c, hibit + call nc, lobit + djnz txbit + pop bc +hibit: + push af + ld a,#0xff + out (0xf9),a + ld a,#7 +bitwait: + dec a + jp nz,bitwait + pop af + ret +lobit: + push af + ld a,#0 + out (0xf9),a + ld a,#7 + dec a + jp bitwait diff --git a/Kernel/platform-rc2014-sbc64/commonmem.s b/Kernel/platform-rc2014-sbc64/commonmem.s new file mode 100644 index 00000000..12884457 --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/commonmem.s @@ -0,0 +1,9 @@ +; +; Common is placed at 0xF000 by fuzix.lnk +; + + .module commonmem + + .area _COMMONMEM + + .include "../cpu-z80/std-commonmem.s" diff --git a/Kernel/platform-rc2014-sbc64/config.h b/Kernel/platform-rc2014-sbc64/config.h new file mode 100644 index 00000000..b6f8b8d8 --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/config.h @@ -0,0 +1,69 @@ +/* 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 +/* Fixed bank sizes in RAM */ +#define CONFIG_BANK_FIXED +#define MAP_SIZE 0x8000 +#define MAX_MAPS 2 /* 128K: 2 x 32K user 1 x 60K kernel 4K boot */ + +/* Permit large I/O requests to bypass cache and go direct to userspace */ +#define CONFIG_LARGE_IO_DIRECT(x) 1 +/* Memory banks */ +#define CONFIG_BANKS 1 +#define TICKSPERSEC 10 /* Ticks per second */ +#define PROGBASE 0x0000 /* also data base */ +#define PROGLOAD 0x0100 /* also data base */ +#define PROGTOP 0x7E00 /* Top of program */ + +#define PROC_SIZE 32 /* Memory needed per process */ + +#define SWAPDEV (swap_dev) /* A variable for dynamic, or a device major/minor */ +extern unsigned int swap_dev; +#define SWAP_SIZE 0x40 /* 32K in blocks (prog + udata) */ +#define SWAPBASE 0x0000 /* start at the base of user mem */ +#define SWAPTOP 0x8000 /* Swap out program */ +#define MAX_SWAPS 16 /* We will size if from the partition */ +/* Swap will be set up when a suitably labelled partition is seen */ +#define CONFIG_DYNAMIC_SWAP + +/* + * 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)) + +/* We need a tidier way to do this from the loader */ +#define CMDLINE NULL /* 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 zeta-v2.s */ +#define NMOUNTS 2 /* Number of mounts at a time */ + +#define MAX_BLKDEV 2 /* 2 IDE */ + +/* On-board DS1302, we can read the time of day from it */ +#define CONFIG_RTC +#define CONFIG_RTC_FULL +#define CONFIG_NO_CLOCK + +/* IDE/CF support */ +#define CONFIG_IDE + +/* Device parameters */ +#define NUM_DEV_TTY 3 + +/* UART0 as the console */ +#define BOOT_TTY (512 + 1) +#define TTY_INIT_BAUD B115200 /* Hardwired generally */ + +#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ + +#define platform_copyright() diff --git a/Kernel/platform-rc2014-sbc64/crt0.s b/Kernel/platform-rc2014-sbc64/crt0.s new file mode 100644 index 00000000..45a1bcad --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/crt0.s @@ -0,0 +1,127 @@ + .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. + + ; Start with the ROM area CODE-CODE2 + .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 + .area _GSINIT ; unused + .area _GSFINAL ; unused + .area _BUFFERS ; _BUFFERS grows to consume all before it (up to KERNTOP) + ; Discard is loaded where process memory wil blow it away + .area _DISCARD + ; The rest grows upwards from C000 starting with the udata so we can + ; swap in one block, ending with the buffers so they can expand up + ; note that areas below here may be overwritten by the heap at runtime, so + ; put initialisation stuff in here + ; These get overwritten and don't matter + .area _INITIALIZER ; binman copies this to the right place for us + .area _COMMONMEM + + ; exported symbols + .globl _suspend + + ; imported symbols + .globl _fuzix_main + .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 s__BUFFERS + .globl l__BUFFERS + .globl kstack_top + + .globl interrupt_handler + .globl nmi_handler + .globl outstring + + .include "kernel.def" + + ; Starts at 0x1000 at the moment + + ; Entered with bank = 3 from the bootstrap logic + +resume_sp .equ 0x0080 +resume_tag .equ 0x0084 + + .area _CODE + + jp start + jp resume + .dw 0x10AD + +start: + 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 + + 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_hardware + call _fuzix_main + ; Should never return + di +stop: halt + jr stop +; +; Helpers for the battery backed memory model +; +resume: + di + ld sp, (resume_sp) + pop iy + pop ix + ld hl, #0 + ld (resume_tag), hl + ret + +_suspend: + push ix + push iy + ld (resume_sp), sp + ld hl,#0xC0DE + ld (resume_tag), hl + ld hl, #suspended + call outstring + di + halt +suspended: + .ascii 'Suspended, you may now power off' + .db 13,10,0 + + + \ No newline at end of file diff --git a/Kernel/platform-rc2014-sbc64/devices.c b/Kernel/platform-rc2014-sbc64/devices.c new file mode 100644 index 00000000..f3f1029c --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/devices.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct devsw dev_tab[] = /* The device driver switch table */ +{ +/* open close read write ioctl */ + /* 0: /dev/hd - block device interface */ +#ifdef CONFIG_IDE + { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl}, +#else + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl}, +#endif + /* 1: /dev/fd - Floppy disk block devices */ + { 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: RAM disk */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl}, + /* 4: /dev/mem etc System devices (one offs) */ + { no_open, no_close, sys_read, sys_write, sys_ioctl}, +}; + +bool validdev(uint16_t dev) +{ + /* This is a bit uglier than needed but the right hand side is + a constant this way */ + if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255) + return false; + else + return true; +} diff --git a/Kernel/platform-rc2014-sbc64/devinput.c b/Kernel/platform-rc2014-sbc64/devinput.c new file mode 100644 index 00000000..4e5eb54f --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/devinput.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include + +__sfr __at 0x01 js1; +__sfr __at 0x02 js2; + +static uint8_t js_data[2]= {255, 255}; + +uint8_t read_js(uint8_t *slot, uint8_t n, uint8_t r) +{ + uint8_t d = 0; + r &= 63; + if (r == js_data[n]) + return 0; + js_data[n] = r; + if (r & 1) + d = STICK_DIGITAL_U; + if (r & 2) + d |= STICK_DIGITAL_D; + if (r & 4) + d |= STICK_DIGITAL_L; + if (r & 8) + d |= STICK_DIGITAL_R; + if (r & 16) + d |= BUTTON(0); + if (r & 32) + d |= BUTTON(1); + *slot++ = STICK_DIGITAL | (n + 1); + *slot = d; + return 2; +} + +int platform_input_read(uint8_t *slot) +{ + if (read_js(slot, 0, js1)) + return 2; + if (read_js(slot, 1, js2)) + return 2; + return 0; +} + +void platform_input_wait(void) +{ + psleep(&js_data); +} + +void poll_input(void) +{ + if ((js1 & 63) != js_data[0] || + ((js2 & 63) != js_data[1])) + wakeup(&js_data); +} + +int platform_input_write(uint8_t flag) +{ + flag; + udata.u_error = EINVAL; + return -1; +} diff --git a/Kernel/platform-rc2014-sbc64/devinput.h b/Kernel/platform-rc2014-sbc64/devinput.h new file mode 100644 index 00000000..5e7922aa --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/devinput.h @@ -0,0 +1 @@ +extern void poll_input(void); diff --git a/Kernel/platform-rc2014-sbc64/devtty.c b/Kernel/platform-rc2014-sbc64/devtty.c new file mode 100644 index 00000000..d566c9a9 --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/devtty.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include +#include +#include + +__sfr __at 0xf8 cpld_status; +__sfr __at 0xf9 cpld_data; + +static char tbuf1[TTYSIZ]; +static char tbuf2[TTYSIZ]; +static char tbuf3[TTYSIZ]; + +static uint8_t sleeping; + +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, tbuf3, tbuf3, TTYSIZ, 0, TTYSIZ / 2}, +}; + +static tcflag_t uart0_mask[4] = { + _ISYS, + _OSYS, + _CSYS, + _LSYS +}; + +static tcflag_t uart1_mask[4] = { + _ISYS, + _OSYS, + CSIZE|CSTOPB|PARENB|PARODD|_CSYS, + _LSYS +}; + +static tcflag_t uart2_mask[4] = { + _ISYS, + /* FIXME: break */ + _OSYS, + /* FIXME CTS/RTS */ + CSIZE|CBAUD|CSTOPB|PARENB|PARODD|_CSYS, + _LSYS, +}; + +tcflag_t *termios_mask[NUM_DEV_TTY + 1] = { + NULL, + uart0_mask, + uart1_mask, + uart2_mask +}; + +uint8_t sio_r[] = { + 0x03, 0xC1, + 0x04, 0xC4, + 0x05, 0xEA +}; + +static void sio2_setup(uint8_t minor, uint8_t flags) +{ + struct termios *t = &ttydata[minor].termios; + uint8_t r; + + used(flags); + + /* Set bits per character */ + sio_r[1] = 0x01 | ((t->c_cflag & CSIZE) << 2); + r = 0xC4; + if (t->c_cflag & CSTOPB) + r |= 0x08; + if (t->c_cflag & PARENB) + r |= 0x01; + if (t->c_cflag & PARODD) + r |= 0x02; + sio_r[3] = r; + sio_r[5] = 0x8A | ((t->c_cflag & CSIZE) << 1); +} + +void tty_setup(uint8_t minor, uint8_t flags) +{ + if (minor == 1) + return; + sio2_setup(minor, flags); + sio2_otir(SIO0_BASE + 2 * (minor - 1)); + /* We need to do CTS/RTS support and baud setting on channel 2 + yet */ +} + +int tty_carrier(uint8_t minor) +{ + uint8_t c; + if (minor == 1) + return 1; + else if (minor == 2) { + SIOA_C = 0; + c = SIOA_C; + } else { + SIOB_C = 0; + c = SIOB_C; + } + if (c & 0x8) + return 1; + return 0; +} + +void tty_pollirq_sio(void) +{ + static uint8_t old_ca, old_cb; + uint8_t ca, cb; + uint8_t progress; + + /* Check for an interrupt */ + SIOA_C = 0; + if (!(SIOA_C & 2)) + return; + + /* FIXME: need to process error/event interrupts as we can get + spurious characters or lines on an unused SIO floating */ + do { + progress = 0; + SIOA_C = 0; // read register 0 + ca = SIOA_C; + /* Input pending */ + if ((ca & 1) && !fullq(&ttyinq[1])) { + progress = 1; + tty_inproc(1, SIOA_D); + } + /* Break */ + if (ca & 2) + SIOA_C = 2 << 5; + /* Output pending */ + if ((ca & 4) && (sleeping & 2)) { + tty_outproc(1); + sleeping &= ~2; + SIOA_C = 5 << 3; // reg 0 CMD 5 - reset transmit interrupt pending + } + /* Carrier changed */ + if ((ca ^ old_ca) & 8) { + if (ca & 8) + tty_carrier_raise(1); + else + tty_carrier_drop(1); + } + SIOB_C = 0; // read register 0 + cb = SIOB_C; + if ((cb & 1) && !fullq(&ttyinq[2])) { + tty_inproc(2, SIOB_D); + progress = 1; + } + if ((cb & 4) && (sleeping & 4)) { + tty_outproc(2); + sleeping &= ~4; + SIOB_C = 5 << 3; // reg 0 CMD 5 - reset transmit interrupt pending + } + if ((cb ^ old_cb) & 8) { + if (cb & 8) + tty_carrier_raise(2); + else + tty_carrier_drop(2); + } + } while(progress); +} + +void tty_poll_cpld(void) +{ + uint8_t ca; + + ca = cpld_status; + if (ca & 1) + tty_inproc(1, cpld_data); +} + +void tty_putc(uint8_t minor, unsigned char c) +{ + if (minor == 1) + cpld_bitbang(c); + if (minor == 2) + SIOA_D = c; + else if (minor == 2) + SIOB_D = c; +} + +/* We will need this for SIO once we implement flow control signals */ +void tty_sleeping(uint8_t minor) +{ + sleeping |= (1 << minor); +} + +/* Be careful here. We need to peek at RR but we must be sure nobody else + interrupts as we do this. Really we want to switch to irq driven tx ints + on this platform I think. Need to time it and see + + An asm common level tty driver might be a better idea + + Need to review this we should be ok as the IRQ handler always leaves + us pointing at RR0 */ +ttyready_t tty_writeready(uint8_t minor) +{ + irqflags_t irq; + uint8_t c; + + /* Bitbanged so trick the kernel into yielding when appropriate */ + if (minor == 1) + return need_reschedule() ? TTY_READY_SOON: TTY_READY_NOW; + irq = di(); + if (minor == 2) { + SIOA_C = 0; /* read register 0 */ + c = SIOA_C; + irqrestore(irq); + if (c & 0x04) /* THRE? */ + return TTY_READY_NOW; + return TTY_READY_SOON; + } else if (minor == 3) { + SIOB_C = 0; /* read register 0 */ + c = SIOB_C; + irqrestore(irq); + if (c & 0x04) /* THRE? */ + return TTY_READY_NOW; + return TTY_READY_SOON; + } + irqrestore(irq); + return TTY_READY_NOW; +} + +void tty_data_consumed(uint8_t minor) +{ + used(minor); +} + +/* kernel writes to system console -- never sleep! */ +void kputchar(char c) +{ + /* Always use the bitbang port - no need for write waits therefore */ + if (c == '\n') + tty_putc(TTYDEV - 512, '\r'); + tty_putc(TTYDEV - 512, c); +} diff --git a/Kernel/platform-rc2014-sbc64/devtty.h b/Kernel/platform-rc2014-sbc64/devtty.h new file mode 100644 index 00000000..22427d5b --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/devtty.h @@ -0,0 +1,10 @@ +#ifndef __DEVTTY_DOT_H__ +#define __DEVTTY_DOT_H__ + +void tty_putc(uint8_t minor, unsigned char c); +void tty_pollirq_sio(void); +void tty_poll_cpld(void); + +extern uint8_t ser_type; + +#endif diff --git a/Kernel/platform-rc2014-sbc64/discard.c b/Kernel/platform-rc2014-sbc64/discard.c new file mode 100644 index 00000000..e48753af --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/discard.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include +#include +#include "config.h" + +void map_init(void) +{ +} + +void pagemap_init(void) +{ + /* The high bits do nothing but it's a cheap way to avoid 0x00 */ + pagemap_add(0x12); + pagemap_add(0x10); +} + +/* + * 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_init(n--); +} + +void device_init(void) +{ + ds1302_init(); +#ifdef CONFIG_IDE + devide_init(); +#endif +} diff --git a/Kernel/platform-rc2014-sbc64/fuzix.lnk b/Kernel/platform-rc2014-sbc64/fuzix.lnk new file mode 100644 index 00000000..a4884a62 --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/fuzix.lnk @@ -0,0 +1,43 @@ +-mwxuy +-i fuzix.ihx +-b _CODE=0x1000 +-b _COMMONMEM=0xF400 +-l z80 +platform-rc2014-sbc64/crt0.rel +platform-rc2014-sbc64/commonmem.rel +platform-rc2014-sbc64/rc2014.rel +start.rel +version.rel +lowlevel-z80.rel +platform-rc2014-sbc64/tricks.rel +platform-rc2014-sbc64/main.rel +timer.rel +kdata.rel +platform-rc2014-sbc64/devices.rel +devio.rel +filesys.rel +process.rel +inode.rel +syscall_exec16.rel +syscall_fs.rel +syscall_proc.rel +syscall_fs2.rel +syscall_fs3.rel +syscall_other.rel +mm.rel +swap.rel +bankfixed.rel +tty.rel +devsys.rel +usermem.rel +usermem_std-z80.rel +platform-rc2014-sbc64/discard.rel +platform-rc2014-sbc64/devtty.rel +platform-rc2014-sbc64/mbr.rel +platform-rc2014-sbc64/blkdev.rel +platform-rc2014-sbc64/devide.rel +platform-rc2014-sbc64/devide_discard.rel +platform-rc2014-sbc64/ds1302.rel +platform-rc2014-sbc64/ds1302_discard.rel +platform-rc2014-sbc64/ds1302_rc2014.rel +-e diff --git a/Kernel/platform-rc2014-sbc64/kernel.def b/Kernel/platform-rc2014-sbc64/kernel.def new file mode 100644 index 00000000..f173bbbf --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/kernel.def @@ -0,0 +1,25 @@ +; FUZIX mnemonics for memory addresses etc + +U_DATA .equ 0xF400 ; (this is struct u_data from kernel.h) +U_DATA__TOTALSIZE .equ 0x200 ; 256+256 bytes. +Z80_TYPE .equ 0 ; CMOS +U_DATA_STASH .equ 0x7E00 + +Z80_MMU_HOOKS .equ 0 + +CONFIG_SWAP .equ 1 + +PROGBASE .equ 0x0000 +PROGLOAD .equ 0x0100 + +; Mnemonics for I/O ports etc + +CONSOLE_RATE .equ 115200 + +CPU_CLOCK_KHZ .equ 7372 + +; Z80 CTC ports +CTC_CH0 .equ 0x88 ; CTC channel 0 and interrupt vector +CTC_CH1 .equ 0x89 ; CTC channel 1 (periodic interrupts) +CTC_CH2 .equ 0x8A ; CTC channel 2 +CTC_CH3 .equ 0x8B ; CTC channel 3 diff --git a/Kernel/platform-rc2014-sbc64/loader.s b/Kernel/platform-rc2014-sbc64/loader.s new file mode 100644 index 00000000..1c0b45a3 --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/loader.s @@ -0,0 +1,72 @@ + + .area _BOOT(ABS) + + .org 0xB000 + +installer: + ld a,#0x03 + out (0x1f),a + ld hl,#begin + call outstr + ld hl,#data + ld de,#0x0000 + ld bc,#0x1000 + ldir + ld hl,#done + call outstr + rst 0 + +begin: + .asciz 'Configuring booter...' +done: + .ascii 'Done' + .db 13,10,0 + +outstr: + ld a,(hl) + or a + ret z + call outchar + inc hl + jr outstr + +; +; Based on the ROM code but slightly tighter +; - use ld a,#0 so 0 and 1 bits are same length +; - don't duplicate excess code in the hi/lo bit paths +; - use conditional calls to keep 0/1 timing identical +; +; FIXME: my math says it's still slightly off timing. +; +outchar: + push bc + ld c,a + ld b,#8 + call lobit + ld a,c +txbit: + rrca + call c, hibit + call nc, lobit + djnz txbit + pop bc +hibit: + push af + ld a,#0xff + out (0xf9),a + ld a,#7 +bitwait: + dec a + jp nz,bitwait + pop af + ret +lobit: + push af + ld a,#0 + out (0xf9),a + ld a,#7 + dec a + jp bitwait + +data: + .include "loader.inc" diff --git a/Kernel/platform-rc2014-sbc64/main.c b/Kernel/platform-rc2014-sbc64/main.c new file mode 100644 index 00000000..955e0df5 --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/main.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +extern unsigned char irqvector; +struct blkbuf *bufpool_end = bufpool + NBUFS; /* minimal for boot -- expanded after we're done with _DISCARD */ +uint16_t swap_dev = 0xFFFF; +uint16_t ramtop = 0x7E00; +uint8_t ctc = 0; + +void platform_discard(void) +{ + while (bufpool_end < (struct blkbuf *) ((uint16_t)&udata - 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++; + } +} + +static uint8_t idlect; + +void platform_idle(void) +{ + /* Check the clock. We try and reduce the impact of the clock on + latency by not doing it so often. 256 may be too small a divide + need t see what 1/10th sec looks like in poll loops */ + tty_poll_cpld(); + if (!idlect++) + sync_clock(); +} + +uint8_t platform_param(unsigned char *p) +{ + used(p); + return 0; +} + +void platform_interrupt(void) +{ + tty_pollirq_sio(); +} + +/* + * Logic for tickless system. If you have an RTC you can ignore this. + */ + +static uint8_t newticks = 0xFF; +static uint8_t oldticks; + +static uint8_t re_enter; + +/* + * Hardware specific logic to get the seconds. We really ought to enhance + * this to check minutes as well just in case something gets stuck for + * ages. + */ +static void sync_clock_read(void) +{ + uint8_t s; + oldticks = newticks; + ds1302_read_clock(&s, 1); + s = (s & 0x0F) + (((s & 0xF0) >> 4) * 10); + newticks = s; +} + +/* + * The OS core will invoke this routine when idle (via platform_idle) but + * also after a system call and in certain other spots to ensure the clock + * is roughly valid. It may be called from interrupts, without interrupts + * or even recursively so it must protect itself using the framework + * below. + * + * Having worked out how much time has passed in 1/10ths of a second it + * performs that may timer_interrupt events in order to advance the clock. + * The core kernel logic ensures that we won't do anything silly from a + * jump forward of many seconds. + * + * We also choose to poll the ttys here so the user has some chance of + * getting control back on a messed up process. + */ +void sync_clock(void) +{ + if (!ctc) { + irqflags_t irq = di(); + int16_t tmp; + if (!re_enter++) { + sync_clock_read(); + if (oldticks != 0xFF) { + tmp = newticks - oldticks; + if (tmp < 0) + tmp += 60; + tmp *= 10; + while(tmp--) { + timer_interrupt(); + } + } + } + re_enter--; + irqrestore(irq); + } +} + +/* + * This method is called if the kernel has changed the system clock. We + * don't work out how much work we need to do by using it as a reference + * so we don't care. + */ +void update_sync_clock(void) +{ +} diff --git a/Kernel/platform-rc2014-sbc64/platform_ide.h b/Kernel/platform-rc2014-sbc64/platform_ide.h new file mode 100644 index 00000000..8343e588 --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/platform_ide.h @@ -0,0 +1,6 @@ +#define ide_select(x) +#define ide_deselect() + +/*8bit, no altstatus/control */ +#define IDE_8BIT_ONLY +#define IDE_REG_CS1_BASE 0x10 diff --git a/Kernel/platform-rc2014-sbc64/rc2014.h b/Kernel/platform-rc2014-sbc64/rc2014.h new file mode 100644 index 00000000..f4baac3b --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/rc2014.h @@ -0,0 +1,18 @@ +#ifndef __RC2014_SIO_DOT_H__ +#define __RC2014_SIO_DOT_H__ + +#include "config.h" + +#define SIO0_IVT 8 + +/* Standard RC2014 */ +#define SIO0_BASE 0x80 +__sfr __at (SIO0_BASE + 0) SIOA_C; +__sfr __at (SIO0_BASE + 1) SIOA_D; +__sfr __at (SIO0_BASE + 2) SIOB_C; +__sfr __at (SIO0_BASE + 3) SIOB_D; + +extern void sio2_otir(uint8_t port) __z88dk_fastcall; +extern void cpld_bitbang(uint8_t c) __z88dk_fastcall; + +#endif diff --git a/Kernel/platform-rc2014-sbc64/rc2014.s b/Kernel/platform-rc2014-sbc64/rc2014.s new file mode 100644 index 00000000..cfb4386d --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/rc2014.s @@ -0,0 +1,294 @@ +; 2014-02-19 Sergey Kiselev +; RC2014 hardware specific code + + .module rc2014 + + ; exported symbols + .globl init_hardware + .globl _program_vectors + .globl map_kernel + .globl map_process + .globl map_process_always + .globl map_process_a + .globl map_kernel_di + .globl map_process_di + .globl map_process_always_di + .globl map_save_kernel + .globl map_restore + .globl map_for_swap + .globl map_buffers + .globl platform_interrupt_all + .globl _platform_reboot + .globl _platform_monitor + .globl _bufpool + .globl _int_disabled + .globl _cpld_bitbang + + ; imported symbols + .globl _ramsize + .globl _procmem + .globl outhl + .globl outnewline + .globl interrupt_handler + .globl unix_syscall_entry + .globl nmi_handler + + ; exported debugging tools + .globl outchar + + .include "kernel.def" + .include "../kernel.def" + +;========================================================================= +; Constants +;========================================================================= +CONSOLE_DIVISOR .equ (1843200 / (16 * CONSOLE_RATE)) +CONSOLE_DIVISOR_HIGH .equ (CONSOLE_DIVISOR >> 8) +CONSOLE_DIVISOR_LOW .equ (CONSOLE_DIVISOR & 0xFF) + +RTS_HIGH .EQU 0xE8 +RTS_LOW .EQU 0xEA + +; Base address of SIO/2 chip 0x80 +; For the Scott Baker SIO card adjust the order to match rc2014.h +SIOA_C .EQU 0x80 +SIOA_D .EQU SIOA_D+1 +SIOB_C .EQU SIOA_D+2 +SIOB_D .EQU SIOA_D+3 + +ACIA_C .EQU 0x80 +ACIA_D .EQU 0x81 +ACIA_RESET .EQU 0x03 +ACIA_RTS_HIGH_A .EQU 0xD6 ; rts high, xmit interrupt disabled +ACIA_RTS_LOW_A .EQU 0x96 ; rts low, xmit interrupt disabled +;ACIA_RTS_LOW_A .EQU 0xB6 ; rts low, xmit interrupt enabled + +;========================================================================= +; Buffers +;========================================================================= + .area _BUFFERS + .globl kernel_endmark + +_bufpool: + .ds (BUFSIZE * 4) ; adjust NBUFS in config.h in line with this +; +; So we can check for overflow +; +kernel_endmark: + +;========================================================================= +; Initialization code +;========================================================================= + .area _DISCARD +init_hardware: + ld hl,#128 + ld (_ramsize), hl + ld hl,#64 + ld (_procmem), hl + + ; FIXME: autodetect SIO + + ld hl,#sio_setup + ld bc,#0xA00 + SIOA_C ; 10 bytes to SIOA_C + otir + ld hl,#sio_setup + ld bc,#0x0A00 + SIOB_C ; and to SIOB_C + otir + +serial_up: + ; --------------------------------------------------------------------- + ; Initialize CTC + ; + ; Need to do autodetect on this + ; + ; We must initialize all channels of the CTC. The documentation + ; states that the initial CTC state is undefined and we don't want + ; random interrupt surprises + ; --------------------------------------------------------------------- + + ld a,#0x57 ; counter mode, disable interrupts + out (CTC_CH0),a ; set CH0 mode + ld a,#0 ; time constant = 256 + out (CTC_CH0),a ; set CH0 time constant + ld a,#0x57 ; counter mode, FIXME C7 enable interrupts + out (CTC_CH1),a ; set CH1 mode + ld a,#180 ; time constant = 180 + out (CTC_CH1),a ; set CH1 time constant + ld a,#0x57 ; counter mode, disable interrupts + out (CTC_CH2),a ; set CH2 mode + ld a,#0x57 ; counter mode, disable interrupts + out (CTC_CH3),a ; set CH3 mode + + ; Done CTC Stuff + ; --------------------------------------------------------------------- + + im 1 ; set Z80 CPU interrupt mode 1 + ret + +sio_setup: + .byte 0x00 + .byte 0x18 ; Reset + .byte 0x04 + .byte 0xC4 + .byte 0x01 + .byte 0x18 + .byte 0x03 + .byte 0xE1 + .byte 0x05 + .byte RTS_LOW + + .area _COMMONMEM + +_platform_monitor: +_platform_reboot: + ld a,#0x03 + out (0x1f),a + rst 0 + +_int_disabled: + .db 1 +pagereg: + .db 0 +pagesave: + .db 0 + +platform_interrupt_all: + ret + +; install interrupt vectors +_program_vectors: + di + pop de ; temporarily store return address + pop hl ; function argument -- base page number + push hl ; put stack back as it was + push de + + ; At this point the common block has already been copied + call map_process + + ; write zeroes across all vectors + ld hl,#0 + ld de,#1 + ld bc,#0x007f ; program first 0x80 bytes only + ld (hl),#0x00 + ldir + + ; now install the interrupt vector at 0x0038 + ld a,#0xC3 ; JP instruction + ld (0x0038),a + ld hl,#interrupt_handler + ld (0x0039),hl + + ; set restart vector for UZI system calls + ld (0x0030),a ; rst 30h is unix function call vector + ld hl,#unix_syscall_entry + ld (0x0031),hl + + ; We can't add a NULL handler - it's the restart for power off + + ld (0x0066),a ; Set vector for NMI + ld hl,#nmi_handler + ld (0x0067),hl + + jr map_kernel + +;========================================================================= +; Memory management +;========================================================================= + +map_process: +map_process_di: + ld a,h + or l ; HL == 0? + jr z,map_kernel ; HL == 0 - map the kernel + ld a,(hl) +map_for_swap: +map_process_a: + ld (pagereg),a + out (0x1f),a + ret + +map_process_always: +map_process_always_di: + push af + ld a,(U_DATA__U_PAGE) +map_pop_a: + ld (pagereg),a + out (0x1f),a + pop af + ret + +map_buffers: +map_kernel: +map_kernel_di: + push af + ld a,#3 + jr map_pop_a + +map_restore: + push af + ld a,(pagesave) + jr map_pop_a + +map_save_kernel: + push af + ld a,(pagereg) + ld (pagesave),a + ld a,#3 + jr map_pop_a + +; +; A little SIO helper +; + .globl _sio_r + .globl _sio2_otir + +_sio2_otir: + ld b,#0x06 + ld c,l + ld hl,#_sio_r + otir + ret + +; C entry point +_cpld_bitbang: + ld a,l +; Debug entry point +; +; Based on the ROM code but slightly tighter +; - use ld a,#0 so 0 and 1 bits are same length +; - don't duplicate excess code in the hi/lo bit paths +; - use conditional calls to keep 0/1 timing identical +; +; FIXME: my math says it's still slightly off timing. +; +outchar: + push bc + ld c,a + ld b,#8 + call lobit + ld a,c +txbit: + rrca + call c, hibit + call nc, lobit + djnz txbit + pop bc +hibit: + push af + ld a,#0xff + out (0xf9),a + ld a,#7 +bitwait: + dec a + jp nz,bitwait + pop af + ret +lobit: + push af + ld a,#0 + out (0xf9),a + ld a,#7 + dec a + jp bitwait + diff --git a/Kernel/platform-rc2014-sbc64/target.mk b/Kernel/platform-rc2014-sbc64/target.mk new file mode 100644 index 00000000..3bffcde0 --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/target.mk @@ -0,0 +1 @@ +export CPU = z80 diff --git a/Kernel/platform-rc2014-sbc64/tricks.s b/Kernel/platform-rc2014-sbc64/tricks.s new file mode 100644 index 00000000..015ba064 --- /dev/null +++ b/Kernel/platform-rc2014-sbc64/tricks.s @@ -0,0 +1,5 @@ + .include "kernel.def" + .include "../kernel.def" + + .include "../lib/z80fixedbank.s" +