nascom: Initial work in progress code (not complete)
authorAlan Cox <alan@linux.intel.com>
Sat, 21 Jul 2018 12:03:36 +0000 (13:03 +0100)
committerAlan Cox <alan@linux.intel.com>
Sat, 21 Jul 2018 12:03:36 +0000 (13:03 +0100)
These are the initial pieces to support the Lucas/Nascom 80-bus systems. There
is a lot to do before this is remotely useful.

28 files changed:
Kernel/platform-nascom/Makefile [new file with mode: 0644]
Kernel/platform-nascom/PortMap [new file with mode: 0644]
Kernel/platform-nascom/README [new file with mode: 0644]
Kernel/platform-nascom/commonmem.s [new file with mode: 0644]
Kernel/platform-nascom/config.h [new file with mode: 0644]
Kernel/platform-nascom/crt0.s [new file with mode: 0644]
Kernel/platform-nascom/devgm833.c [new file with mode: 0644]
Kernel/platform-nascom/devgm833.h [new file with mode: 0644]
Kernel/platform-nascom/devgm8x9.c [new file with mode: 0644]
Kernel/platform-nascom/devgm8x9.h [new file with mode: 0644]
Kernel/platform-nascom/devices.c [new file with mode: 0644]
Kernel/platform-nascom/devinput.c [new file with mode: 0644]
Kernel/platform-nascom/devinput.h [new file with mode: 0644]
Kernel/platform-nascom/devnascom.c [new file with mode: 0644]
Kernel/platform-nascom/devtty.h [new file with mode: 0644]
Kernel/platform-nascom/discard.c [new file with mode: 0644]
Kernel/platform-nascom/gm833.s [new file with mode: 0644]
Kernel/platform-nascom/gm849a_sasi.s [new file with mode: 0644]
Kernel/platform-nascom/gm8x9.s [new file with mode: 0644]
Kernel/platform-nascom/henelec.s [new file with mode: 0644]
Kernel/platform-nascom/kernel.def [new file with mode: 0644]
Kernel/platform-nascom/main.c [new file with mode: 0644]
Kernel/platform-nascom/nasboot.s [new file with mode: 0644]
Kernel/platform-nascom/nascom-pagemode.s [new file with mode: 0644]
Kernel/platform-nascom/nascom.h [new file with mode: 0644]
Kernel/platform-nascom/nascom.s [new file with mode: 0644]
Kernel/platform-nascom/target.mk [new file with mode: 0644]
Kernel/platform-nascom/tricks.s [new file with mode: 0644]

diff --git a/Kernel/platform-nascom/Makefile b/Kernel/platform-nascom/Makefile
new file mode 100644 (file)
index 0000000..e292a32
--- /dev/null
@@ -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 (file)
index 0000000..5500706
--- /dev/null
@@ -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 (file)
index 0000000..4f9bab3
--- /dev/null
@@ -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 (file)
index 0000000..447d11d
--- /dev/null
@@ -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 (file)
index 0000000..9824ccb
--- /dev/null
@@ -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 (file)
index 0000000..046bd80
--- /dev/null
@@ -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 (file)
index 0000000..cdd3683
--- /dev/null
@@ -0,0 +1,146 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devgm833.h>
+#include <nascom.h>
+
+/*
+ *     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 (file)
index 0000000..273bd61
--- /dev/null
@@ -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 (file)
index 0000000..c764d90
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ *     Driver for the GM809/829/849/849A floppy controllers
+ *     (The SASI/SCSI has its own driver)
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <nascom.h>
+#include <devgm8x9.h>
+
+__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 (file)
index 0000000..689e742
--- /dev/null
@@ -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 (file)
index 0000000..85cb004
--- /dev/null
@@ -0,0 +1,41 @@
+#include <kernel.h>
+#include <tty.h>
+#include <version.h>
+#include <kdata.h>
+#include <devgm833.h>
+#include <devgm8x9.h>
+#include <devsys.h>
+#include <vt.h>
+#include <devtty.h>
+#include <blkdev.h>
+#include <devscsi.h>
+
+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 (file)
index 0000000..a646262
--- /dev/null
@@ -0,0 +1,44 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <input.h>
+#include <devinput.h>
+
+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 (file)
index 0000000..5d79a9e
--- /dev/null
@@ -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 (file)
index 0000000..f94bb26
--- /dev/null
@@ -0,0 +1,242 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <vt.h>
+#include <devtty.h>
+#include <input.h>
+#include <devinput.h>
+#include <stdarg.h>
+#include <nascom.h>
+
+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 (file)
index 0000000..43c8649
--- /dev/null
@@ -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 (file)
index 0000000..e977c6e
--- /dev/null
@@ -0,0 +1,60 @@
+#include <kernel.h>
+#include <devtty.h>
+#include <devscsi.h>
+#include <devgm833.h>
+#include <printf.h>
+#include <blkdev.h>
+#include <tty.h>
+#include <nascom.h>
+
+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 (file)
index 0000000..34dd8a4
--- /dev/null
@@ -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 (file)
index 0000000..8d54abe
--- /dev/null
@@ -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 (file)
index 0000000..dfb9176
--- /dev/null
@@ -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 (file)
index 0000000..8062fd2
--- /dev/null
@@ -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 (file)
index 0000000..0caf051
--- /dev/null
@@ -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 (file)
index 0000000..6ceee02
--- /dev/null
@@ -0,0 +1,60 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+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 (file)
index 0000000..3d7594a
--- /dev/null
@@ -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 (file)
index 0000000..a18dffa
--- /dev/null
@@ -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 (file)
index 0000000..2971ba6
--- /dev/null
@@ -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 (file)
index 0000000..1bd30d7
--- /dev/null
@@ -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 (file)
index 0000000..3bffcde
--- /dev/null
@@ -0,0 +1 @@
+export CPU = z80
diff --git a/Kernel/platform-nascom/tricks.s b/Kernel/platform-nascom/tricks.s
new file mode 100644 (file)
index 0000000..ee50c07
--- /dev/null
@@ -0,0 +1,5 @@
+
+       .include "../kernel.def"
+       .include "kernel.def"
+
+       .include "../lib/z80fixedbank.s"