--- /dev/null
+
+CSRCS = devtty.c devfd.c
+CSRCS += devices.c main.c
+
+ASRCS = crt0.s cromemco.s
+ASRCS += tricks.s commonmem.s
+
+AOBJS = $(ASRCS:.s=.rel)
+COBJS = $(CSRCS:.c=.rel)
+
+OBJS = $(AOBJS) $(COBJS)
+
+JUNK = *.rel *.lst *.asm *.sym *.rst
+
+all: $(OBJS)
+
+$(AOBJS): %.rel: %.s
+ $(CROSS_AS) $(ASOPTS) $<
+
+$(COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+clean:
+ rm -f $(OBJS) $(JUNK) core *~
+
+image:
+ sdasz80 -o cloader-16fdc.s
+ sdldz80 -m -i cloader-16fdc.rel
+ makebin -s 256 cloader-16fdc.ihx > cloader-16fdc.tmp
+ dd if=cloader-16fdc.tmp of=cloader-16fdc.bin bs=1 skip=128
+
--- /dev/null
+FUZIX on a Cromenco (no hard disk support right now - need docs for that and
+suitable emulation!)
+
+We expect
+
+0 tuart / 4 FDC
+4 fdc aux
+5-9 times
+32 tuart / parallel 36
+48 fdc
+64 mmu
+80 tuart / parallel 84
+255 front panel
+
+
+
+
+The MMU settings we use are
+
+1 << n That bank 0-6
+
+0x80 Turn on comon writing (write to all banks)
+
+
+UARTs: TMS 5501 x 2 per board. These can do Z80 IM2 where the vector is
+determined by A7-A5 of the IRQ (used as D7-D5 of the vector)
+
+R 0 Status
+
+7: set if tx buffer empty
+6: set if rx data available
+5: irq pending
+4: start bit detected
+3: full bit detected
+2: serial receive line state
+1: overrun (clear on read)
+0: frame error (clear on valid rx)
+
+W 0
+7: 1 for one stop 0 for 2
+0-6 set baud rate (all 0 off)
+0: 110
+1: 150
+2: 300
+3: 1200
+4: 2400
+5: 4800
+6: 9600
+(but see later)
+
+R 1 Data received
+
+W 1 Load tx
+
+W 2 cmd
+
+0: reset (self clears)
+1: break send
+2: IRQ on parallel msb
+3: May respond to an IRQ by gating an RST
+ Low prevents gating instructions
+4: Baud rate x8
+5: Test
+
+
+R 3: Interrupt address (gives service priority order for events)
+ Each read clears a priority
+
+ C7 timer 1
+ CF timer 2
+ D7 !sens
+ DF timer 3
+ E7 rx data
+ EF tx data
+ F7 timer 4
+ FF timer 5 (or none)
+
+W 3: Interrupt mask
+
+7: timer 5
+6: timer 4
+5: tx buffer empty
+4: rx data ready
+3: timer 3
+2: !sens
+1: timer 2
+0: timer 1
+
+R 4:
+ Read parallel input data captured after strobe
+W 4:
+ Write parallel
+
+ (Parallel will halt the CPU for the other end to respond .. !)
+
+W 5: timer 1 (decremented ever 64uS - longest interval 16.32ms)
+
+W 6: timer 2 (ditto)
+
+W 7: timer 3 (ditto)
+
+W 8: timer 4 (ditto)
+
+W 9: timer 5 (ditto)
+
+
+
+
+The 4FDC has a uart at 0 (parallel out is used for disk control)
+Disk (FD1771). 16FDC is same ports but a 1793
+
+Parallel in is wired so that (16FDC)
+ D7 = DRQ/RTC (Jumper sets it to 512ms clock used with 5501 int)
+ D6 = Seek in progress
+ D3 = Switch 5
+ D2 = Switch 6
+ D1 = Switch 7
+ D0 = Switch 8
+
+4FDC has only D7 (DRQ) optional - default off, D6 seek in progress
+
+64FDC
+ D7 = DRQ/RTC
+ D6 = 1
+ D5 = ?
+ D4-D0 Switch 5/1-4
+
+
+16FDC out
+ D6 clear to eject (not always supported)
+ D5 set to disconnect drive select (shared disk configurations)
+ D4 clear for fast step mode (8" drives), set once seek complete sent
+ D3 clear to force drive to track 0 (restore)
+ D2 clear activates control OUT (test pin)
+ D1 side select (0 for second side of 2 sided media)
+
+4FDC out
+ D6: eject left
+ D5: eject right
+ D4: clear for fast seek
+ D3: cler for restore
+ D2: clear activates daisy chain
+
+64FDC out
+ D5: drive select override (as 16FDC)
+ D2: control out (as 16FDC)
+ D1: side select (as 16FDC)
+
+(hex)
+R 30 status
+W 30 command
+R 31 track
+W 31 track
+R 32 sector
+W 32 sector
+R 33 data
+W 33 data
+R 34 flags
+ D7: !DRQ
+ D6: !BOOT (0 SW3 set to boot)
+ D5 HEADLOAD (Drive sect if high on 64FDC)
+ D4 Zero mean switch inhibit init is on (16FDC)
+ D3 motor is on (16FDC) on = 1
+ D2 motor timeout (16FDC) timedout = 1
+ D1 autowait timeout if 1
+ D0: End of Job
+W 34 control
+ D7: autowait - 34H will hold CPU in wait until DRQ or EOJ or RESET
+ D6: double density (4FDC not ??)
+ D5: motor on
+ D4: 1 = 8" (fast step) 0 = 5.25 (slow step)
+ D3-D0 - drive select (only one at a time)
+
+W 40 Disables boot rom as a side effect
+
+
+W 40 bank select
+
+
--- /dev/null
+ .area ASEG(ABS)
+ .org 0x80
+;
+; Loader for Fuzix on a Cromenco 16FDC
+;
+; The kernel is written in blocks 1+ SS/SD with no magic
+; involved. We have 128 bytes, but fortunately a ROM helper
+;
+;
+; Memory map
+; 0x0000-0x002E Stack
+; 0x0060-0x007F Used by the boot ROM
+; 0x0080-0x00FF This boot block
+; 0x0100-0xBFFF Free RAM
+; 0xC000-0xCFFF Boot ROM (until we out to 0x40)
+; 0xD000-0xFFFF May be RAM but may also be ROM shadows
+;
+
+set_side .equ 0xC003
+set_track .equ 0xC006
+set_sector .equ 0xC009
+set_buffer .equ 0xC00C
+disk_restore .equ 0xC00F
+disk_seek .equ 0xC012
+disk_read .equ 0xC015
+disk_write .equ 0xC018
+setup_uart .equ 0xC01B
+char_input .equ 0xC01E
+char_ready .equ 0xC021
+line_input .equ 0xC024
+char_output .equ 0xC027
+newline_output .equ 0xC02A
+
+ .globl start
+ .globl endc
+ .globl end
+ .globl msg
+; Carry set, drive in A
+start:
+ push af
+ ld hl,#boot
+ call tout
+ ld hl,#0xFC
+ pop af
+ ld (hl),a ; boot drive
+ inc hl
+ ld a,#16
+ ld (hl),a ; controller type (16FDC)
+ inc hl
+ ld bc,(0x7E) ; features and feature save for boot disk
+ ld (hl),c
+ inc hl
+ ld (hl),b
+ inc hl ; now 0x100
+ xor a
+ ld b,a
+ call set_side
+ ld a,(0x6E) ; sectors per track
+ inc a
+ ld c,a
+ ld a,#1 ; sector 2
+ ld de,#128
+trackloop:
+ push af
+ ld a,b
+ call set_track
+ pop af
+secloop:
+ inc a
+ call set_sector
+ call set_buffer
+ ex af,af' ; shorter than pushing them
+ exx
+ call disk_seek
+ call disk_read
+ jr c, crap
+ exx
+ add hl,de
+ ld a,h
+ cp #0xC0
+ jr z,go
+ ex af,af'
+ cp c
+ jr nz, secloop
+ inc b
+ xor a
+ jr trackloop
+go:
+ ld a,(0x100)
+ cp #0xC3
+ jr z, 0x100
+crap:
+ ld hl, #fail
+ call tout
+ jp 0xC000
+
+tout:
+ ld a,(hl)
+ inc hl
+ or a
+ ret z
+ push hl
+ call char_output
+ pop hl
+ jr tout
+
+msg:
+boot: .asciz 'Boot'
+fail: .ascii ' failed'
+ .byte 13,10,0
+
+endc:
+ .org 0xF8
+
+ .ascii 'FZSSSD'
+end:
--- /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
+/* Fixed banking */
+#define CONFIG_BANK_FIXED
+/* 7 64K banks, 1 is kernel */
+#define MAX_MAPS 6
+#define MAP_SIZE 0xF000U
+
+/* Banks as reported to user space */
+#define CONFIG_BANKS 1
+
+#define TICKSPERSEC 10 /* Ticks per second */
+#define PROGBASE 0x0000 /* also data base */
+#define PROGLOAD 0x0100 /* also data base */
+#define PROGTOP 0xED00 /* Top of program, base of U_DATA copy */
+#define PROC_SIZE 60 /* Memory needed per process */
+
+#define BOOT_TTY (512 + 1)/* Set this to default device for stdio, stderr */
+ /* In this case, the default is the first TTY device */
+
+/* We need a tidier way to do this from the loader */
+#define CMDLINE NULL /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 3
+
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+#define NBUFS 8 /* Number of block buffers */
+#define NMOUNTS 4 /* Number of mounts at a time */
+
+#define platform_discard()
--- /dev/null
+;
+; Cromemco hardware support
+;
+
+ .module cromemco
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl _program_vectors
+ .globl platform_interrupt_all
+
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_always
+ .globl map_process_a
+ .globl map_save
+ .globl map_restore
+
+ .globl _platform_reboot
+
+ ; exported debugging tools
+ .globl _platform_monitor
+ .globl outchar
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+
+ .globl unix_syscall_entry
+ .globl null_handler
+ .globl nmi_handler
+ .globl interrupt_handler
+ .globl _doexit
+ .globl _inint
+ .globl kstack_top
+ .globl _panic
+ .globl _need_resched
+ .globl _ssig
+
+ .globl outcharhex
+ .globl outhl, outde, outbc
+ .globl outnewline
+ .globl outstring
+ .globl outstringhex
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0xF000 upwards)
+; -----------------------------------------------------------------------------
+ .area _COMMONMEM
+
+_platform_reboot:
+_platform_monitor:
+ jr _platform_monitor
+platform_interrupt_all:
+ ret
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (below 0xF000, only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+ .area _CODE
+
+init_early:
+ ld a,#0x81 ; Every memory is in bank 7
+ out (0x40),a ; MMU set
+ ld hl,#0xF000 ; Copy 4K to itself loading
+ ld d,h ; into into the other banks
+ ld e,l
+ ld b,#0x10
+ ld c,l
+ ldir
+ ld a,#0x01 ; bank to the kernel bank
+ out (0x40),a
+ ret
+
+init_hardware:
+ ; set system RAM size
+ ld hl, #448
+ ld (_ramsize), hl
+ ld hl, #(448-64) ; 64K for kernel
+ ld (_procmem), hl
+
+ ld a, #156 ; ticks for 10Hz (9984uS per tick)
+ out (8), a ; 10Hz timer on
+
+ ; 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
+
+; ld a, #0xfe ; Use FEFF (currently free)
+; ld i, a
+; im 2 ; set CPU interrupt mode
+ im 1 ; really should use a page and im2?
+ ret
+
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+ .area _COMMONMEM
+
+
+_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
+
+ ; 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 0x38
+ ld hl, #interrupt_handler
+ ld (0x39), hl
+
+ ld a,#0xC3 ; JP
+ ; set restart vector for UZI system calls
+ ld (0x0030), a ; (rst 30h is unix function call vector)
+ ld (0x0038), a ; (rst 38h)
+ ld hl, #unix_syscall_entry
+ ld (0x0031), hl
+
+ ; now install the interrupt vector at 0x38
+ ld hl, #interrupt_handler
+ ld (0x39), hl
+
+ ; Set vector for jump to NULL
+ ld (0x0000), a
+ ld hl, #null_handler ; to Our Trap Handler
+ ld (0x0001), hl
+
+ ld (0x0066), a ; Set vector for NMI
+ ld hl, #nmi_handler
+ ld (0x0067), hl
+
+ ; falls through
+
+ ; put the paging back as it was -- we're in kernel mode so this is predictable
+map_kernel:
+ push af
+ ld a,#1
+ out (0x40), a
+ ld (map_page), a ; map_page lives in kernel so be careful
+ pop af ; our common is r/o common so writes won't
+ ret ; cross a bank
+map_process:
+ ld a, h
+ or l
+ jr z, map_kernel
+ ld a, (hl)
+map_process_a:
+ ld (map_page),a ; save before we map out kernel
+ out (0x40), a
+ ret
+map_process_always:
+ push af
+ ld a, (U_DATA__U_PAGE)
+ ld (map_page),a ; save before we map out kernel
+ out (0x40), a
+ pop af
+ ret
+map_save: ; this is a bit naughty, but we know
+ push af ; map_save will always be followed by
+ ld a, #1 ; map_kernel so we do the map_kernel
+ out (0x40), a ; a shade early.
+ ld a, (map_page) ; then we can get the vars
+ ld (map_store), a
+ pop af
+ ret
+map_restore: ; called in kernel map
+ push af
+ ld a, (map_store)
+ ld (map_page),a
+ out (0x40), a
+ pop af
+ ret
+map_store:
+ .db 0
+map_page:
+ .db 0
+
+; outchar: Wait for UART TX idle, then print the char in A
+; destroys: AF
+outchar:
+ push af
+outcharl:
+ in a, (0x00)
+ bit 7,a
+ jr z, outcharl
+ pop af
+ out (0x01), a
+ ret
+
+;
+; Low level pieces for floppy driver
+;
+ .globl _fd_reset
+ .globl _fd_operation
+ .globl _fd_map
+ .globl _fd_cmd
+
+_fd_reset:
+ ret
+_fd_operation:
+ ret
+_fd_map:
+ .byte 0
+_fd_cmd:
+ .byte 0, 0, 0, 0, 0, 0, 0
+
--- /dev/null
+; 2013-12-18 William R Sowerbutts
+
+ .module crt0
+
+ ; Ordering of segments for the linker.
+ ; WRS: Note we list all our segments here, even though
+ ; we don't use them all, because their ordering is set
+ ; when they are first seen.
+ .area _CODE
+ .area _CODE2
+ .area _CONST
+ .area _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 _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
+
+ ; Configure memory map
+ call init_early
+
+ ; 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
+
+ ; Hardware setup
+ call init_hardware
+
+ ; Call the C main routine
+ call _fuzix_main
+
+ ; main shouldn't return, but if it does...
+ di
+stop: halt
+ jr stop
+
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+
+#define MAX_DRIVE 4
+
+#define OPDIR_NONE 0
+#define OPDIR_READ 1
+#define OPDIR_WRITE 2
+
+#define FD_READ 0x80 /* 2797 needs 0x88, 1797 needs 0x80 */
+#define FD_WRITE 0xA0 /* Likewise A8 v A0 */
+
+/* Extern as they live in common */
+extern uint8_t fd_map;
+extern uint8_t fd_selected;
+extern uint8_t fd_cmd[7];
+
+static uint8_t fd_tab[MAX_DRIVE];
+
+__sfr __at 0x04 fd_aux;
+__sfr __at 0x30 fd_stcmd;
+__sfr __at 0x31 fd_track;
+__sfr __at 0x32 fd_sector;
+__sfr __at 0x33 fd_data;
+__sfr __at 0x34 fd_ctrlflag;
+
+/* 4 large disks (0 5" 1 8" 255 none */
+
+uint8_t drivetype[4] = { 1, 1, 1, 1 };
+
+/*
+ * We only support normal block I/O for the moment.
+ */
+
+static uint8_t selmap[4] = { 0x01, 0x02, 0x04, 0x08 };
+
+static const uint8_t skewtab[2][18] = {
+ /* Matches CDOS 5" skew for DSDD */
+ { 1, 5, 9, 3, 7, 2, 6, 10, 4, 9 },
+ /* No idea what CDOS uses for 8" DSDD right now */
+ { 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16 },
+};
+
+static uint8_t live[4];
+
+/*
+ * A note on disk formats and the Cromenco (and also emulator limits)
+ *
+ * Classic 8" SS/SD 26 sectors, 128 bytes/sector, 77 track
+ * software skewed
+ * Classic 5" SS/SD 18 sectors, 40 tracks otherwise as before
+ *
+ * Double sided use the same formats for SD but with two sides
+ *
+ * When we go to DD 8" goes to 16 512 byte sectors/track and
+ * 5" goes to 10 512 byte sectors/track
+ *
+ * The controller can do a lot more but the emulators don't always
+ * cope. In particular a 'correct' bootable disk is supposed to be
+ * SD on track 0 side 0 and can be DS/DD on the rest.
+ *
+ * Likewise the hardware can write PC format media in theory and
+ * do format level skewing at least for 5.25" disks
+ *
+ * Just to get us going - only do DSDD.
+ */
+
+static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ int tries;
+ uint8_t err = 0;
+ uint8_t drive = minor & 3;
+ uint8_t *driveptr = fd_tab + drive;
+ uint8_t large = !(minor & 0x10);
+ const uint8_t *skew = skewtab[large]; /* skew table */
+
+ if(rawflag == 2)
+ goto bad2;
+
+ fd_map = rawflag;
+ if (rawflag && d_blkoff(BLKSHIFT))
+ return -1;
+
+ /* Command to go to the controller after any seek is done */
+ fd_cmd[0] = is_read ? FD_READ : FD_WRITE;
+ if (large) {
+ /* Track */
+ fd_cmd[1] = udata.u_block / 16;
+ /* Sector */
+ fd_cmd[2] = skew[udata.u_block % 16]; /* 1..n skewed */
+ /* control */
+ fd_cmd[6] = 0xF0;
+ } else {
+ fd_cmd[1] = udata.u_block / 10;
+ fd_cmd[2] = skew[udata.u_block % 10];
+ fd_cmd[6] = 0xA0;
+ }
+ fd_cmd[6] |= (1 << drive);
+ /* Directon of xfer */
+ fd_cmd[3] = is_read ? OPDIR_READ: OPDIR_WRITE;
+ /* Buffer */
+ fd_cmd[4] = ((uint16_t)udata.u_dptr) & 0xFF;
+ fd_cmd[5] = ((uint16_t)udata.u_dptr) >> 8;
+
+ /* FIXME: Sides. Really 32 sectors/track 16 each side */
+
+ while (udata.u_done < udata.u_nblock) {
+ if (large) {
+ fd_cmd[1] = udata.u_block / 16;
+ fd_cmd[2] = (udata.u_block % 16) + 1;
+ } else {
+ fd_cmd[1] = udata.u_block / 10;
+ fd_cmd[2] = (udata.u_block % 10) + 1;
+ }
+ for (tries = 0; tries < 4 ; tries++) {
+ err = fd_operation(driveptr);
+ if (err == 0)
+ break;
+ if (tries > 1)
+ fd_reset(driveptr);
+ }
+ if (tries == 4)
+ goto bad;
+ udata.u_block++;
+ udata.u_done++;
+ }
+ return udata.u_done << 9;
+bad:
+ kprintf("fd%d: error %x\n", minor, err);
+bad2:
+ udata.u_error = EIO;
+ return -1;
+}
+
+/* We encode the minor has
+ 000[8/5][sd/dd][ss/ds][disk.2] */
+
+int fd_open(uint8_t minor, uint16_t flag)
+{
+ uint8_t *dp = live + (minor & 3);
+
+ flag;
+ if ((minor & 0xE0) || drivetype[minor] == 255) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ /* Ensure we can't open the same physical disk in two modes at once */
+ if (*dp != 0xFF) {
+ udata.u_error = EBUSY;
+ return -1;
+ }
+ *dp = minor;
+ return 0;
+}
+
+int fd_close(uint8_t minor)
+{
+ live[minor & 3] = 0xFF;
+ return 0;
+}
+
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return fd_transfer(minor, true, rawflag);
+}
+
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;rawflag;minor;
+ return fd_transfer(minor, false, rawflag);
+}
+
+
\ No newline at end of file
--- /dev/null
+extern int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+extern int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+extern int fd_close(uint8_t minor);
+extern int fd_open(uint8_t minor, uint16_t flag);
+
+extern void fd_reset(uint8_t *ptr);
+extern int fd_operation(uint8_t *ptr);
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <tty.h>
+#include <devfd.h>
+#include <devsys.h>
+#include <devtty.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+// minor open close read write ioctl
+// -----------------------------------------------------------------
+ /* 0: /dev/fd Floppy disc block devices */
+ /* TODO: add fd_ioctl and eject etc */
+ { fd_open, fd_close, fd_read, fd_write, no_ioctl },
+ /* 1: /dev/hd Hard disc block devices (absent) */
+ { nxio_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: none for now */
+ { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 4: /dev/mem etc System devices (one offs) */
+ { no_open, sys_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;
+}
+
+void device_init(void)
+{
+}
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <devtty.h>
+
+char tbuf1[TTYSIZ];
+char tbuf2[TTYSIZ];
+char tbuf3[TTYSIZ];
+
+struct s_queue ttyinq[NUM_DEV_TTY+1] = { /* ttyinq[0] is never used */
+ { NULL, NULL, NULL, 0, 0, 0 },
+ { tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ/2 },
+ { tbuf2, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ/2 },
+ { tbuf3, tbuf3, tbuf3, TTYSIZ, 0, TTYSIZ/2 },
+};
+
+static uint8_t ttybase[NUM_DEV_TTY+1] = {
+ 0, 0, 32, 80
+};
+
+static uint8_t ttypoll;
+
+/* Write to system console */
+void kputchar(char c)
+{
+ /* handle CRLF */
+ if(c=='\n')
+ tty_putc(1, '\r');
+ tty_putc(1, c);
+}
+
+char tty_writeready(uint8_t minor)
+{
+ uint8_t r = ttybase[minor];
+ r = in(r);
+ if (r & 0x80)
+ return 1;
+ return 0;
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ uint8_t r = ttybase[minor];
+ out(r + 1, c);
+}
+
+void tty_sleeping(uint8_t minor)
+{
+ ttypoll |= 1 << minor;
+}
+
+__sfr __at 0x08 timer4;
+
+void tty_irq(uint8_t minor)
+{
+ uint8_t r = ttybase[minor];
+ uint8_t op;
+ while((op = in(r + 3)) != 0xFF) {
+ switch(op) {
+ case 0xE7:
+ /* should check in(r) for error bits */
+ tty_inproc(minor, in(r + 1 ));
+ case 0xEF:
+ ttypoll &= ~(1 << minor);
+ wakeup(&ttydata[minor]);
+ case 0xF7:
+ if (minor == 1) {
+ timer_interrupt();
+ timer4 = 156;
+ }
+ }
+ }
+}
+
+static uint8_t baudbits[] = {
+ 1,
+ 1,
+ 1,
+ 1, /* 110 */
+ 1,
+ 2, /* 150 */
+ 4, /* 300 */
+ 4,
+ 8, /* 1200 */
+ 16, /* 2400 */
+ 32, /* 4800 */
+ 64, /* 9600 */
+ /* x8 modes */
+ 8, /* 19200 */
+ 16, /* 38400 */
+ 16,
+ 16
+};
+
+void tty_setup(uint8_t minor)
+{
+ struct termios *t = &ttydata[minor].termios;
+ uint8_t baud = t->c_cflag & CBAUD;
+ uint8_t r = ttybase[minor];
+ out(r, baudbits[baud] | (t->c_cflag & CSTOPB) ? 0 : 0x80);
+ /* Assume we keep to IM1 */
+ if (baud <= B9600)
+ out(r + 2, 0x8);
+ else
+ out(r + 2, 0x18);
+ t->c_cflag |= CS8;
+ t->c_cflag &= ~PARENB;
+ /* Ok we could do sw parity .. */
+}
+
+/* For the moment */
+int tty_carrier(uint8_t minor)
+{
+ used(minor);
+ return 1;
+}
--- /dev/null
+extern void tty_irq(uint8_t minor);
--- /dev/null
+-mwxuy
+-i fuzix.ihx
+-b _CODE=0x0100
+-b _COMMONMEM=0xF000
+-b _DISCARD=0xE000
+-l z80
+platform-cromemco/crt0.rel
+platform-cromemco/commonmem.rel
+platform-cromemco/cromemco.rel
+platform-cromemco/main.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+usermem_std-z80.rel
+platform-cromemco/tricks.rel
+timer.rel
+kdata.rel
+usermem.rel
+platform-cromemco/devfd.rel
+platform-cromemco/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
+devsys.rel
+platform-cromemco/devtty.rel
+-e
--- /dev/null
+#include <kernel.h>
+#include <devtty.h>
+
+uaddr_t ramtop = PROGTOP;
+
+void pagemap_init(void)
+{
+ int i;
+ for (i = 1; i < 7; i++)
+ pagemap_add(1 << i);
+}
+
+void platform_idle(void)
+{
+ /* halt ?? */
+}
+
+void platform_interrupt(void)
+{
+ tty_irq(1);
+ tty_irq(2);
+ tty_irq(3);
+}
+
+/* Nothing to do for the map of init */
+void map_init(void)
+{
+}
+
+uint8_t platform_param(char *p)
+{
+ used(p);
+ return 0;
+}
--- /dev/null
+export CPU = z80
--- /dev/null
+
+ .include "../kernel.def"
+ .include "kernel.def"
+
+ .include "../lib/z80fixedbank.s"