From: Alan Cox Date: Sat, 21 Jul 2018 12:03:36 +0000 (+0100) Subject: nascom: Initial work in progress code (not complete) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=1c341fcf873af715275b2b4da655f5502b272070;p=FUZIX.git nascom: Initial work in progress code (not complete) These are the initial pieces to support the Lucas/Nascom 80-bus systems. There is a lot to do before this is remotely useful. --- diff --git a/Kernel/platform-nascom/Makefile b/Kernel/platform-nascom/Makefile new file mode 100644 index 00000000..e292a329 --- /dev/null +++ b/Kernel/platform-nascom/Makefile @@ -0,0 +1,47 @@ + +CSRCS = devnascom.c devgm8x9.c devgm833.c +CSRCS += devices.c main.c devinput.c +DISCARD_CSRCS = discard.c + +ASRCS = nascom.s nascom-pagemode.s nascom-vt.s crt0.s +ASRCS += tricks.s commonmem.s gm8x9.s gm849a_sasi.s gm833.s + +DSRCS = ../dev/devscsi.c ../dev/blkdev.c ../dev/mbr.c +DISCARD_DSRCS = ../dev/devscsi_discard.c + +COBJS = $(CSRCS:.c=.rel) +AOBJS = $(ASRCS:.s=.rel) +DISCARD_COBJS = $(DISCARD_CSRCS:.c=.rel) +DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS)) +DISCARD_DOBJS = $(patsubst ../dev/%.c,%.rel, $(DISCARD_DSRCS)) +OBJS = $(COBJS) $(AOBJS) $(DISCARD_COBJS) $(DOBJS) $(DISCARD_DOBJS) + +CROSS_CCOPTS += -I../dev/ + +JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:.s=.lst) $(ASRCS:.s=.sym) $(CSRCS:.c=.rst) $(ASRCS:.s=.rst) + +all: $(OBJS) + +$(COBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +$(AOBJS): %.rel: %.s + $(CROSS_AS) $(ASOPTS) $< + +$(DOBJS): %.rel: ../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +$(DISCARD_DOBJS): %.rel: ../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(DISCARD_COBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +clean: + rm -f $(OBJS) $(JUNK) core *~ *.asm *.rst *.lst *.sym + +image: + sdasz80 -o nasboot.s + sdldz80 -m -i nasboot.rel + makebin -s 1024 nasboot.ihx | dd of=nasboot.bin bs=512 skip=1 + cp ../fuzix.bin fuzix.com diff --git a/Kernel/platform-nascom/PortMap b/Kernel/platform-nascom/PortMap new file mode 100644 index 00000000..5500706d --- /dev/null +++ b/Kernel/platform-nascom/PortMap @@ -0,0 +1,180 @@ + Nascom Gemini MAP80 + +00 Keyboard/system +01 6402 RX/TX +02 6402 Status +03 Unused (mainboard) +04 PIO0A data +05 PIO0B data +06 PIO0A ctrl +07 PIO0B ctrl + +08 IO 1 CTC +09 IO 1 CTC +08 IO 1 CTC +0B IO 1 CTC +0C IO 2 CTC +0D IO 2 CTC +0E IO 2 CTC +0F IO 2 CTC + +10 IO CTC +11 IO 1 6402 data IO CTC +12 IO 1 6402 status IO CTC +13 IO CTC + +14 IO 1 PIO1 (1) IO PIO1 +15 IO 1 PIO1 IO PIO1 +16 IO 1 PIO1 IO PIO1 +17 IO 1 PIO1 IO PIO1 + +18 IO 1 PIO2 IO PIO2 +19 IO 1 PIO2 IO PIO2 +1A IO 1 PIO2 IO PIO2 +1B IO 1 PIO2 IO PIO2 + +1C IO 1 PIO3 IO PIO3 +1D IO 1 PIO3 IO PIO3 +1E IO 1 PIO3 IO PIO3 +1F IO 1 PIO3 IO PIO3 + +20 RTC +21 IO 2 6402 data RTC +22 IO 2 6402 status RTC +23 RTC + +24 IO 2 PIO1 RTC +25 IO 2 PIO1 RTC +26 IO 2 PIO1 RTC +27 IO 2 PIO1 RTC + +28 IO 2 PIO1 RTC +29 IO 2 PIO1 RTC +2A IO 2 PIO1 RTC +2B IO 2 PIO1 RTC + +2C IO 2 PIO1 RTC +2D IO 2 PIO1 RTC +2E IO 2 PIO1 RTC +2F IO 2 PIO1 RTC + +30 ADC via PIO +31 ADC via PIO +32 ADC via PIO +33 ADC via PIO + +80 FPU +81 FPU +82 FPU + +A0 Pluto +A1 Pluto + +B0 AVC CRTC addr +B1 AVC CRTC data IVC Data +B2 AVC CRTC control IVC Handshake +B3 IVC Reset + +B4 GM813 PIO (1) +B5 GM813 PIO +B6 GM813 PIO +B7 GM813 PIO + +B8 8250@2MHz +B9 8250 (2) +BA 8250 +BB 8250 +BC 8250 +BD 8250 +BE 8250 +BF 8250 + +C0 MV256 +C1 MV256 +C2 MV256 +C3 MV256 +C4 MV256 +C5 MV256 +C6 MV256 +C7 MV256 +C8 MV256 +C9 MV256 +CA MV256 +CB MV256 +CC MV256 +CD MV256 +CE MV256 +CF MV256 + +D0 MV256 + +E0 FDC stat/cmd FDC stat/cmd FDC stat/cmd +E1 FDC track FDC track FDC track +E2 FDC sector FDC sector FDC sector +E3 FDC data FDC data FDC data +E4 FDC select FDC select/status select/status +E5 FDC status SCSI control select/status +E6 SCSI data keyboard +E7 Reserved keyboard +E8 Alarm +E9 Alarm +EA 6845 register +EB 6845 data +EC Video control +ED Video control +EE Select video 1 +EF Select video 2 + +F6 ARFON Speech +FB Ramdisc +FC Ramdisc +FD Ramdisc +FE GM813 MMU 32K Paging +FF Page Mode Page Mode Page Mode + + +(1) Nascom PIO's are organized +Data A +Data B +Control A +Control B + +(2) The 8250 also controls the eprom enable via an I/O pin +- OUT1 switches RS232 and tape (0 = tape) +- OUT2 switches internalmemory disable (1 = disable) + + + +Memory Overlays + +8000 - BFFF Default for AVC + +F000 - FFFF GM813 EPROM + + +Attached to a PIO + +GM805 Henelec +Centronics Printer (info needed) +GM822 RTC +EPROM programmer +AVC vblank etc + +To Add + +GM848 quad serial - what was the usual base ? + +offsets are + +00 Dart 0 channel 0 data +01 Dart 0 channel 1 data +02 Dart 0 channel 0 ctrl +03 Dart 0 channel 1 ctrl +04-07 ditto for DART 1 +08-0C Baud rates for 0/0 0/1 1/0 1/1 +0D-0F Yet another Z80 PIO + +MAP80 MPI + +WT625 + diff --git a/Kernel/platform-nascom/README b/Kernel/platform-nascom/README new file mode 100644 index 00000000..4f9bab31 --- /dev/null +++ b/Kernel/platform-nascom/README @@ -0,0 +1,76 @@ +Just initial sketches. Nothing tested yet. + +Memory Map + +0000 - 00FF Vectors } +0100 - E7FF User Space } Banked Area +E600 - E7FF udata stash } +E800 - EFFF Common space } +F000 - F7FF ROM +F800 - FBFF Video +FC00 - FFFF Udata and common data (unbanked block) + +Once we have a vaguely sensible port and the split banking we need to move +things around a bit to avoid stacks FC00-FFFF because of video contention. +However that means supporting double stack switches as the Cromenco and +32K/32K banked pair code will need. + +Target Hardware + +Nascom I/II/III or maybe Gemini GM811/813 (is the 813 a variant port ?) + +Required: +Nascom I/II/II + 192K "page mode" RAM (may be able to do a 128K port as well) eg + RAM64/GM802 + A timer interrupt source (* indicates possible options) + +Options: + GM833 Ramdisc. Driver written, also steals up to 1MB of it for + swap if no other swap partition is found on disk. The full theoretical + 8MB is supported although 2MB was the most practically possible + + GM809/29/49/49A Floppy. Initial driver code done + + GM829/49/49A SASI/SCSI. Initial driver code done + + +Boot Loaders Needed +- Nascom Floppy +- MAP80 VFC +- Gemini FDC +- Gemini SASI/SCSI +- Probably a CP/M boot loader for the other cases + + + +Options: +Nascom AVC (384x256 / 768x256) (* if jumpered for Vblank port A bit1) +Nascom Floppy Controller +Nascom I/O card (PIO, CTC, UART) + Mostek 3882 CTC (Z80 CTC) * + 6402 UART + MK3881 PIO (Z80 PIO) + +GM805 'Henelec' single density floppy +GM810 IVC +GM816 (CTC 3x PIO optional serial GM818 - dual 8250) * +GM822 RTC over PIO ? +GM832 SVC +GM837 Climax Colour +BE847 Maths card +GM848 quad serial +GM862 RAM +MAP80 256K RAM +MAP80 MPI (serial, CTC, FDC, SASI) ? * +MAP80 VFC +WT625 Viewdata +Other RTC options (some RTC have 2Hz or so interrupt - not much use!) + + +Useful Disk Formats + +SS/DD 5.25" +77 cys 1 side 10x512 sectors per track skew 3 +BSH 4 BLM 15 EXM 1 DSM 186 DRM 127 AL0 0C0H AL1 0 OFS 2 + diff --git a/Kernel/platform-nascom/commonmem.s b/Kernel/platform-nascom/commonmem.s new file mode 100644 index 00000000..447d11d1 --- /dev/null +++ b/Kernel/platform-nascom/commonmem.s @@ -0,0 +1,11 @@ +; +; We only have a small amount of true (ie writable) common space. We +; don't really want stacks and stuff in it (it's contended by video) +; but for now this will get us going +; + .module commonmem + + .area _COMMONDATA + + .include "../cpu-z80/std-commonmem.s" + diff --git a/Kernel/platform-nascom/config.h b/Kernel/platform-nascom/config.h new file mode 100644 index 00000000..9824ccbe --- /dev/null +++ b/Kernel/platform-nascom/config.h @@ -0,0 +1,68 @@ +/* Set if you want RTC support */ +#undef CONFIG_RTC +/* 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 +/* Video terminal, not a serial tty */ +#define CONFIG_VT +/* Banked memory set up */ +#define CONFIG_BANK_FIXED +/* Input device support */ +#define CONFIG_INPUT +/* Full key up/down support */ +#define CONFIG_INPUT_GRABMAX 3 +/* SCSI or SASI */ +#define CONFIG_SCSI + +#define MAX_MAPS 16 +#define MAP_SIZE 0xE600 + +#define CONFIG_BANKS 1 /* 1 x 60K */ + +/* Vt definitions */ +/* Although it's a simple display the margins and weird top line mean it's + got its own little driver */ +#define VT_WIDTH 48 +#define VT_HEIGHT 16 /* Lie for the moment as the top line is weird */ +#define VT_RIGHT 47 +#define VT_BOTTOM 15 + +#define TICKSPERSEC 50 /* Ticks per second */ +#define PROGBASE 0x0000 /* Base of user */ +#define PROGLOAD 0x0100 /* Load and run here */ +#define PROGTOP 0xE600 /* Top of program, udata stash follows */ +#define PROC_SIZE 58 /* Memory needed per process */ + +#define SWAP_SIZE 0x74 /* 58K in blocks (to get the udata stash) */ +#define SWAPBASE 0x0000 /* We swap the lot in one, include the */ +#define SWAPTOP 0xE600 /* vectors so its a round number of sectors */ + +#define MAX_SWAPS 64 /* Should be plenty (2MB!) */ + +#define swap_map(x) ((uint8_t *)(x)) + +#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 */ + +#define MAX_BLKDEV 4 + +/* Device parameters */ +#define NUM_DEV_TTY 2 /* Tackle 80bus serial later */ +#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ +#define SWAPDEV (swap_dev) /* Device for swapping (dynamic). */ +#define NBUFS 10 /* Number of block buffers - keep in sync with asm! */ +#define NMOUNTS 4 /* Number of mounts at a time */ +/* Reclaim the discard space for buffers */ +#define CONFIG_DYNAMIC_BUFPOOL + +extern void platform_discard(void); diff --git a/Kernel/platform-nascom/crt0.s b/Kernel/platform-nascom/crt0.s new file mode 100644 index 00000000..046bd808 --- /dev/null +++ b/Kernel/platform-nascom/crt0.s @@ -0,0 +1,89 @@ + ; 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 _VIDEO + .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 _GSINIT + .area _GSFINAL + ; Buffers must be directly before discard as they will + ; expand over it + .area _BUFFERS + .area _DISCARD + .area _COMMONMEM + ; Doesn't matter if these go over the I/O space as they are + ; removed at the end of the build + .area _INITIALIZER + + ; imported symbols + .globl _fuzix_main + .globl init_early + .globl init_hardware + .globl s__DATA + .globl l__DATA + .globl s__DISCARD + .globl l__DISCARD + .globl s__BUFFERS + .globl l__BUFFERS + .globl s__COMMONMEM + .globl l__COMMONMEM + .globl s__INITIALIZER + .globl kstack_top + + ; exports + .globl _discard_size + + ; startup code + .area _CODE + +; +; We get booted from CP/M or a disk loader. +; +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 + ; then zero the data area + ld hl, #s__DATA + ld de, #s__DATA + 1 + ld bc, #l__DATA - 1 + ld (hl), #0 + ldir +; Zero buffers area + ld hl, #s__BUFFERS + ld de, #s__BUFFERS + 1 + ld bc, #l__BUFFERS - 1 + ld (hl), #0 + ldir + ld hl,#s__COMMONMEM + ld de,#s__DISCARD + or a + sbc hl,de + ld (_discard_size),hl + call init_early + call init_hardware + call _fuzix_main + di +stop: halt + jr stop + + .area _DISCARD +_discard_size: + .dw 0 diff --git a/Kernel/platform-nascom/devgm833.c b/Kernel/platform-nascom/devgm833.c new file mode 100644 index 00000000..cdd3683e --- /dev/null +++ b/Kernel/platform-nascom/devgm833.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include + +/* + * GM833 I/O mapped RAM disc + * + * 512K of RAM on I/O ports. A maximum of 16 boards can in theory be used + * although the reality is probably a limit of 4, and that was all CP/M + * coped with. It's organized as an array of 128 byte sectors, with 256 + * per track. In other words its a 16bit block address, 128 byte sector + * device. That makes it really simple to drive. + * + * Multiple cards form one bigger ramdisc, with a theoretical 8MB max but + * we expose each card as its own minor device as well as a single unified + * device so you can pick. Just don't mix! + * + * Use minor 0 for 'all', minors 1-16 for units. + */ + +__sfr __at 0xfb gm833_track; +__sfr __at 0xfc gm833_sector; + +static uint8_t openshift; +static uint8_t openmask; +static uint8_t num_gm833; + +static int gm833_transfer(uint8_t minor, bool is_read, uint8_t rawflag) +{ + int ct = 0; + uint8_t err = 0; + + /* We support swap, it's after all an ideal swap device! */ + io_page = 0; + if (rawflag == 1) { + if (d_blkoff(BLKSHIFT)) + return -1; + io_page = udata.u_page; + } else if (rawflag == 2) + io_page = swappage; + + /* Shift later minors by 512K per unit */ + if (minor) { + if (udata.u_block + udata.u_nblock > 1024) { + udata.u_error = EIO; + return -1; + } + udata.u_block += 1024 * (minor + openshift - 1); + } else + udata.u_block += 1024 * openshift; + /* We might overflow but the largest possible configuration is + 8MB so we always just fit */ + udata.u_nblock *= 4; + udata.u_block *= 4; + + while (ct < udata.u_nblock) { + gm833_sector = udata.u_block; + gm833_track = udata.u_block >> 8; + if (is_read) + gm833_in(udata.u_dptr); + else + gm833_out(udata.u_dptr); + udata.u_dptr += 128; + udata.u_block++; + ct++; + } + return udata.u_nblock << 7; +} + +int gm833_open(uint8_t minor, uint16_t flag) +{ + flag; + if (minor >= num_gm833 - openshift) { + udata.u_error = ENODEV; + return -1; + } + if (minor == 0) { + if (openmask) { + udata.u_error = EBUSY; + return -1; + } + } else + openmask |= (1 << minor); + return 0; +} + +int gm833_close(uint8_t minor) +{ + openmask &= ~(1 << minor); + return 0; +} + +int gm833_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag; + return gm833_transfer(minor, true, rawflag); +} + +int gm833_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag; + return gm833_transfer(minor, false, rawflag); +} + +/* Move to discard ?? */ + +void gm833_init(void) +{ + uint8_t *tmp = tmpbuf(); + int i; + + gm833_sector = 0; + io_page = 0; + + /* Holes are not allowed according to the config rules. If you have + holes it breaks - tough */ + for (i = 0;i < 16; i++) { + gm833_track = i << 4; + *tmp = 0x90 | i; + gm833_out(tmp); + gm833_in(tmp); + if (*tmp != (0x90 | i)) + break; + } + tmpfree(tmp); + + /* i is now the number of units present */ + num_gm833 = i; + if (i == 0) + return; + /* Steal 512K or 1MB for swap if there is no swap device + allocated on disk */ + if (swap_dev == 0) { + /* No swap was found so bag ram disc 1 */ + swap_dev = 0x0801; + /* Add swap */ + openshift = i >= 2 ? 2 : 1; + for (i = 0; i < openshift * 8 ; i++) + swapmap_add(i); + } + kprintf("gm833: %dK found", num_gm833 * 512); + kprintf(", %dK allocated for swap.\n", openshift * 512); + kputs("\n"); +} diff --git a/Kernel/platform-nascom/devgm833.h b/Kernel/platform-nascom/devgm833.h new file mode 100644 index 00000000..273bd617 --- /dev/null +++ b/Kernel/platform-nascom/devgm833.h @@ -0,0 +1,9 @@ + +extern void gm833_in(uint8_t *addr) __z88dk_fastcall; +extern void gm833_out(uint8_t *addr) __z88dk_fastcall; + +extern int gm833_open(uint8_t minor, uint16_t flag); +extern int gm833_close(uint8_t minor); +extern int gm833_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +extern int gm833_write(uint8_t minor, uint8_t rawflag, uint8_t flag); +extern void gm833_init(void); diff --git a/Kernel/platform-nascom/devgm8x9.c b/Kernel/platform-nascom/devgm8x9.c new file mode 100644 index 00000000..c764d904 --- /dev/null +++ b/Kernel/platform-nascom/devgm8x9.c @@ -0,0 +1,264 @@ +/* + * Driver for the GM809/829/849/849A floppy controllers + * (The SASI/SCSI has its own driver) + */ + +#include +#include +#include +#include +#include + +__sfr __at 0xE0 gm8x9_cmd; +__sfr __at 0xE0 gm8x9_status; +__sfr __at 0xE1 gm8x9_track; +__sfr __at 0xE2 gm8x9_sector; +__sfr __at 0xE3 gm8x9_data; +__sfr __at 0xE4 gm8x9_ctrl; +__sfr __at 0xE5 gm8x9_type; + +#define SIDE 1 +#define DDENS 2 +#define HDENS 4 +#define EIGHTINCH 8 + +static uint8_t gm8x9_cursel; +uint8_t gm8x9_steprate; +static uint8_t drive_last = 0xFF, flags_last; + +/* IBM3740 skew table */ +static uint8_t skew_3740[] = { + 1, 7, 13, 19, 25, 5, 11, 17, 23, 3, 9, 15, 21, 2, 8, 14, 20, 26, 6, 12, 18, 24, 4, 10, 16, 22 +}; + +/* Skewed at format level */ +static uint8_t skew_hard[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19.20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 +}; + +struct gmfd gmfd_drives[MAX_GMFD]; + +/* + * Calculate the drive control value and if need be update it + */ +static uint8_t gm8x9_select(uint8_t drive, uint8_t flags) +{ + uint8_t ret = 1; + + if (drive_last == drive && flags_last == flags) + return 0; + + if (drive_last != drive) { + if (drive_last != 0xFF) + gmfd_drives[drive_last].track = gm8x9_track; + drive_last = drive; + gm8x9_track = gmfd_drives[drive].track; + gm8x9_steprate = gmfd_drives[drive].steprate; + ret = 2; + } + + flags_last = flags; + + + if (gm8x9_type & 0x80) { + /* 809 / 829 */ + drive = 1 << drive; + if (flags & SIDE) + drive |= 0/*?FIXME?*/; + } else { + if (flags & HDENS) + drive |= 0xB0; + if (flags & SIDE) + drive |= 0x08; + } + if (flags & DDENS) + drive |= 0x10; + if (flags & EIGHTINCH) + drive |= 0x20; + gm8x9_cursel = drive; + gm8x9_ctrl = drive; + return ret; +} + +/* + * We only support normal block I/O for the moment. We do need to + * add swapping! + */ + +static int gm8x9_transfer(uint8_t minor, bool is_read, uint8_t rawflag) +{ + int ct = 0; + int tries; + uint8_t err = 0; + uint8_t side, sector, track; + irqflags_t irqflags; + struct gmfd *fd = gmfd_drives + minor; + + if (rawflag == 2) + goto bad2; + + /* Translate everything into physical sectors. d_blkoff does the work + for raw I/O we do it for normal block I/O */ + if (rawflag) { + io_page = udata.u_page; + if (d_blkoff(fd->bs)) + return -1; + } else { + io_page = 0; + udata.u_nblock <<= fd->bs; + udata.u_block <<= fd->bs; + } + + /* Loop through each logical sector translating it into a head/track/sector + and then attempting to do the I/O a few times */ + while (ct < udata.u_nblock) { + side = 0; + sector = fd->skewtab[udata.u_block % fd->spt]; + track = udata.u_block / fd->spt; + if (sector > fd->ds) { + sector -= fd->ds; + side = SIDE; + } + /* Set the drive parameters, also pokes the motor */ + gm8x9_select(minor, fd->dens | side); + /* TODO - any delays ?? */ + + /* Make multiple attempts to get the data. If it keeps failing try + restoring the head and seeking in order to re-align */ + for (tries = 0; tries < 5; tries++) { + /* Try to get the requested track */ + if (gm8x9_track != track) { + if ((err = gm8x9_seek(track))) { + gm8x9_restore(); + continue; + } + } + /* The timing on these is too tight to do with interrupts on */ + irqflags = di(); + if (is_read) + err = gm8x9_ioread(udata.u_dptr); + else + err = gm8x9_iowrite(udata.u_dptr); + irqrestore(irqflags); + + /* It worked - exit then inner retry loop and move on */ + if (err == 0) + break; + /* Force a head seek */ + if (tries > 1) + gm8x9_restore(); + } + if (tries == 5) + goto bad; + /* Move on a sector */ + udata.u_block++; + udata.u_dptr += fd->ss; + ct++; + } + + /* Data read in bytes */ + return udata.u_nblock << (9 - fd->bs); + bad: + kprintf("fd%d: error %x\n", minor, err); + bad2: + udata.u_error = EIO; + return -1; +} + +uint8_t gm8x9_density(uint8_t minor, uint8_t flags) +{ + flags &= EIGHTINCH; + /* Try double density */ + gm8x9_select(minor, DDENS | flags); + if (gm8x9_restore_test() == 0) + return DDENS | flags; + /* Try single density */ + gm8x9_select(minor, flags); + if (gm8x9_restore_test() == 0) + return flags; + /* We can only do HD with 5.25/3.5/3 inch media on an 849 or 849A */ + if ((gm8x9_type & 0x80) || (flags & EIGHTINCH)) + return 255; + gm8x9_select(minor, HDENS | flags); + if (gm8x9_restore_test() == 0) + return flags | HDENS; + /* Nothing worked */ + return 255; +} + +int gm8x9_open(uint8_t minor, uint16_t flag) +{ + uint8_t den; + struct gmfd *d = gmfd_drives + minor; + + flag; + if (((gm8x9_type & 0x80) && minor > 4) || minor > MAX_GMFD) { + udata.u_error = ENODEV; + return -1; + } + if ((den = gm8x9_density(minor, d->dens)) == 255 && !(flag & O_NDELAY)) { + udata.u_error = -EIO; + return -1; + } + /* FIXME: how to detect double sided ? */ + d->dens = den; + + /* Default media types need to add switching ioctls yet */ + /* Once we also have the media geometry info in the superblock + it'll get a *lot* easier */ + /* Also need to add soft skewing ioctl */ + d->bs = 0; + d->ss = 512; + d->ds = 0; + memcpy(d->skewtab, skew_hard, MAX_SKEW); + switch (den) { + case 0: + /* 18 tps 128bps double sided */ + d->spt = 36; + d->bs = 2; + break; + case EIGHTINCH: + /* IBM3740: Classic CP/M SS/SD 77 track 26 tps 128bps */ + d->spt = 26; + d->ds = 255; + memcpy(d->skewtab, skew_3740, MAX_SKEW); + break; + case DDENS: + case 255: + /* IBM PC style 5.25" double density is 18/9 but Nascom like many + other systems use 20/10 */ + d->spt = 20; + break; + case DDENS | EIGHTINCH: + /* There are no real standards here, so use the Cromemco one */ + d->spt = 32; + break; + case HDENS: + /* IBM PC style 5.25" high density. 3.5" is 36/18 spt */ + d->spt = 30; + break; + } + d->ss >>= d->bs; + if (!d->ds) + d->ds = d->spt / 2; + return 0; +} + +int gm8x9_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag; + return gm8x9_transfer(minor, true, rawflag); +} + +int gm8x9_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag; + rawflag; + minor; +// return 0; + return gm8x9_transfer(minor, false, rawflag); +} + + +/* TODO discard routine to init this lot */ \ No newline at end of file diff --git a/Kernel/platform-nascom/devgm8x9.h b/Kernel/platform-nascom/devgm8x9.h new file mode 100644 index 00000000..689e7423 --- /dev/null +++ b/Kernel/platform-nascom/devgm8x9.h @@ -0,0 +1,34 @@ +#ifndef __DEVFD_DOT_H__ +#define __DEVFD_DOT_H__ + +/* public interface */ +int gm8x9_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +int gm8x9_write(uint8_t minor, uint8_t rawflag, uint8_t flag); +int gm8x9_open(uint8_t minor, uint16_t flag); + +/* low level interface */ +uint8_t gm8x9_seek(uint8_t track) __z88dk_fastcall; +uint8_t gm8x9_restore(void); +uint8_t gm8x9_restore_test(void); +uint8_t gm8x9_reset(void); +uint8_t gm8x9_ioread(uint8_t *dptr) __z88dk_fastcall; +uint8_t gm8x9_iowrite(uint8_t *dptr) __z88dk_fastcall; + +#define MAX_GMFD 16 +#define MAX_SKEW 32 + +struct gmfd { + uint8_t track; /* Saved track value */ + uint8_t spt; /* Sectors per track (including both sides if DS) */ + uint8_t bs; /* Block shift to sectors */ + uint16_t ss; /* Sector size */ + uint8_t ds; /* Sector that starts second side, 255 = SS */ + uint8_t dens; /* Density and sides info, inc 8 v 5.25 */ + uint8_t skewtab[MAX_SKEW]; /* Skew table */ + uint8_t steprate; +}; + +extern struct gmfd gmfd_drives[MAX_GMFD]; + + +#endif /* __DEVFD_DOT_H__ */ diff --git a/Kernel/platform-nascom/devices.c b/Kernel/platform-nascom/devices.c new file mode 100644 index 00000000..85cb0042 --- /dev/null +++ b/Kernel/platform-nascom/devices.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct devsw dev_tab[] = /* The device driver switch table */ +{ + /* 0: /dev/hd SCSI/SASI block devices */ + { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl}, + /* 1: /dev/fd Floppy disc block devices */ + {gm8x9_open, no_close, gm8x9_read, gm8x9_write, no_ioctl}, + /* 2: /dev/tty TTY devices */ + {tty_open, tty_close, tty_read, tty_write, vt_ioctl}, + /* 3: /dev/lpr Printer devices */ + {nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl}, + /* 4: /dev/mem etc System devices (one offs) */ + {no_open, no_close, sys_read, sys_write, sys_ioctl}, + /* Pack to 7 with nxio if adding private devices and start at 8 */ + {nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl}, + {nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl}, + {nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl}, + /* Ram driver */ + {gm833_open, gm833_close, gm833_read, gm833_write, no_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) - 1) + return false; + else + return true; +} diff --git a/Kernel/platform-nascom/devinput.c b/Kernel/platform-nascom/devinput.c new file mode 100644 index 00000000..a6462622 --- /dev/null +++ b/Kernel/platform-nascom/devinput.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +static char buf[32]; +static struct s_queue kqueue = { + buf, buf, buf, sizeof(buf), 0, sizeof(buf) / 2 +}; + +/* Queue a character to the input device */ +void queue_input(uint8_t c) +{ + insq(&kqueue, c); + wakeup(&kqueue); +} + +int platform_input_read(uint8_t * slot) +{ + uint8_t r, k; + if (remq(&kqueue, &r)) { + remq(&kqueue, &k); + *slot++ = KEYPRESS_CODE | r; + *slot++ = k; + return 2; + } + return 0; +} + +void platform_input_wait(void) +{ + psleep(&kqueue); +} + +int platform_input_write(uint8_t flag) +{ + flag; + udata.u_error = EINVAL; + return -1; +} + +void poll_input(void) +{ +} diff --git a/Kernel/platform-nascom/devinput.h b/Kernel/platform-nascom/devinput.h new file mode 100644 index 00000000..5d79a9e0 --- /dev/null +++ b/Kernel/platform-nascom/devinput.h @@ -0,0 +1,4 @@ + +extern void queue_input(uint8_t c); +extern void poll_input(void); +extern uint8_t vblank; diff --git a/Kernel/platform-nascom/devnascom.c b/Kernel/platform-nascom/devnascom.c new file mode 100644 index 00000000..f94bb26d --- /dev/null +++ b/Kernel/platform-nascom/devnascom.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char tbuf1[TTYSIZ]; +char tbuf2[TTYSIZ]; + +struct vt_repeat keyrepeat; + +__sfr __at 0x01 s6402_data; +__sfr __at 0x02 s6402_status; + +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}, +}; + +/* Write to system console */ +void kputchar(char c) +{ + if (c == '\n') + tty_putc(1, '\r'); + tty_putc(1, c); +} + +ttyready_t tty_writeready(uint8_t minor) +{ + uint8_t reg; + if (minor == 1) + return TTY_READY_NOW; + reg = s6402_status; + return (reg & 0x40) ? TTY_READY_NOW : TTY_READY_SOON; +} + +void tty_putc(uint8_t minor, unsigned char c) +{ + if (minor == 2) + s6402_data = c; + else + vtoutput(&c, 1); +} + +void tty_poll(void) +{ + uint8_t reg = s6402_status; + if (reg & 0x80) { + reg = s6402_data; + tty_inproc(2, reg); + } +} + +void tty_setup(uint8_t minor) +{ + /* The console is a crt/keyboard, the 6402 is set by jumpers */ +} + +int tty_carrier(uint8_t minor) +{ + return 1; +} + +void tty_sleeping(uint8_t minor) +{ + used(minor); +} + +void tty_data_consumed(uint8_t minor) +{ + used(minor); +} + +uint8_t keymap[9]; +static uint8_t keyin[9]; +static uint8_t keybyte, keybit; +static uint8_t newkey; +static int keysdown = 0; +static uint8_t shiftmask[8] = { + 0, 0, 0, 0, 0, 0, 0, 7 +}; + +__sfr __at 0 kbd_data; + +static void keyproc(void) +{ + int i; + uint8_t key; + + kbd_data = 2; /* Reset the keyboard */ + + for (i = 0; i < 9; i++) { + /* Read the keyboard row */ + keyin[i] = kbd_data; + kbd_data = 1; /* Clock the keyboard */ + key = keyin[i] ^ keymap[i]; + if (key) { + int n; + int m = 1; + for (n = 0; n < 8; n++) { + if ((key & m) && (keymap[i] & m)) { + if (!(shiftmask[i] & m)) { + if (keyboard_grab == 3) { + queue_input(KEYPRESS_UP); + queue_input(keyboard[i][n]); + } + keysdown--; + } + } + if ((key & m) && !(keymap[i] & m)) { + if (!(shiftmask[i] & m)) { + keysdown++; + newkey = 1; + keybyte = i; + keybit = n; + } + } + m += m; + + } + } + keymap[i] = keyin[i]; + } +} + +/* + * The Nascom has a surprisingly complete and good keyboard. It's lacking + * only a capslock key and {|}. There are some oddities however + * + * Escape is shift-enter, cs is shift-backspace, there is a lf/ch button + * and a graph button that's basically unused but will no doubt excite + * emacs people 8) + */ + +uint8_t keyboard[9][8] = { + /* just a shift in the top row */ + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, KEY_UP, 't', 'x', 'f', '5', 'b', 'h'}, + {0, KEY_LEFT, 'y', 'z', 'd', '6', 'n', 'j'}, + {0, KEY_DOWN, 'u', 's', 'e', '7', 'm', 'k'}, + {0, KEY_RIGHT, 'i', 'a', 'w', '8', ',', 'l'}, + /* FIXME: the ? is the graph key - what to do with it */ + {0, '?', 'o', 'q', '3', '9', '.', ';'}, + {0, '[', 'p', '1', '2', '0', '/', ':'}, + {0, ']', 'r', ' ', 'c', '4', 'v', 'g'}, + /* What to do with ch ? */ + {0, '?', '@', 0, 0, '-', KEY_ENTER, KEY_BS} + /* Ch, @ shift cntrl - ... */ +}; + +uint8_t shiftkeyboard[9][8] = { + /* just a shift in the top row */ + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, KEY_UP, 'T', 'X', 'F', '%', 'B', 'H'}, + {0, KEY_LEFT, 'Y', 'Z', 'D', '&', 'N', 'J'}, + {0, KEY_DOWN, 'U', 'S', 'E', '\'', 'M', 'K'}, + {0, KEY_RIGHT, 'I', 'A', 'W', '(', '<', 'L'}, + /* FIXME key graph ? */ + {0, '?', 'O', 'Q', KEY_POUND, ')', '>', '+'}, + {0, ' \\ ', ' P ', ' ! ', ' "', '^', '/', '*'}, + {0, '_', 'R', ' ', 'C', '$', 'V', 'G'}, + /* What to do with ch ? */ + {0, '?', '@', 0, 0, '=', KEY_ESC, KEY_BS}, + /* Ch, @ shift cntrl - ... */ +}; + +static uint8_t kbd_timer; + +static void keydecode(void) +{ + uint8_t c; + uint8_t m = 0; + + if ((keymap[8] & 0x20) || (keymap[0] & 0x10)) { /* shift */ + m = KEYPRESS_SHIFT; + c = shiftkeyboard[keybyte][keybit]; + } else + c = keyboard[keybyte][keybit]; + + if (keymap[8] & 0x10) { /* control */ + m |= KEYPRESS_CTRL; + if (m & KEYPRESS_SHIFT) { /* shift */ + if (c == '(') + c = '{'; + if (c == ')') + c = '}'; + if (c == '^') + c = '|'; + } else if (c > 31 && c < 127) + c &= 31; + } + if (c) { + switch (keyboard_grab) { + case 0: + vt_inproc(1, c); + break; + case 1: + if (!input_match_meta(c)) { + vt_inproc(1, c); + break; + } + /* Fall through */ + case 2: + queue_input(KEYPRESS_DOWN); + queue_input(c); + break; + case 3: + /* Queue an event giving the base key (unshifted) + and the state of shift/ctrl/alt */ + queue_input(KEYPRESS_DOWN | m); + queue_input(keyboard[keybyte][keybit]); + break; + } + } +} + +void kbd_poll(void) +{ + /* Report any press of the NMI button */ + if (nmikey) { + nmikey = 0; + vt_inproc(1, KEY_STOP); + } + newkey = 0; + keyproc(); + if (keysdown && keysdown < 3) { + if (newkey) { + keydecode(); + kbd_timer = keyrepeat.first; + } else if (!--kbd_timer) { + keydecode(); + kbd_timer = keyrepeat.continual; + } + } +} diff --git a/Kernel/platform-nascom/devtty.h b/Kernel/platform-nascom/devtty.h new file mode 100644 index 00000000..43c86495 --- /dev/null +++ b/Kernel/platform-nascom/devtty.h @@ -0,0 +1,13 @@ +#ifndef _DEVTTY_H +#define _DEVTTY_H + +extern void tty_poll(void); +extern void kbd_poll(void); + +#define KEY_ROWS 9 +#define KEY_COLS 8 +extern uint8_t keymap[9]; +extern uint8_t keyboard[9][8]; +extern uint8_t shiftkeyboard[9][8]; + +#endif diff --git a/Kernel/platform-nascom/discard.c b/Kernel/platform-nascom/discard.c new file mode 100644 index 00000000..e977c6e3 --- /dev/null +++ b/Kernel/platform-nascom/discard.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +uint8_t io_page; +uint16_t bankmap; + +void device_init(void) +{ +#ifdef CONFIG_RTC + /* Time of day clock */ + inittod(); +#endif + devscsi_init(); + /* Must come last as we want to allocate this for swap if no other + swap was found */ + gm833_init(); +} + +void map_init(void) +{ +} + +/* Add pagemap codes depending upon the present banks */ +void pagemap_init(void) +{ + /* We didn't find any extra page mode memory */ + if (bankmap < (2 << 8)) + panic("no pagemode"); + /* Add the present banks (may be non contiguous). The + first bank is the kernel and not available */ + if (bankmap & 2) + pagemap_add(0x22); + if (bankmap & 4) + pagemap_add(0x44); + if (bankmap & 8) + pagemap_add(0x88); +} + +/* Swap partition on the hard disk */ +void platform_swap_found(uint8_t letter, uint8_t m) +{ + blkdev_t *blk = blk_op.blkdev; + uint16_t n; + if (swap_dev != 0xFFFF) + return; + letter -= 'a'; + kputs("(swap) "); + swap_dev = letter << 4 | m; + n = blk->lba_count[m - 1] / SWAP_SIZE; + if (n > MAX_SWAPS) + n = MAX_SWAPS; + while (n) + swapmap_add(n--); +} diff --git a/Kernel/platform-nascom/gm833.s b/Kernel/platform-nascom/gm833.s new file mode 100644 index 00000000..34dd8a45 --- /dev/null +++ b/Kernel/platform-nascom/gm833.s @@ -0,0 +1,31 @@ +; +; Gemini 833 Ramdisc support code +; + .module gm833 + + .include "kernel.def" + .include "../kernel.def" + + .globl _gm833_in + .globl _gm833_out + + .globl map_process_a + .globl map_kernel + .globl _io_page + + .area _COMMONMEM + +_gm833_in: + ld a,(_io_page) + or a + call nz, map_process_a + ld bc,#0x80FD + inir + jp map_kernel +_gm833_out: + ld a,(_io_page) + or a + call nz, map_process_a + ld bc,#0x80FD + otir + jp map_kernel diff --git a/Kernel/platform-nascom/gm849a_sasi.s b/Kernel/platform-nascom/gm849a_sasi.s new file mode 100644 index 00000000..8d54abe0 --- /dev/null +++ b/Kernel/platform-nascom/gm849a_sasi.s @@ -0,0 +1,162 @@ + .module gm8x9_sasi +; +; SASI/SCSI support for the GM829/49/49A +; This is basically a dumb bus interface except that it automates +; ACK handling. + + .include "kernel.def" + .include "../kernel.def" + + .globl _si_read + .globl _si_write + .globl _si_writecmd + .globl _si_select + .globl _si_reset + .globl _si_clear + .globl _si_deselect + + .globl _io_page + .globl _si_dcb + + .globl map_process_a + .globl map_kernel + + .area _COMMONMEM +; +; Wait for /REQ. Preserve registers except for A +; Z = ok, NZ = error, in which case L holds any error info and is != 0 +; +; FIXME: add timeout and error checks +; +si_waitreq: + ld l,#1 +si_waitreql: + in a,(0xE5) + ; What should we do about monitoring BSY, C/D and I/O direction ? + bit 0,a + jr nz, si_waitreql + ld l,#0 + ret +; +; Read a block of bytes into HL +; +_si_read: + ld c,#0xE6 ; port + ld de,(_si_dcb + 16) ; length + ld a,(_io_page) + or a + call nz, map_process_a +si_readw: + call si_waitreq + jr nz, si_bad + ; + ; Each time we see /REQ we write a byte which causes the controller + ; to generate /ACK for us + ; + ini + dec de + ld a,d + or e + jr z, si_readw +si_good: + ld hl,#0 +si_bad: + jp map_kernel + +_si_write: + ld b,c + ld c,#0xE6 + ld de,(_si_dcb+16) ; length word + ld a,(_io_page) + or a + call nz, map_process_a +si_writew: ; wait for REQ + call si_waitreq + jr nz, si_bad + outi + dec de + ld a,d + or e + jr nz, si_writew + jr si_good + + .area _CODE +; +; On entry HL is the length of the command block +; +_si_writecmd: + in a,(0xE5) + ; FIXME: check errors/timeout + bit 0,a + jr nz, _si_writecmd ; wait for REQ + bit 2,a + jp nz, si_bad ; phase wrong + ex de,hl + ld hl,#_si_dcb + jp si_writew +; +; Select a device +; +_si_select: + ld a,(_si_dcb + 20) + cp #7 + jr z, si_badsel ; clash of id +; +; Now perform the device selection ritual +; + ld b,a + ld a,#0x80 +shiftid: + rra + djnz shiftid + + ld de,#0xFFFF +selwait: + dec de + ld a,d + or e + jr z, timedout + ; FIXME errors and timeout + in a,(0xE5) + ; Wait for BSY to drop so the bus is idle + bit 4,a + jr z, selwait + ; Now put the mask on the bus and assert /SEL + ld a,#0x05 + out (0xE5),a +selwait2: + dec de + ld a,d + or e + jr z, timedout + ; FIXME: errors, timeout + in a,(0xE5) + bit 4,a + jr nz, selwait2 + ; Drive responded, drop /SEL + ld a,#0x07 + out (0xE5),a + ld hl,#0 + ret +timedout: + ld hl,#1 + ret +si_badsel: + ld hl,#2 + ret + +_si_reset: + ; Assert reset + ld a,#0x03 + out (0xE5),a + ; FIXME: check spec for delay if needed + ld a,#0x07 + out (0xE5),a + ret +_si_clear: + ; Disconnect from the bus + ld a,#0x0F + out (0xE5),a +_si_deselect: + ret + diff --git a/Kernel/platform-nascom/gm8x9.s b/Kernel/platform-nascom/gm8x9.s new file mode 100644 index 00000000..dfb9176d --- /dev/null +++ b/Kernel/platform-nascom/gm8x9.s @@ -0,0 +1,234 @@ + .module gm8x9 + + .include "kernel.def" + .include "../kernel.def" + + .globl _gm8x9_ioread + .globl _gm8x9_iowrite + .globl _gm8x9_reset + .globl _gm8x9_seek + .globl _gm8x9_restore + .globl _gm8x9_restore_test + + .globl _io_page + .globl _gm8x9_steprate + .globl map_process_a + .globl map_kernel + +; +; The 809 is a 1797 with 5.25" (optionally 8") disk support +; and unlike the Henelec supports double density, and does automatic +; hardware delays. +; +; Usual formats are +; SD: 35 track 18 sector 128 bytes/sector dual sided +; DD: 35 track 10 sector 512 bytes/sector dual sided +; +; The GM829 is similar but also has an optional SASI interface. The +; MAP80 VFC is GM809 compatible on the floppy side. +; +; E0-E3 are the 1797 +; +; E4 0-3 selects drives 0-3 +; E4 4 switches density +; E4 5 switches drive type (829 only) +; +; Side select is in type II commands (bit 3) and bit 3 of ctrl ? +; +; Writing E4 also turns on the motor +; +; On the read side it's the usual bit 7 is DRQ bit 0 is INTRQ +; Ready *may* be available on bit 1. If not and it's properly jumpered +; bit 1 is tied to the drive being selected. The spare bits are tied +; to 0 and the motor on is 0 when running. In other words we can do +; the wait purely on flags +; +; The GM849 moves up to a WD2797 and by this time "normal" drives +; are 40/80 track. 3.5" and 3" are also supported. +; +; E4 changes entirely +; E4 0-2 select drive by value (0-7) +; E4 3 is now side only +; E4 4 is SD/DD as before +; E4 5 is still 5/8" [1 = 8] +; E4 7 is 300rpm(DD) or 360rpm(HD) +; +; Basically bit 4/5 are speed selectors +; 5 4 +; 0 0 125Kbit FM +; 0 1 250Kbit MFM +; 1 0 250Kbit FM +; 1 1 500Kbit MFM +; +; And bit 7 controls the rotation speed so you can use 500Kbit MFM +; with 5.25" to read 1.2MB disks (or 1.44MB) if you have a 4MHz CPU +; +; E5 bit 7 reads 0 for an 849 and 1 for an 829 +; +; +; The read and write data loops here are taken from +; "GM809 and Eight Inch Drives" 80BUS News July-Oct 1982, by D Parkinson +; who deserves a medal for figuring this one out. +; +; The Nascom FDC is very similar except that E4 is again different +; 0-3: select drive 0-3 +; 4: side select (if LK.2 is A-C) +; 5: low if LK3 set (normally high) +; 6: density +; 7: low if LK4 set (normally high) +; +; Status is on E5 although arranged the same way as the GM8x9 +; + .area _COMMONMEM + +FDREAD .equ 0x88 +FDWRITE .equ 0xA8 +FDSEEK .equ 0x14 ; + step rate 0-3 1C to keep head loaded +FDREST .equ 0x00 ; "" "" +FDRESCHK .equ 0x04 ; "" "" (should this be 0C Check) +; +; Read the sector data into (HL). Caller has seeked appropriately +; and loaded sector register +; +_gm8x9_ioread: + call motorbusy_check + ret nz + ld a,(_io_page) + or a + call nz, map_process_a + ; FIXME: need to sort out head loading and also side here + ld a,#FDREAD + out (0xE0),a ; issue command + ex (sp),hl ; check length versus 12h djnz loop FIXME + ex (sp),hl + ld c,#0xE4 ; magic status + jr read_sync +read_loop: + ; We can't ldi this because we need C to be E4 + ; LDI would be 16 versus 24 but it costs us 8 clocks to move + ; C back and forth so it's not a win + in a,(0xE3) ; 11 + ld (hl),a ; 7 + inc hl ; 6 +read_sync: + ; From the moment DRQ goes true we have 54 T states to read it + in a,(c) ; 11 + jr z, read_sync ; 12 / 7 + jp m, read_loop ; 10 + ; IRQ or timeout (eg motor off) - ie we finished +read_status: + call map_kernel + call motor_check + ret nz + ; + ; Read status + ; + in a,(0xE0) + ld l,a + ret +timed_out: + ld l,#0xFF + ret +; +; Write the sector data to (HL) +; +_gm8x9_iowrite: + call motorbusy_check + ret nz + ld a,(_io_page) + or a + call nz, map_process_a + ; FIXME: need to sort out head loading and also side here + ld a,#FDWRITE + out (0xE0),a ; issue command + ex (sp),hl + ex (sp),hl + ld c,#0xE4 ; 7 +write_loop: + ld a,(hl) ; 7 + inc hl ; 6 +write_wait: + ; For write DRQ requires data within 46 T states - hence the + ; load of A must occur first + in b,(c) ; 12 + jr z, write_wait ; 12 / 7 + ; If we load data with an extra byte (as we do on the end), it + ; will be ignored. + out (0xE3),a ; 11 + jp m, write_loop ; 10 + jr read_status + +; +; On error sets NZ and l to the error code +; +motorbusy_check: + in a,(0xE0) + bit 0,a + jr z,motor_check + ld l,#253 + ret +motor_check: + in a,(0xE4) + bit 1,a + ret z + ld l,#254 + ret + + .area _CODE +; +; Seek to track L. Assumes motor is running +; +_gm8x9_seek: + call motorbusy_check + ret nz + ld a,l + out (0xE3),a ; target track + ld a,#0x01 ; always valid sector + out (0xE2),a + ld b,#FDSEEK +issue_seek: + ld a,(_gm8x9_steprate) + or b +issue_cmd: + out (0xE0),a + ex (sp),hl + ex (sp),hl + ; FIXME: timeout on the wait loop +seek_wait_ready: + in a,(0xE4) + bit 0,a ; check + jr nz, seek_wait_ready + in a,(0xE0) + ld a,l + ret + +; +; Restore +; +_gm8x9_restore: + call motorbusy_check + ret nz + ld b,#FDREST + jr issue_seek +; +; Restore and check we can ready the track +; +_gm8x9_restore_test: + call motorbusy_check + ret nz + ld b,#FDRESCHK ; FIXME - what headload should we have ? + jr issue_seek + +; +; Reset things +; +; Needs a timeout check FIXME +; +_gm8x9_reset: + ld a,#0xD0 +reset_wait: + in a,(0xE4) + bit 1,a + jr nz, reset_wait + ld l,#0 + ret diff --git a/Kernel/platform-nascom/henelec.s b/Kernel/platform-nascom/henelec.s new file mode 100644 index 00000000..8062fd27 --- /dev/null +++ b/Kernel/platform-nascom/henelec.s @@ -0,0 +1,346 @@ + .module henelec + +; +; The original NASCOM floppy controller. Single density hooked up over +; the PIO ports so it didn't need an expansion chassis of any kind +; Data is wired to the FDC and a drive select latch. Data ends up +; inverted. +; +; The Henelec was a user designed device sold as a Henry's Electric +; kit and then by Gemini as the GM805. +; +; If you've met the PPIDE then it's the same essential model +; +; PIO A is wired to the data bus as D0-D7 but inverting +; PIO B is wired to the control lines as follows +; bit 7: DRQ +; bit 6: IRQ +; bit 5: reset +; bit 4: !wr for the drive select latch +; bit 3: !wr for the fdc +; bit 2: !rd for the fdc +; bits 1/0: A1/A0 for the FDC +; +; PIO B is set up once into bit mode with 7-6 input 5-0 output and +; PIO A is normally input but for writes goes to output +; +; The drive select latch selects between drives based upon bits +; bit 3: drive 2 +; bit 2: drive 1 +; bit 1: drive 0 +; bit 0: side select +; +; The system is usually plugged into a 35 track DSSD drive +; 35 x 18 x 128 5.25". It does apparently support 8" +; +; +; TODO: format +; Format causes another problem - you can't 'seek' to an unformatted +; track so we'll need _hfd_stepin as well. Then you can +; _hfd_restore ioctl +; generate track data +; _hfd_format ioctl +; read to check +; _hfd_stepin +; rinse/repeat +; +; FIXME: align methods with gm8x9.s so we can just jump table them +; +; FIXME: port addresses are different between a GM813 and a Nascom +; + ; + ; I/O methods + ; + .globl _hfd_select + .globl _hfd_io + .globl _hfd_reset + .globl _hfd_restore + + ; + ; Imports + ; + .globl _hfd_track + .globl _hfd_buffer + .globl _hfd_seccmd + .globl _io_page + + .include "kernel.def" + .include "../kernel.def" + + .area _CODE + +init_pio: + ; Program the PIO + ; Make our data port input + ld a,#0xFF + out (pioa_ctrl),a + out (pioa_ctrl),a + ; And our control port bit controlled + out (piob_data),a + out (piob_ctrl),a + ld a,#0xC0 ; DRQ and IRQ are input lines + out (piob_ctrl),a + ret + +; +; Select drive B +; +_hfd_select: + ld a,(_hfd_drive) +henelec_drvsel: + ld a,#0x3F + out (piob_data),a ; ensure all write/read enables are off + ld a,b + ld (pioa_data),a + ld a,#0x1f + out (piob_data),a ; write enable for the latch, latches 0 + ld a,#0x3f + out (piob_data),a ; write enable back off + ld a,#0xff + out (pioa_ctrl),a ; back to being an input + out (pioa_ctrl),a + call sleep100ms + ret + +; +; We might be able to squash more of this out of commonmem +; + .area _COMMONMEM +; +; Return value from port c in b +; +henelec_in: + ld a,c + out (piob_data),a ; addres bits +; +; We always I think (maybe not data xfer - check) +; put these back in out methods, so could skip here ? +; + ld a,#0xFF + out (pioa_ctrl),a + inc a + out (pioa_ctrl),a ; input mode + ld a,c + res 2,c ; pulse RE + out (piob_data),a + in a,(pioa_data) + cpl + or a ; does cpl set these FIXME + ld b,a + ld a,c + out (piob_data),a ; pulse over + ret + +; +; Send command B to register C +; +henelec_out: + ld a,c + out (piob_data),a ; address bits + ld a,#0xFF ; data to output + out (pioa_ctrl),a + out (pioa_ctrl),a + ld a,b ; gets inverted + cpl + out (pioa_data),a ; data on data lines + ld a,c + res 3,c ; pulse WE + out (piob_data),a + ld a,c + out (piob_data),a + ld a,#0xFF ; back to input + out (pioa_ctrl),a + inc a + out (pioa_ctrl),a + ret + +henelec_reset: + ; + ; This magic sequence is what we see DDOS do + ; + ; Data to output + ld a,#0xFF + out (pioa_ctrl),a + inc a + out (pioa_ctrl),a + ld b,a ; b = 0 + call henelec_drvsel + ld bc,#SEC_REG + out (c),b + call wait_a_second + ; + ; Waggle the reset line + ; + ld a,#0x2F + out (piob_data),a + ; + ; 1771 delay + ; + ld b,#20 +l1: djnz l1 + ; + ; Wait for 1771 to go ready + ; + ld a,#0x3F +; +; Wait for motor spin up +; +wait_ready: + push bc + ld c,#STAREG + call henelec_in + jp p,motor_running + ; Poke sector register (seems to write crap to it) +motor_up: + ld c,#SECREG + call henelec_out + ; And sleep + call wait_a_second + ; And see if we are ready +motor_running: + ld c,#STAREG + call henelec_in + jp m,motor_up + jp c,motor_running + pop bc + ret +; +; Disk seek +; +henelec_seek: + call wait_ready + ld c,#DATAREG + ld a,(_hfdc_track) + cpl + ld b,a + call henelec_out + ld bc,#(SEEKCMD*256)+CMDREG + call henelec_out +swait: + in a,(piob_data) + and #0x40 + jr z,swait + ld c,#STAREG + call henelec_in + and #0x98 + ld l,a + ; check for a timeout + ret +; +; Disk read +; B = sector +; HL = buffer +; +; Length is taken from the media and I'm not sure there +; is time to do better! +; +henelec_diskread: + call wait_ready + ld c,#SECREG + call henelec_out + ld bc,#(READCMD * 256)+CMDREG + call henelec_out + ld bc,#(DATREG*256)+DATREG-4 + ld a,b + out (piob_data),a +rdwait: in a,(piob_data) ; Check status + and #0xC0 + jp z,rdwait + jp p,rdirq + ld a,c + out (piob_data),a + in a,(pioa_data) + cpl + ld (hl),a + ld a,b + out (piob_data),a + inc hl + jp rdwait +rdirq: ld c,#STAREG + call henelec_in + ; check timeout etc + ret +; +; Disk write +; B = sector +; HL = buffer +; +; Same basic idea +; +henelec_diskwrite: + call wait_ready + ld c,#SECREG + call henelec_out + ld bc,#(WRITECMD*256)+CMDREG + call henelec_out + ld a,#0xff + out (pioa_ctrl),a + out (pioa_ctrl),a + ld a,b + out (piob_data),a + ld bc,#(DATREG*256)+DATREG-8 +wbytel: ld a,(hl) + inc hl + cpl + out (pioa_data),a +wrwait: in a,(piob_data) + and 0xC0 + jp z, wrwait + jp p,wrirq + ld a,c + out (piob_data),a + ld a,b + out (piob_data),a + jp wbytel +wrirq: + ld c,#STAREG + call henelec_int + ; check timeout etc + ret +; +; Fuzix glue +; +_hfd_io: + ld c, #TRKREG + call henelec_in + ld a,(_hfd_track) + cp b + jr z,no_seekw + call henelec_seek + jr nz,seek_fail +no_seekw: + ld hl,(_hfd_buffer) + ld bc,(_hfd_seccmd) ; loads sector into B + ld a,(_io_page) + or a + call nz, map_process_a + or a + jr z, iowrite + call henelec_diskread +iodone: + call map_kernel +seek_fail: + ld l,a ; return the status code + ret +iowrite: + call henelec_diskwrite + jr iodone + +_hfd_reset: + call init_pio + call henelec_reset + ld bc,#0x5500+SECREG + call henelec_out + ld c,#SECREG + call henelec_in + ld a,#0x55 + cp b + ld hl,#0 + ret z + dec hl + ret + +_hfd_restore: + ld c, #(CMD_RESTORE * 256) + CMDREG + call henelec_out + jr swait diff --git a/Kernel/platform-nascom/kernel.def b/Kernel/platform-nascom/kernel.def new file mode 100644 index 00000000..0caf0514 --- /dev/null +++ b/Kernel/platform-nascom/kernel.def @@ -0,0 +1,17 @@ +; UZI mnemonics for memory addresses etc + +U_DATA .equ 0xFC00 ; (this is struct u_data from kernel.h) +U_DATA__TOTALSIZE .equ 0x200 ; 256+256 (we don't save istack) + +U_DATA_STASH .equ 0xE600 ; E800-E9FF + +PROGBASE .equ 0x0000 +PROGLOAD .equ 0x0100 + +Z80_TYPE .equ 1 + +NBUFS .equ 10 + +Z80_MMU_HOOKS .equ 0 + +CONFIG_SWAP .equ 1 diff --git a/Kernel/platform-nascom/main.c b/Kernel/platform-nascom/main.c new file mode 100644 index 00000000..6ceee02d --- /dev/null +++ b/Kernel/platform-nascom/main.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include + +uint16_t ramtop = PROGTOP; +uint8_t vtattr_cap; +uint16_t swap_dev; + +struct blkbuf *bufpool_end = bufpool + NBUFS; + +/* On idle we spin checking for the terminals. Gives us more responsiveness + for the polled ports */ +void platform_idle(void) +{ + irqflags_t irq = di(); + tty_poll(); + irqrestore(irq); +} + +void do_beep(void) +{ +} + +uint8_t platform_param(char *p) +{ + used(p); + return 0; +} + +void platform_interrupt(void) +{ + /* TODO */ + kbd_poll(); + tty_poll(); + timer_interrupt(); +} + +/* + * FIXME: reclaim to end of usable memory + */ +void platform_discard(void) +{ + bufptr bp = bufpool_end; + extern uint16_t discard_size; + + discard_size /= sizeof(struct blkbuf); + + kprintf("%d buffers reclaimed from discard\n", discard_size); + + bufpool_end += discard_size; /* Reclaim the discard space */ + + memset(bp, 0, discard_size * sizeof(struct blkbuf)); + /* discard_size is in discard so it dies here */ + for (bp = bufpool + NBUFS; bp < bufpool_end; ++bp) { + bp->bf_dev = NO_DEVICE; + bp->bf_busy = BF_FREE; + } +} diff --git a/Kernel/platform-nascom/nasboot.s b/Kernel/platform-nascom/nasboot.s new file mode 100644 index 00000000..3d7594a9 --- /dev/null +++ b/Kernel/platform-nascom/nasboot.s @@ -0,0 +1,54 @@ +; +; NASCOM boot block +; +; The ROM loads 512 bytes from track 0, side 0, sector 1 into a buffer +; and then down to 0x0100 +; +; The block must start with the code 'NCB' and the next byte is the +; entry point. +; +; The stack is below 0100 and the ROM is fixed at F000 +; +; It has a jump table at the start with helper routines +; +; Our disk is selected +; +; FC74/5/6 hold the disk, track, sector +; +; track 0-76 - side 0, 77+ side 1! +; +; This will not work on other controllers. The MAP80 for example +; doesn't have the same ROM arrangement and loads sector 0 into +; 0x0C00, needs 8080 at the start then jumps to n + 2 +; + .area ASEG(ABS) + .org 0x0100 + .byte 'N' + .byte 'C' + .byte 'B' + +boot: + ld hl,#0x0200 ; Load address of image +diskloop: + ld a,(0xfc76) ; sector 0-9 + inc a + cp #10 + call nz, newtrack + ld (0xfc76),a + push hl + call 0xf43d ; read block + pop hl + inc h + inc h + ld a,#0xE8 + cp h + jr nz, diskloop + jp 0x200 +newtrack: + ld a,(0xfc75) + inc a + ld (0xfc75),a + xor a + ret + + \ No newline at end of file diff --git a/Kernel/platform-nascom/nascom-pagemode.s b/Kernel/platform-nascom/nascom-pagemode.s new file mode 100644 index 00000000..a18dffac --- /dev/null +++ b/Kernel/platform-nascom/nascom-pagemode.s @@ -0,0 +1,166 @@ +; +; NASCOM/Gemini 'page mode' banking +; + + .module nascom-pagemode + + ; exported symbols + .globl init_hardware + .globl map_kernel + .globl map_process + .globl map_process_a + .globl map_process_always + .globl map_save + .globl map_restore + + ; imported symbols + .globl _program_vectors + .globl _ramsize + .globl _procmem + .globl _bankmap + + .globl s__COMMONMEM + .globl l__COMMONMEM + + .include "kernel.def" + .include "../kernel.def" + +; +; We keep the probe routine in the common copy area as we need +; to bank flip as we probe +; + .area _COMMONMEM +banktest: + out (0xFF),a + ld a,#0xAA + cp (hl) + ret nz + inc (hl) + ld (de),a ; any old load, just cause bus traffic + inc a + cp (hl) + ret + +size_ram: + ld hl,#0xE7FF + ld de,#0xE7FE + ld a,#0xF1 + out (0xFF),a ; write all + ; Now test each bank and see who is present. There can be holes + xor a + ld c,a + ld b,a + ld (de),a + ld a,#0x11 +size_loop: + call banktest + jr z,nobank + set 0,c ; mask of banks present + inc b ; count of banks present +nobank: + sla a + sla c + jr nc,size_loop + ld a,#0x11 + out (0xFF),a ; back to kernel map + srl c + ld (_bankmap),bc ; count and mask + ; Compute 64 * B + ld hl,#0 + srl b + rr l + srl b + rr l + ld h,b + ret + + .area _CODE + +init_hardware: + ld a,#0xF1 ; Write all banks read kernel + out (0xFF),a + ; set up interrupt vectors for the kernel (also sets up common memory in page 0x000F which is unused) + ld hl, #s__COMMONMEM + ld d,h + ld e,l + ld bc, #l__COMMONMEM + ldir ; Common copy into each bank + ld a,#0x11 + out (0xFF),a + ; Only now is it safe to call into common space + call size_ram + ld (_ramsize), hl + ld de, #64 ; for kernel + or a + sbc hl, de + ld (_procmem), hl + ld hl,#0 + call _program_vectors +; +; Need to work out how we find the right IRQ source +; + ret + + +;------------------------------------------------------------------------------ +; COMMON MEMORY PROCEDURES FOLLOW + + .area _COMMONDATA + +pagereg: .db 0x11 ; bank 1 R/W +pagesave: .db 0x11 ; saved copy + + .area _COMMONMEM +; +; Mapping set up for the Nascom 'Page mode' +; +; The kernel is in bank 1, the user processes in the other banks. We +; take care to keep common writables in COMMONDATA which is the area +; of unbanked memory. +; +map_kernel: + push af + ld a,#0x11 + ld (pagereg),a + out (0xFF), a + pop af + ret +; +; Userspace mapping is mode 3, U64K/L32 mapped at L64K/L32 +; Mapping codes 0x63 / 0x73. 0x94 on a bank expanded TRS80 then +; selects how the upper bank decodes +; +map_process: + ld a, h + or l + jr z, map_kernel + ld a, (hl) +map_process_a: ; used by bankfork + ld (pagereg),a + out (0xFF),a + ret + +map_process_always: + push af + push hl + ld hl, #U_DATA__U_PAGE + ld a, (hl) + ld (pagereg),a + out (0xFF),a + pop hl + pop af + ret + +map_save: push af + ld a,(pagereg) + ld (pagesave), a + pop af + ret + +map_restore: + push af + ld a, (pagesave) + ld (pagereg), a + out (0xFF), a + pop af + ret diff --git a/Kernel/platform-nascom/nascom.h b/Kernel/platform-nascom/nascom.h new file mode 100644 index 00000000..2971ba6d --- /dev/null +++ b/Kernel/platform-nascom/nascom.h @@ -0,0 +1,4 @@ +extern uint8_t io_page; +extern uint16_t swap_dev; +extern uint8_t nmikey; + diff --git a/Kernel/platform-nascom/nascom.s b/Kernel/platform-nascom/nascom.s new file mode 100644 index 00000000..1bd30d74 --- /dev/null +++ b/Kernel/platform-nascom/nascom.s @@ -0,0 +1,135 @@ +; +; Nascom hardware support +; + + .module nascom + + ; exported symbols + .globl init_early + .globl init_hardware + .globl interrupt_handler + .globl _program_vectors + .globl platform_interrupt_all + .globl _nmikey + + ; exported debugging tools + .globl _platform_monitor + .globl _platform_reboot + .globl outchar + + ; imported symbols + .globl _ramsize + .globl _procmem + .globl istack_top + .globl istack_switched_sp + .globl unix_syscall_entry + .globl outcharhex + .globl null_handler + .globl map_kernel + .globl map_process + .globl map_process_a + .globl map_process_always + .globl map_save + .globl map_restore + + .globl s__COMMONMEM + .globl l__COMMONMEM + + .globl _bufpool + + .include "kernel.def" + .include "../kernel.def" + + .area _BUFFERS + +_bufpool: + .ds BUFSIZE * NBUFS +; ----------------------------------------------------------------------------- +; COMMON MEMORY BANK (0xE800 upwards) +; ----------------------------------------------------------------------------- + .area _COMMONMEM + +platform_interrupt_all: + ret + +_platform_monitor: +_platform_reboot: + di + jr _platform_reboot + +; ----------------------------------------------------------------------------- +; KERNEL MEMORY BANK (below 0xE800, only accessible when the kernel is mapped) +; ----------------------------------------------------------------------------- + .area _CODE + +init_early: + 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 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 + + ld (0x0000), a + ld hl, #null_handler ; to Our Trap Handler + ld (0x0001), hl + + ; Some Nascoms got fitted with an NMI button - what should it do + ; ? + ld (0x0066), a ; Set vector for NMI + ld hl, #nmi_key_handler + ld (0x0067), hl + jp map_kernel + +; +; This isn't absolutely perfect but as close as we can get +; +nmi_key_handler: + push af + ld a,#1 + ld (_nmikey),a + pop af + retn + +; outchar: Wait for UART TX idle, then print the char in A +; destroys: AF +outchar: + push af +outwait: + in a, (0x02) + bit 6,a + jr z, outwait + out (0x1), a + ret + + .area _COMMONDATA +_nmikey: .byte 0 diff --git a/Kernel/platform-nascom/target.mk b/Kernel/platform-nascom/target.mk new file mode 100644 index 00000000..3bffcde0 --- /dev/null +++ b/Kernel/platform-nascom/target.mk @@ -0,0 +1 @@ +export CPU = z80 diff --git a/Kernel/platform-nascom/tricks.s b/Kernel/platform-nascom/tricks.s new file mode 100644 index 00000000..ee50c077 --- /dev/null +++ b/Kernel/platform-nascom/tricks.s @@ -0,0 +1,5 @@ + + .include "../kernel.def" + .include "kernel.def" + + .include "../lib/z80fixedbank.s"