--- /dev/null
+
+CSRCS =
+DISCARD_CSRCS = discard.c devhd_discard.c
+NSRCS = ../dev/net/net_native.c
+
+ASRCS = trs80.s trs80-bank.s crt0.s
+ASRCS += tricks.s commonmem.s floppy.s ide.s
+
+CSRCS = devfd.c devhd.c
+CSRCS += devices.c main.c devinput.c
+CSRCS += devlpr.c devtty.c devgfx.c
+DSRCS = ../dev/blkdev.c ../dev/devide.c ../dev/mbr.c
+DISCARD_DSRCS = ../dev/devide_discard.c
+
+COBJS = $(CSRCS:.c=.rel)
+DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS))
+AOBJS = $(ASRCS:.s=.rel)
+NOBJS = $(patsubst ../dev/net/%.c,%.rel, $(NSRCS))
+DISCARD_COBJS = $(DISCARD_CSRCS:.c=.rel)
+DISCARD_DOBJS = $(patsubst ../dev/%.c,%.rel, $(DISCARD_DSRCS))
+OBJS = $(COBJS) $(AOBJS) $(DISCARD_COBJS) $(DOBJS) $(DISCARD_DOBJS) $(NOBJS)
+
+CROSS_CCOPTS += -I../dev/
+
+all: $(OBJS) trs80load.bin
+
+$(COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG1) -c $<
+
+$(AOBJS): %.rel: %.s
+ $(CROSS_AS) $(ASOPTS) $<
+
+$(DISCARD_COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+$(DOBJS): %.rel: ../dev/%.c
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG2) -c $<
+
+$(DISCARD_DOBJS): %.rel: ../dev/%.c
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+$(NOBJS): %.rel: ../dev/net/%.c
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG2) -c $<
+
+clean:
+ rm -f *.rel *.lst *.asm *.lst *.sym *.adb *.rst *.ihx core *~ boot.raw
+
+image: trs80load.bin
+ dd if=/dev/zero of=boot.raw bs=256 count=400
+ # Boot block at 0,0
+ dd if=trs80load.bin of=boot.raw bs=256 count=1 conv=notrunc
+ # With the image straight afterwards
+ dd if=../fuzix.bin of=boot.raw bs=256 skip=1 conv=notrunc
+ ../tools/makejv3 -s -t sd40s -d boot.raw boot.jv3
+
+trs80load.bin: trs80load.s
+ sdasz80 -o trs80load.s
+ sdldz80 -i trs80load.rel
+ makebin -s 17152 trs80load.ihx trs80load.tmp
+ dd if=trs80load.tmp of=trs80load.bin bs=256 skip=66 count=1
--- /dev/null
+Video Genie With EG 64 banking option, or the TRS80 'Lubomir Soft Banker'
+clone of the EG 64
+
+This port supports the following configurations currently
+
+ Video Genie & Video Genie I/II / Dick Smith System 80 MkI & MkII /
+ PMC-80/81 / TRZ-80 with EG3014/X-4010/X-4020 and EG64
+
+ Options:
+ Floppy Disk (somewhat basic support, no format tool yet)
+ Lower case mod or built in lower case
+ EG3016 printer interface
+ EG3020 RS232 interface
+ EG3022 S100 adapter (but no specific card drivers)
+ Percom compatible double density kit
+ HRG1B Graphics Card
+ Lowe Electronics LE18 graphics adapter
+ Tandy style RTC at 0xB0 (only usable for time locking)
+ Anything on the model I list that works with a Genie/System-80
+ to TRS80 expansion convertor (*)
+ IDE CF at 0x40
+
+ Planned:
+ Lowe Electronics FRED
+ Tandy compatible double density kit
+
+ Unsupported:
+ TRS80 mapped serial/printer port on Video Genie (BUG)
+ Genie IIs/III: These are CP/M capable systems with a different
+ memory model.
+ Any non memory S100 cards that don't match the TRS80/VG
+ devices (ie the X-4010 S100 printer card should work)
+
+ TRS80 model I with the Lubomir Soft Banker clone of the EG64
+
+ Options:
+ Floppy Disk (somewhat basic support, no format tool yet)
+ RS-232-C Interface(26-1145, or compatible)
+ Hard Disk (Tandy compatible 26-1132)
+ Lower Case Kit (either Tandy 26-1104 or the simple mods)
+ Percom Compatible Doubler
+ Holmes style speed up board (anything using port 254 bit 0)
+ Real Time Clock (supported for time locking, can't do dates as
+ is not Y2K capable, must be at 0xB0)
+ HRG1B Graphics Card
+ Alpha Products Joystick
+ ChromaTRS (as joystick and a graphics device only - need a VDP
+ emulation in xtrs to do console support)
+ Lo-tech or similar IDE CF at 0x40
+ Tandy HRG (user defined graphics 26-9800) [boot opt micro]
+ Orcim PCG80 [boot opt pcg80]
+ Progamma Intl. 80 Grafix [boot opt 80gfx]
+
+ Planned:
+ Orchestra 80 sound card
+ Tandy Model I Double Density Kit (26-1143)
+ Support for the FreHD extra features (clock, volume switches)
+
+ Unsupported:
+ M1SE/M1RE/FreHD (except as far as compatibility features go)
+
+Generally Not Supported:
+ Alpha Supermem and other big bankers (use the trs80m1 port)
+ Various 64K only CP/M adapters
+ (Unless someone knows one that stacks with the supermem!)
+
+Would Be Nice To Do Better
+ 80-Grafix - need docs, example apps to debug
+ an emulator
+ TRS80 Model 1 Hires - The UK 'hires' actually a font
+ adapter also needs emulation support
+
+User Space:
+ The TRS80 Model I/III have ROM in the low 16K. The EG 64 allows this
+ space to be mapped out and the upper 32K to be bank switched. Because
+ the switching is in the upper 32K binaries must be linked at 0x8000
+ as with the TRS80 model 1 port. This limit should go away when binaries
+ become relocatable
+
+To Do:
+ Faster user copiers once it works
+ Fast block copy routines for uget/uput
+ Hint based pre-fetching uget
+ When we have > 512K or so RAM use the rest as a ramdisc
+ Orchestra
+ M3SE video at least
+
+Memory Map:
+ 0000-3BFF Kernel data and boot area
+ 37C0-3FFF I/O and ROM
+ 4000-7FFF Kernel code
+ 8000-FFFF Kernel code and some data/bss areas
+
+ 8000-FFFF User space
+
+ Note that the current build is an over featured one. Turning off
+ the IDE support will shrink it loads/
+
+ Unfortunately right now some core changes are needed to get to the
+ point we can have configurable options done properly.
+
+Floppy boot requires a single density disk. The Level II ROM reads
+disk 0 side 0 track 0 sector 0 (TRS80 disks are 0 offset sector count)
+into 4200-42FF and then does a JP 4200 (stack is around 407D)
--- /dev/null
+/* Set if you want RTC support and have an RTC on ports 0xB0-0xBC */
+#define CONFIG_RTC
+#define CONFIG_RTC_FULL
+/* 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
+/* Memory set up */
+#define CONFIG_SWAP_ONLY
+#define CONFIG_SPLIT_UDATA
+#define UDATA_BLKS 1
+#define UDATA_SIZE 0x200
+/* Direct I/O support */
+#define CONFIG_LARGE_IO_DIRECT
+/* Raw input layer */
+#define CONFIG_INPUT
+/* Full keycode level grabbing supported */
+#define CONFIG_INPUT_GRABMAX 3
+/* And our buffer pool is dynamically sized */
+#define CONFIG_DYNAMIC_BUFPOOL
+/* And IDE */
+#define MAX_BLKDEV 2
+#define CONFIG_IDE
+
+#define MAP_SIZE 0x8000
+
+#define CONFIG_BANKS 1 /* 32K */
+
+/* Vt definitions */
+#define CONFIG_VT_SIMPLE
+#define VT_BASE ((uint8_t *)0x3C00)
+#define VT_MAP_CHAR(x) vt_map_char(x)
+#define VT_WIDTH 64
+#define VT_HEIGHT 16
+#define VT_RIGHT 63
+#define VT_BOTTOM 15
+
+extern unsigned char vt_map_char(unsigned char);
+
+/* Keyboard bitmap definitions */
+#define KEY_ROWS 8
+#define KEY_COLS 8
+
+#define TICKSPERSEC 40 /* Ticks per second */
+#define PROGBASE 0x8000 /* Base of user */
+#define PROGLOAD 0x8000 /* Load and run here */
+#define PROGTOP 0xFFFF /* Top of program */
+#define PROC_SIZE 32 /* Memory needed per process */
+
+#define SWAP_SIZE 0x41 /* 32.5K in blocks */
+#define SWAPBASE 0x8000 /* We swap the lot in one, include the */
+#define SWAPTOP 0x10000UL /* vectors so its a round number of sectors */
+
+#define MAX_SWAPS 16 /* Should be plenty (512K!) */
+
+#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 */
+
+/* Device parameters */
+#define NUM_DEV_TTY 3
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+#define SWAPDEV (swap_dev) /* Device for swapping (dynamic). */
+#define NBUFS 4 /* Number of block buffers - keep in sync with asm! */
+#define NMOUNTS 4 /* Number of mounts at a time */
+
+extern void platform_discard(void);
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+
+#define MAX_FD 4
+
+#define OPDIR_NONE 0
+#define OPDIR_READ 1
+#define OPDIR_WRITE 2
+
+#define FD_READ 0x80 /* 2797 needs 0x88, 1797 needs 0x80 */
+#define FD_WRITE 0xA0 /* Likewise A8 v A0 */
+
+static uint8_t motorct;
+
+/* Extern as they live in common */
+extern uint8_t fd_map, fd_tab[MAX_FD];
+extern uint8_t fd_selected;
+extern uint8_t fd_cmd[6];
+
+/*
+ * We only support normal block I/O for the moment. We do need to
+ * add swapping!
+ */
+static uint8_t selmap[4] = { 0x01, 0x02, 0x04, 0x08 };
+
+static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ int ct = 0;
+ int tries;
+ uint8_t err = 0;
+ uint8_t *driveptr = fd_tab + minor;
+
+ if(rawflag == 2)
+ goto bad2;
+
+ /* FIXME: We force DD for now */
+ err = fd_motor_on(selmap[minor]|0x80);
+ if (err)
+ goto bad;
+
+ if (*driveptr == 0xFF)
+ fd_reset(driveptr);
+
+ fd_map = rawflag;
+ if (rawflag && d_blkoff(BLKSHIFT))
+ return -1;
+
+ udata.u_nblock *= 2;
+
+ fd_cmd[0] = is_read ? FD_READ : FD_WRITE;
+ fd_cmd[1] = udata.u_block / 9; /* 2 sectors per block */
+ fd_cmd[2] = ((udata.u_block % 9) << 1) + 1; /*eww.. */
+ fd_cmd[3] = is_read ? OPDIR_READ: OPDIR_WRITE;
+ fd_cmd[4] = ((uint16_t)udata.u_dptr) & 0xFF;
+ fd_cmd[5] = ((uint16_t)udata.u_dptr) >> 8;
+
+ while (ct < udata.u_nblock) {
+ for (tries = 0; tries < 4 ; tries++) {
+ err = fd_operation(driveptr);
+ if (err == 0)
+ break;
+ if (tries > 1)
+ fd_reset(driveptr);
+ }
+ /* FIXME: should we try the other half and then bale out ? */
+ if (tries == 4)
+ goto bad;
+ fd_cmd[5]++; /* Move on 256 bytes in the buffer */
+ fd_cmd[2]++; /* Next sector for 2nd block */
+ ct++;
+ }
+ return udata.u_nblock << 8;
+bad:
+ kprintf("fd%d: error %x\n", minor, err);
+bad2:
+ udata.u_error = EIO;
+ return -1;
+}
+
+int fd_open(uint8_t minor, uint16_t flag)
+{
+ flag;
+ if(minor >= MAX_FD) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return 0;
+}
+
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return fd_transfer(minor, true, rawflag);
+}
+
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;rawflag;minor;
+ return fd_transfer(minor, false, rawflag);
+}
--- /dev/null
+#ifndef __DEVFD_DOT_H__
+#define __DEVFD_DOT_H__
+
+/* public interface */
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int fd_open(uint8_t minor, uint16_t flag);
+
+/* low level interface */
+uint16_t fd_reset(uint8_t *driveptr);
+uint16_t fd_operation(uint8_t *driveptr);
+uint16_t fd_motor_on(uint16_t drivesel);
+uint16_t fd_motor_off(uint16_t driveptr);
+
+#endif /* __DEVFD_DOT_H__ */
--- /dev/null
+/*
+ * Graphics logic for the TRS80 model I, III and friends
+ *
+ * - Need to figure out how to interact with console switches
+ * - Need to add LNW80 graphics modes
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <tty.h>
+#include <vt.h>
+#include <graphics.h>
+#include <devgfx.h>
+#include "trs80.h"
+
+__sfr __at 0x00 hrg_off;
+__sfr __at 0x04 hrg_data;
+__sfr __at 0x79 vdps;
+__sfr __at 0x7C chromajs0;
+__sfr __at 0x82 gfx_data;
+__sfr __at 0x83 gfx_ctrl;
+__sfr __at 0xEC le18_data;
+__sfr __at 0xEF le18_ctrl;
+__sfr __at 0xFF ioctrl;
+
+uint8_t trs80_udg;
+
+uint8_t has_hrg1;
+uint8_t has_chroma; /* and thus 2 joystick ports */
+uint8_t video_mode;
+static uint8_t max_mode = 0;
+
+static struct display trsdisplay[6] = {
+ /* Text mode */
+ {
+ 0,
+ 128, 48,
+ 64, 16,
+ 255, 255,
+ FMT_6PIXEL_128,
+ HW_UNACCEL,
+ GFX_MAPPABLE|GFX_MULTIMODE|GFX_TEXT,
+ 1,
+ 0
+ },
+ /* Tandy Graphics */
+ {
+ 1,
+ 640, 240,
+ 640, 240,
+ 255, 255,
+ FMT_MONO_WB,
+ HW_TRS80GFX,
+ GFX_MULTIMODE|GFX_MAPPABLE, /* No overlay control on Model 3 */
+ 32,
+ 0 /* For now lets just worry about map */
+ },
+ /* Microlabs Grafyx */
+ {
+ 1,
+ 512, 192,
+ 512, 192,
+ 255, 255,
+ FMT_MONO_WB,
+ HW_MICROLABS,
+ GFX_MULTIMODE|GFX_MAPPABLE,
+ 12,
+ 0
+ },
+ /* HRG1B */
+ {
+ 1,
+ 384, 192,
+ 384, 192,
+ 255, 255,
+ FMT_MONO_WB,
+ HW_HRG1B,
+ GFX_MULTIMODE|GFX_MAPPABLE|GFX_TEXT,
+ 9,
+ 0
+ },
+ /* CHROMAtrs */
+ {
+ 1,
+ 256, 192,
+ 256, 192,
+ 255, 255,
+ FMT_VDP,
+ HW_VDP_9918A,
+ GFX_MULTIMODE|GFX_MAPPABLE, /* We don't support it as a console yet */
+ 16,
+ 0
+ },
+ /* Lowe Electronics LE-18 */
+ {
+ 1,
+ 384, 192,
+ 384, 192,
+ 255, 255,
+ FMT_MONO_WB,
+ HW_LOWE_LE18,
+ GFX_MULTIMODE|GFX_MAPPABLE|GFX_TEXT,
+ 16,
+ 0
+ }
+};
+
+static struct videomap trsmap[6] = {
+ {
+ 0,
+ 0,
+ 0x3C00,
+ 0x0400, /* Directly mapped */
+ 0, 0, /* No segmentation */
+ 1, /* Standard spacing */
+ MAP_FBMEM_SIMPLE|MAP_FBMEM
+ },
+ /* Tandy board */
+ {
+ 0,
+ 0x80, /* I/O ports.. 80-83 + 8C-8E */
+ 0, 0, /* Not mapped into main memory */
+ 0, 0, /* No segmentation */
+ 1, /* Standard spacing */
+ MAP_PIO
+ },
+ /* Microlabs */
+ {
+ 0,
+ 0xFF,
+ 0x3C00, 0x0400,
+ 0, 0,
+ 1,
+ MAP_FBMEM|MAP_PIO
+ },
+ /* HRG1B */
+ {
+ 0,
+ 0x00, /* I/O ports.. 0-5 */
+ 0, 0, /* Not mapped into main memory */
+ 0, 0, /* No segmentation */
+ 1, /* Standard spacing */
+ MAP_PIO
+ },
+ /* CHROMAtrs */
+ {
+ 0,
+ 0x78, /* I/O ports 0x78/79 */
+ 0, 0,
+ 0, 0,
+ 1,
+ MAP_PIO /* VDP is I/O mapped */
+ },
+ /* Lowe Electronics LE-18 */
+ {
+ 0,
+ 0xEC, /* I/O ports 0xEC-0xEF */
+ 0, 0,
+ 0, 0,
+ 1,
+ MAP_PIO /* VDP is I/O mapped */
+ }
+};
+
+static uint8_t displaymap[4] = {0, 0, 0, 0};
+
+static int8_t udg_ioctl(uarg_t arg, char *ptr);
+
+/* TODO: Arbitrate graphics between tty 1 and tty 2 */
+int gfx_ioctl(uint8_t minor, uarg_t arg, char *ptr)
+{
+ uint8_t m;
+
+ if (minor <= 2 && (arg == VTFONTINFO || arg == VTSETFONT || arg == VTSETUDG))
+ return udg_ioctl(arg, ptr);
+
+ if (minor > 2 || (arg >> 8 != 0x03))
+ return vt_ioctl(minor, arg, ptr);
+
+ switch(arg) {
+ case GFXIOC_GETINFO:
+ return uput(&trsdisplay[video_mode], ptr, sizeof(struct display));
+ case GFXIOC_GETMODE:
+ case GFXIOC_SETMODE:
+ m = ugetc(ptr);
+ if (m > max_mode)
+ break;
+ m = displaymap[m];
+ if (arg == GFXIOC_GETMODE)
+ return uput(&trsdisplay[m], ptr, sizeof(struct display));
+ video_mode = m;
+ /* Going back to text mode we need to turn off graphics cards. Going the
+ other way we let the applications handle it as they may want to do
+ memory wipes and the like first */
+ if (video_mode == 0) {
+ if (displaymap[1] == 1)
+ gfx_ctrl = 0;
+ else if (displaymap[1] == 2)
+ ioctrl = 0x20;
+ else if (displaymap[1] == 3)
+ hrg_off = 1;
+ else if (displaymap[1] == 5)
+ le18_ctrl = 0;
+ }
+ return 0;
+ case GFXIOC_UNMAP:
+ return 0;
+ /* Users can "map" 8) the framebuffer into their process and use the
+ card directly */
+ case GFXIOC_MAP:
+ return uput(&trsmap[video_mode], ptr, sizeof(struct videomap));
+ }
+ return -1;
+}
+
+void gfx_init(void)
+{
+ if (trs80_model == TRS80_MODEL1 || trs80_model == VIDEOGENIE) {
+ /* HRG1B support. Might be good to also support 80-Grafix as a UDG
+ module */
+ if (hrg_data != 0xFF) { /* We ought to test more carefully */
+ max_mode = 1;
+ displaymap[1] = 3;
+ has_hrg1 = 1;
+ }
+ /* LE-18 */
+ if(trs80_model == VIDEOGENIE && le18_data != 0xFF) {
+ displaymap[++max_mode] = 5;
+ trsdisplay[5].mode = max_mode;
+ }
+ } else if (trs80_model == TRS80_MODEL3) {
+ /* The model 3 might have an 80-Grafix UDG card, or a Graphyx
+ or a Tandy card */
+ uint8_t *fb = (uint8_t *)0x3C00;
+ uint8_t c = *fb;
+ *fb = 128;
+ ioctrl = 0xB2;
+ if (*fb != 128) {
+ /* Hopeful */
+ *fb = 128;
+ if (*fb == 128) { /* 80 Grafix only has 6bit wide RAM but wants
+ the top bit set on writes.. */
+ displaymap[1] = 2;
+ max_mode = 1;
+ } /* else add UDG support FIXME */
+ }
+ ioctrl = 0x20;
+ *fb = c;
+ if (max_mode == 0 && gfx_data != 0xFF) {
+ max_mode = 1;
+ displaymap[1] = 1;
+ }
+ }
+ /* It's possible to have a CHROMAtrs and other adapters as it fits
+ on the external bus. 70-7C is also a common location for RTC clocks
+ which makes detection trickier. The clock will show 0 in the upper
+ bits, the joystick port will not */
+ if (vdps != 0xFF && (chromajs0 & 0xC0) == 0xC0) {
+ displaymap[++max_mode] = 4;
+ trsdisplay[4].mode = max_mode;
+ has_chroma = 1;
+ }
+}
+
+/*
+ * UDG drivers
+ *
+ * We don't currently address UDG fitted without lower case. That gets
+ * deeply weird because you get disjoint ranges with one bit magically
+ * invented by logic. For the UDG only ones we just halve the range, but
+ * for the PCG80 what about full font ?
+ *
+ * (The Tandy (Microfirma) one requires lowercase is fitted according
+ * to the manual)
+ *
+ * One other corner case to consider is that if you have no lower case
+ * we ought to requisition the UDG and load a font into it then use
+ * 128-159 for lower case.
+ */
+
+static struct fontinfo fonti[4] = {
+ { 0, },
+ { 0, 255, 0, 255, FONT_INFO_6X12P16 },
+ { 128, 191, 128, 191, FONT_INFO_6X12P16 },
+ { 128, 255, 128, 255, FONT_INFO_6X12P16 }
+};
+
+__sfr __at 130 trshg_nowrite;
+__sfr __at 140 trshg_write;
+__sfr __at 150 trshg_gfxoff;
+__sfr __at 155 trshg_gfxon;
+__sfr __at 0xFE pcg80;
+__sfr __at 0xFF p80gfx;
+
+static uint8_t old_pcg80;
+static uint8_t old_p80gfx;
+static uint8_t udgflag; /* So we know what fonts are loaded */
+#define SOFTFONT_UDG 1
+#define SOFTFONT_ALL 2
+
+static void load_char_pcg80(uint8_t ch, uint8_t *cdata)
+{
+ uint8_t bank = ch >> 6;
+ uint8_t *addr = (uint8_t *)0x3C00 + ((ch & 0x3F) << 4);
+ uint8_t i;
+
+ pcg80 = old_pcg80 | 0x60 | bank; /* Programming mode on */
+
+ for (i = 0; i < 16; i++)
+ *addr++ = *cdata++ << 1 | 0x80;
+ pcg80 = old_pcg80;
+}
+
+static void load_char_80gfx(uint8_t ch, uint8_t *cdata)
+{
+ uint8_t *addr = (uint8_t *)0x3C00 + ((ch & 0x3F) << 4);
+ uint8_t i;
+
+ p80gfx = old_p80gfx | 0x60;
+ for (i = 0; i < 16; i++)
+ *addr++ = *cdata++ << 1 | 0x80;
+ p80gfx = old_p80gfx;
+}
+
+static void load_char_trs(uint8_t ch, uint8_t *cdata)
+{
+ uint8_t *addr = (uint8_t *)0x3C00 + ((ch & 0x3F) << 4);
+ uint8_t i;
+
+ trshg_write = 1;
+ for (i = 0; i < 16; i++)
+ *addr++ = *cdata++ << 1 | 0x80;
+ trshg_nowrite = 1;
+}
+
+void (*load_char[4])(uint8_t, uint8_t *) = {
+ NULL,
+ load_char_pcg80,
+ load_char_80gfx,
+ load_char_trs,
+};
+
+static void udg_config(void)
+{
+ switch(trs80_udg) {
+ case UDG_PCG80:
+ if (udgflag & SOFTFONT_UDG)
+ old_pcg80 |= 0x80; /* 128-255 soft font */
+ if (udgflag & SOFTFONT_ALL)
+ old_pcg80 |= 0x88;
+ pcg80 = old_pcg80 | 0x20;
+ break;
+ case UDG_80GFX:
+ if (udgflag & SOFTFONT_UDG)
+ old_p80gfx |= 0x80;
+ p80gfx = old_p80gfx | 0x20;
+ break;
+ case UDG_MICROFIRMA:
+ if (udgflag & SOFTFONT_UDG)
+ trshg_gfxon = 1;
+ break;
+ }
+}
+
+static int8_t udg_ioctl(uarg_t arg, char *ptr)
+{
+ uint8_t base = fonti[trs80_udg].udg_low;
+ uint8_t limit = fonti[trs80_udg].udg_high;
+ int i;
+
+ /* Not supported */
+ if (trs80_udg == UDG_NONE) {
+ udata.u_error = EOPNOTSUPP;
+ return -1;
+ }
+ /* No lower case available */
+ if (!video_lower)
+ limit = 191;
+
+ switch(arg) {
+ case VTFONTINFO:
+ return uput(fonti + trs80_udg, ptr, sizeof(struct fontinfo));
+ case VTSETFONT:
+ base = fonti[trs80_udg].font_low;
+ limit = fonti[trs80_udg].font_high;
+ /* Fall through */
+ case VTSETUDG:
+ for (i = base; i <= limit; i++) {
+ uint8_t c[16];
+ if (uget(c, ptr, 16) == -1)
+ return -1;
+ ptr += 16;
+ load_char[trs80_udg](i, c);
+ }
+ if (arg == VTSETUDG)
+ udgflag |= SOFTFONT_UDG;
+ else
+ udgflag |= SOFTFONT_ALL;
+ udg_config();
+ return 0;
+ }
+ return -1;
+}
--- /dev/null
+#ifndef _DEV_GFX_H
+#define _DEV_GFX_H
+
+#include <graphics.h>
+
+extern int gfx_ioctl(uint8_t minor, uarg_t arg, char *ptr);
+extern void gfx_init(void);
+
+extern uint8_t video_mode;
+extern uint8_t has_hrg1;
+extern uint8_t has_chroma;
+
+#endif
--- /dev/null
+/*
+ * WD1010 hard disk driver
+ */
+
+#define _HD_PRIVATE
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devhd.h>
+#include <diskgeom.h>
+
+/* Used by the asm helpers */
+uint8_t hd_page;
+
+/* Swap is scanned for so not constant */
+uint16_t swap_dev;
+
+/* Seek and restore low 4 bits are the step rate, read/write support
+ multi-sector mode but not all emulators do .. */
+
+struct minipart parts[MAX_HD];
+
+/* Wait for the drive to show ready */
+uint8_t hd_waitready(void)
+{
+ uint8_t st;
+ do {
+ st = hd_status;
+ } while (!(st & 0x40));
+ return st;
+}
+
+/* Wait for DRQ or an error */
+uint8_t hd_waitdrq(void)
+{
+ uint8_t st;
+ do {
+ st = hd_status;
+ } while (!(st & 0x09));
+ return st;
+}
+
+uint8_t hd_xfer(bool is_read)
+{
+ /* Error ? */
+ if (hd_status & 0x01)
+ return hd_status;
+ if (is_read)
+ hd_xfer_in(udata.u_dptr);
+ else
+ hd_xfer_out(udata.u_dptr);
+ /* Should be returning READY, and maybe SEEKDONE */
+ return hd_status;
+}
+
+int hd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ uint16_t ct = 0;
+ staticfast uint8_t tries;
+ uint8_t err = 0;
+ uint8_t cmd = HDCMD_READ;
+ uint8_t head;
+ uint8_t sector;
+ staticfast uint16_t cyl;
+ uint8_t dev = minor >> 4;
+ staticfast struct minipart *p;
+
+ p = &parts[dev];
+
+ /* FIXME: We only support 512 byte access chunks even for raw I/O */
+ hd_page = 0;
+ if (rawflag == 1) {
+ if (d_blkoff(BLKSHIFT))
+ return -1;
+ hd_page = udata.u_page; /* User space */
+ } else if (rawflag == 2)
+ hd_page = swappage;
+
+ udata.u_nblock *= 2;
+
+ if (!is_read)
+ cmd = HDCMD_WRITE;
+
+ /* TRS80 hard disk are 32 sectors/track, 256 byte sectors */
+
+ hd_precomp = p->g.precomp;
+ hd_seccnt = 1;
+
+ sector = udata.u_block;
+ sector = (sector << 1) & 0x1E;
+
+ cyl = udata.u_block >> 4;
+
+ /* Do the maths once and on 16 bit numbers */
+ head = cyl % p->g.head;
+ cyl /= p->g.head;
+ if (minor)
+ cyl += p->cyl[(minor-1)&0x0F];
+
+ while (ct < udata.u_nblock) {
+ /* Head next bits, plus drive */
+ hd_sdh = 0x80 | head | (dev << 3);
+ hd_secnum = sector;
+ /* cylinder bits */
+ hd_cyllo = cyl & 0xFF;
+ hd_cylhi = cyl >> 8;
+
+ for (tries = 0; tries < 4; tries++) {
+ /* issue the command */
+ hd_cmd = cmd;
+ /* DRQ will go high once the controller is ready
+ for us */
+ err = hd_waitdrq();
+ if (!(err & 1)) {
+ err = hd_xfer(is_read);
+ /* Ready, no error ? */
+ if ((err & 0x41) == 0x40)
+ break;
+ } else
+ kprintf("hd%d: err %x\n", minor, err);
+
+ if (tries > 1) {
+ hd_cmd = HDCMD_RESTORE | p->g.seek;
+ if (hd_waitready() & 1)
+ kprintf("hd%d: restore error %z\n", minor, err);
+ }
+ }
+ /* FIXME: should we try the other half and then bale out ? */
+ if (tries == 3)
+ goto bad;
+ ct++;
+ udata.u_dptr += 256;
+ sector++;
+ /* Cheaper than division! */
+ if (sector == 32) {
+ sector = 0;
+ head++;
+ if (head == p->g.head) {
+ head = 0;
+ cyl++;
+ }
+ }
+ }
+ return ct << 8;
+bad:
+ if (err & 1)
+ kprintf("hd%d: error %x\n", minor, hd_err);
+ else
+ kprintf("hd%d: status %x\n", minor, err);
+bad2:
+ udata.u_error = EIO;
+ return -1;
+}
+
+int hd_open(uint8_t minor, uint16_t flag)
+{
+ uint8_t dev = minor >> 4;
+ flag;
+ if (dev > MAX_HD || parts[dev].g.head == 0 ||
+ (minor && parts[dev].cyl[(minor-1)&0x0F] == 0xFFFF)) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return 0;
+}
+
+int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return hd_transfer(minor, true, rawflag);
+}
+
+int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return hd_transfer(minor, false, rawflag);
+}
+
--- /dev/null
+#ifndef __DEVHD_DOT_H__
+#define __DEVHD_DOT_H__
+
+/* public interface */
+extern int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+extern int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+extern int hd_open(uint8_t minor, uint16_t flag);
+
+extern void hd_probe(void);
+
+#ifdef _HD_PRIVATE
+
+__sfr __at 0xC0 hd_wpbits; /* Write protect and IRQ (not used) */
+__sfr __at 0xC1 hd_ctrl; /* Reset and enable bits */
+__sfr __at 0xC8 hd_data;
+__sfr __at 0xC9 hd_precomp; /* W/O */
+__sfr __at 0xC9 hd_err; /* R/O */
+__sfr __at 0xCA hd_seccnt;
+__sfr __at 0xCB hd_secnum;
+__sfr __at 0xCC hd_cyllo;
+__sfr __at 0xCD hd_cylhi;
+__sfr __at 0xCE hd_sdh;
+__sfr __at 0xCF hd_status; /* R/O */
+__sfr __at 0xCF hd_cmd;
+
+#define HDCMD_RESTORE 0x10
+#define HDCMD_READ 0x20
+#define HDCMD_WRITE 0x30
+#define HDCMD_VERIFY 0x40 /* Not on the 1010 later only */
+#define HDCMD_FORMAT 0x50
+#define HDCMD_INIT 0x60 /* Ditto */
+#define HDCMD_SEEK 0x70
+
+#define RATE_4MS 0x08 /* 4ms step rate for hd (conservative) */
+
+#define HDCTRL_SOFTRESET 0x10
+#define HDCTRL_ENABLE 0x08
+#define HDCTRL_WAITENABLE 0x04
+
+#define HDSDH_ECC256 0x80
+
+/* Seek and restore low 4 bits are the step rate, read/write support
+ multi-sector mode but not all emulators do .. */
+
+#define MAX_HD 4
+
+extern struct minipart parts[MAX_HD];
+
+extern uint8_t hd_waitready(void);
+extern uint8_t hd_waitdrq(void);
+extern uint8_t hd_xfer(bool is_read);
+
+/* helpers in common memory for the block transfers */
+extern int hd_xfer_in(uint8_t *addr);
+extern int hd_xfer_out(uint8_t *addr);
+
+#endif
+#endif /* __DEVHD_DOT_H__ */
--- /dev/null
+#define _HD_PRIVATE
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devhd.h>
+#include <diskgeom.h>
+
+/*
+ * Mini part processing. This and mbr and other things all one day
+ * need to become a bit more unified.
+ */
+static void hd_swapon(struct minipart *p, unsigned int d, unsigned int i)
+{
+ uint16_t cyls;
+ if (i == 14 || p->cyl[i+1] == 0xFFFF)
+ cyls = p->g.cyl;
+ else
+ cyls = p->cyl[i+1];
+ cyls -= p->cyl[i];
+ cyls *= p->g.head;
+ /* This is now the number of sets of 32 sectors (16K) in swap.
+ We need 32K per process: hardwire it here - FIX if you change
+ the mapping model */
+
+ swap_dev = (d << 4) + i + 1;
+
+ if (cyls >= MAX_SWAPS)
+ cyls = MAX_SWAPS - 1;
+ for (i = 0; i < cyls; i++) {
+ swapmap_init(i);
+ }
+ kputs("swap-");
+}
+
+void hd_probe(void)
+{
+ unsigned int dev = 0;
+ unsigned int i;
+ /* Second half of second block */
+ struct minipart *p;
+
+ udata.u_dptr = tmpbuf();
+ p = (struct minipart *)(udata.u_dptr + 128);
+
+ for (dev = 0; dev < 4; dev++) {
+ hd_sdh = 0x80 | (dev << 3);
+ hd_cmd = HDCMD_RESTORE | RATE_4MS;
+ if (hd_waitready() & 1) {
+ if ((hd_err & 0x12) == 0x12)
+ continue;
+ }
+ hd_seccnt = 1;
+ hd_sdh = 0x80 | (dev << 3);
+ hd_secnum = 1;
+ hd_cyllo = 0;
+ hd_cylhi = 0;
+ hd_cmd = HDCMD_READ;
+ if (hd_waitdrq() & 1)
+ continue;
+ if((hd_xfer(1) & 0x41) != 0x40)
+ continue;
+ kprintf("hd%c: ", dev + 'a');
+ if (p->g.magic != MP_SIG_0) {
+ p->g.cyl = 1;
+ p->g.head = 1;
+ p->g.sec = 32;
+ p->g.precomp = 0;
+ p->g.seek = 10;
+ p->g.secsize = 8;
+ for (i = 0; i < 15; i++)
+ p->cyl[i] = 0xFFFFU;
+ kputs("(unlabelled)\n");
+ } else {
+ for (i = 0; i < 15; i++) {
+ if (p->cyl[i] != 0xFFFFU) {
+ if (p->type[i] == 0x56) {
+ /* Configure swap */
+ hd_swapon(p, dev, i);
+ }
+ kprintf("hd%c%d ", dev + 'a', i + 1);
+ }
+ }
+ kputs("\n");
+ }
+ if (p->g.seek) {
+ p->g.seek /= 2;
+ if (p->g.seek == 0)
+ p->g.seek = 1;
+ }
+ /* Set the step rate */
+ hd_cmd = HDCMD_SEEK | p->g.seek;
+ hd_waitready();
+ memcpy(&parts[dev], p, sizeof(parts[dev]));
+ }
+ tmpfree(udata.u_dptr);
+}
--- /dev/null
+#include <kernel.h>
+#include <tty.h>
+#include <version.h>
+#include <kdata.h>
+#include <devfd.h>
+#include <devhd.h>
+#include <devsys.h>
+#include <devlpr.h>
+#include <vt.h>
+#include <devtty.h>
+#include <devgfx.h>
+#include <blkdev.h>
+#include <trs80.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+ /* 0: /dev/hd Hard disc block devices */
+ { hd_open, no_close, hd_read, hd_write, no_ioctl },
+ /* 1: /dev/fd Floppy disc block devices */
+ { fd_open, no_close, fd_read, fd_write, no_ioctl },
+ /* 2: /dev/tty TTY devices */
+ { trstty_open, trstty_close, tty_read, tty_write, gfx_ioctl },
+ /* 3: /dev/lpr Printer devices */
+ { lpr_open, lpr_close, no_rdwr, lpr_write, no_ioctl },
+ /* 4: /dev/mem etc System devices (one offs) */
+ { no_open, no_close, sys_read, sys_write, sys_ioctl },
+ /* 5: reserved */
+ { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 6: reserved */
+ { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 7: reserved */
+ { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 8: no tape */
+ { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 9: ide block devices (temporarily until we make /dev/hd a blkdev device */
+ { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_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;
+}
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <input.h>
+#include <devgfx.h>
+#include <devinput.h>
+
+__sfr __at 0x00 stick;
+static uint8_t old_stick;
+
+__sfr __at 0x7C chroma0;
+__sfr __at 0x7E chroma1;
+static uint8_t old_cj[2];
+
+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);
+}
+
+/* If both are present number the Chroma joysticks 1 and 2 */
+static int chroma_js(uint8_t *slot, uint8_t n, uint8_t v)
+{
+ uint8_t k = 0;
+ if (v == old_cj[n])
+ return 0;
+ old_cj[n] = v;
+ if (v & 1)
+ k = STICK_DIGITAL_L;
+ if (v & 2)
+ k = STICK_DIGITAL_R;
+ if (v & 4)
+ k = STICK_DIGITAL_U;
+ if (v & 8)
+ k = STICK_DIGITAL_D;
+ if (v & 16)
+ k = BUTTON(0);
+ *slot++ = STICK_DIGITAL | (n + 1);
+ *slot = k;
+ return 2;
+}
+
+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;
+ }
+
+ if (has_chroma) {
+ if (chroma_js(slot, 0, ~chroma0))
+ return 2;
+ if (chroma_js(slot, 1, ~chroma1))
+ return 2;
+ }
+ /* Clashes with Alpha joystick */
+ if (has_hrg1)
+ return 0;
+
+ r = ~stick;
+ if (r == old_stick)
+ return 0;
+
+ old_stick = r;
+
+ k = 0;
+ /* Legacy joystick encoding U|D = FIRE */
+ if ((r & 3) == 3) {
+ r &= ~3;
+ r |= 16;
+ }
+ if (r & 1)
+ k = STICK_DIGITAL_U;
+ if (r & 2)
+ k |= STICK_DIGITAL_D;
+ if (r & 4)
+ k |= STICK_DIGITAL_L;
+ if (r & 8)
+ k |= STICK_DIGITAL_R;
+ if (r & 16)
+ k |= BUTTON(0);
+ *slot++ = STICK_DIGITAL;
+ *slot = k;
+ return 2;
+}
+
+void platform_input_wait(void)
+{
+ psleep(&kqueue); /* We wake this on timers so it works for sticks */
+}
+
+int platform_input_write(uint8_t flag)
+{
+ flag;
+ udata.u_error = EINVAL;
+ return -1;
+}
+
+void poll_input(void)
+{
+ if ((!has_hrg1 && ~stick != old_stick) ||
+ (has_chroma && (old_cj[0] != ~chroma0 || old_cj[1] != ~chroma1)))
+ wakeup(&kqueue);
+}
--- /dev/null
+
+extern void queue_input(uint8_t c);
+extern void poll_input(void);
+extern uint8_t vblank;
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devlpr.h>
+#include <trs80.h>
+
+#define lp *((volatile uint8_t *)0x37E8)
+
+__sfr __at 0xFD vg_lp;
+
+int lpr_open(uint8_t minor, uint16_t flag)
+{
+ minor; flag; // shut up compiler
+ return 0;
+}
+
+int lpr_close(uint8_t minor)
+{
+ minor; // shut up compiler
+ return 0;
+}
+
+static uint8_t iopoll(void)
+{
+ /* Ought to be a core helper for this lot ? */
+ if (need_reschedule())
+ _sched_yield();
+ if (chksigs()) {
+ if (!udata.u_done) {
+ udata.u_error = EINTR;
+ udata.u_done = (usize_t)-1;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ char *p = udata.u_base;
+ minor; rawflag; flag; // shut up compiler
+
+ while(udata.u_done < udata.u_count) {
+ if (trs80_model == VIDEOGENIE) {
+ while (vg_lp & 0x80) {
+ if (iopoll())
+ return udata.u_done;
+ }
+ /* FIXME: tidy up ugetc and sysio checks globally */
+ vg_lp = ugetc(p++);
+ } else {
+ while (lp & 0x80) {
+ if (iopoll())
+ return udata.u_done;
+ }
+ /* FIXME: tidy up ugetc and sysio checks globally */
+ lp = ugetc(p++);
+ }
+ udata.u_done++;
+ }
+ return udata.u_done;
+}
--- /dev/null
+#ifndef __DEVLPR_DOT_H__
+#define __DEVLPR_DOT_H__
+
+int lpr_open(uint8_t minor, uint16_t flag);
+int lpr_close(uint8_t minor);
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+
+#endif
--- /dev/null
+#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 <devgfx.h>
+#include <stdarg.h>
+#include <trs80.h>
+
+/* FIXME: move to external tty buffers and bank them */
+static char tbuf1[TTYSIZ];
+static char tbuf2[TTYSIZ];
+static char tbuf3[TTYSIZ];
+
+uint8_t curtty; /* output side */
+static uint8_t inputtty; /* input side */
+static struct vt_switch ttysave[2];
+struct vt_repeat keyrepeat;
+extern uint8_t *vtbase[2];
+
+/* Default to having /dev/tty and the two consoles openable. Our probe
+ routine will add tty2/tty3 as appropriate */
+
+static uint8_t ports = 3;
+
+/* The Video Genie EG3020 is similar but the TR1865 is
+ data in: F8, status out F8, data out: F9 status in F9,
+ baud by switches.
+
+ Or at least it probably does. In theory you can use an adapter
+ cable and Tandy bits so we treat them as two ports */
+
+__sfr __at 0xE8 tr1865_ctrl;
+static uint8_t tr1865_ctrl_save;
+__sfr __at 0xE9 tr1865_baud;
+__sfr __at 0xEA tr1865_status;
+__sfr __at 0xEB tr1865_rxtx;
+
+__sfr __at 0xF8 vg_tr1865_wrst;
+__sfr __at 0xF9 vg_tr1865_ctrd;
+static uint8_t vg_tr1865_ctrd_save;
+
+struct s_queue ttyinq[NUM_DEV_TTY + 1] = { /* ttyinq[0] is never used */
+ {NULL, NULL, NULL, 0, 0, 0},
+ {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2},
+ {tbuf2, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ / 2},
+ {tbuf3, tbuf3, tbuf3, TTYSIZ, 0, TTYSIZ / 2},
+};
+
+static uint8_t trs_flow; /* RTS/CTS */
+
+/* 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;
+ /* RTS/CTS is supported by the hardware. We assume delays will be
+ short as with our rather limited serial if we go off and schedule
+ something else each flow control it will get horribly slow */
+ if (minor == 2) {
+ if (ttydata[2].termios.c_cflag & CRTSCTS) {
+ reg = tr1865_ctrl;
+ if (!(reg & 0x80))
+ return TTY_READY_SOON;
+ }
+ reg = tr1865_status;
+ return (reg & 0x40) ? TTY_READY_NOW : TTY_READY_SOON;
+ }
+ /* minor == 3 */
+ reg = vg_tr1865_wrst;
+ if (ttydata[3].termios.c_cflag & CRTSCTS) {
+ /* CTS ? */
+ if (!(reg & 0x40))
+ return TTY_READY_SOON;
+ }
+ return (reg & 0x80) ? TTY_READY_NOW : TTY_READY_SOON;
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ if (minor == 2)
+ tr1865_rxtx = c;
+ else if (minor == 3)
+ vg_tr1865_wrst = c;
+ else
+ vtoutput(&c,1);
+}
+
+void tty_data_consumed(uint8_t minor)
+{
+ if (trs_flow & (1 << minor)) {
+ if (minor == 2) {
+ /* We have space.. raise RTS */
+ if (!fullq(&ttyinq[2]))
+ tr1865_ctrl = tr1865_ctrl_save|0x01;
+ }
+ if (minor == 3) {
+ /* We have space.. raise RTS */
+ if (!fullq(&ttyinq[3]))
+ vg_tr1865_ctrd = vg_tr1865_ctrd_save|0x01;
+ }
+ }
+}
+
+/* Only the Model III has this as an actual interrupt */
+void tty_interrupt(void)
+{
+ uint8_t reg = tr1865_status;
+ if (reg & 0x80) {
+ reg = tr1865_rxtx;
+ tty_inproc(2, reg);
+ }
+ if ((trs_flow & 8) && fullq(&ttyinq[2]))
+ tr1865_ctrl = tr1865_ctrl_save & ~1;
+}
+
+void tty_poll(void)
+{
+ uint8_t reg;
+
+ /* Do the VG port */
+ if (ports & 0x10) {
+ reg = vg_tr1865_wrst;
+ if (reg & 0x01) {
+ reg = vg_tr1865_ctrd;
+ tty_inproc(3, reg);
+ }
+ if ((trs_flow & 0x10) && fullq(&ttyinq[3]))
+ vg_tr1865_ctrd = vg_tr1865_ctrd_save & ~1;
+ }
+ /* Do the Model I/III port */
+ if (ports & 0x08)
+ tty_interrupt();
+}
+
+/* Called to set baud rate etc */
+static const uint8_t trsbaud[] = {
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 14, 15
+};
+
+static const uint8_t trssize[4] = {
+ 0x00, 0x40, 0x20, 0x60
+};
+
+void tty_setup(uint8_t minor)
+{
+ uint8_t baud;
+ uint8_t ctrl = 3; /* DTR|RTS */
+ struct tty *t = ttydata + minor;
+
+ if (minor == 1)
+ return;
+
+ if (minor != 2 || trs80_model == LNW80) {
+ baud = ttydata[2].termios.c_cflag & CBAUD;
+ if (baud > B19200) {
+ ttydata[2].termios.c_cflag &= ~CBAUD;
+ ttydata[2].termios.c_cflag |= B19200;
+ baud = B19200;
+ } else
+ baud = trsbaud[baud];
+
+ tr1865_baud = baud | (baud << 4);
+
+ }
+ if (t->termios.c_cflag & PARENB) {
+ if (t->termios.c_cflag & PARODD)
+ ctrl |= 0x80;
+ } else
+ ctrl |= 0x8; /* No parity */
+ ctrl |= trssize[(t->termios.c_cflag & CSIZE) >> 4];
+
+ if (t->termios.c_cflag & CRTSCTS)
+ trs_flow |= (1 << minor);
+ else
+ trs_flow &- ~(1 << minor);
+ if (minor == 3) {
+ tr1865_ctrl_save = ctrl;
+ tr1865_ctrl = ctrl;
+ } else {
+ vg_tr1865_ctrd_save = ctrl;
+ vg_tr1865_ctrd = ctrl;
+ }
+}
+
+int trstty_open(uint8_t minor, uint16_t flags)
+{
+ /* Serial port cards are optional */
+ if (minor < 8 && !(ports & (1 << minor))) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return tty_open(minor, flags);
+}
+
+int trstty_close(uint8_t minor)
+{
+ if (ttydata[minor].users == 0) {
+ trs_flow &= ~(1 << minor);
+ if (minor == 2)
+ tr1865_ctrl = 0; /* Drop carrier and rts */
+ else if (minor == 3)
+ vg_tr1865_ctrd = 0;
+ }
+ return tty_close(minor);
+}
+
+int tty_carrier(uint8_t minor)
+{
+ if (minor == 1)
+ return 1;
+ if (minor == 2) {
+ if (tr1865_ctrl & 0x80)
+ return 1;
+ } else if (vg_tr1865_ctrd & 0x10)
+ return 1;
+ return 0;
+}
+
+void tty_sleeping(uint8_t minor)
+{
+ used(minor);
+}
+
+void trstty_probe(void)
+{
+ if (vg_tr1865_wrst != 0xFF)
+ ports |= (1 << 3);
+ if (tr1865_status != 0xFF)
+ ports |= (1 << 2);
+}
+
+uint8_t keymap[8];
+static uint8_t keyin[8];
+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
+};
+
+static void keyproc(void)
+{
+ int i;
+ uint8_t key;
+
+ for (i = 0; i < 8; i++) {
+ /* Set one of A0 to A7, and read the byte we get back.
+ Invert that to get a mask of pressed buttons */
+ keyin[i] = *(uint8_t *) (0x3800 | (1 << i));
+ 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 TRS80 keyboard is a little lacking in some rather useful symbols
+ *
+ * There are conventions for some of these
+ * shift-0 is capslock
+ * downarrow-xx is ctrl
+ * There was also a popular keyboard mod
+ *
+ * We also map as follows
+ * left arrow - backspace
+ * right arrow - delete
+ * shift left/right arrow - switch vt
+ * stop - ^C
+ * control-shift-brackets give curly brackets
+ * control-shift minus gives underscore
+ * control-shift slash gives backquote
+ * control-shift les-than gives hat
+ * control-brackets gives square brackets
+ * control-minus gives pipe
+ *
+ * We may want to add others if need be
+ *
+ * TODO: Where does the LNW80 hide F1/F2 ?
+ */
+
+uint8_t keyboard[8][8] = {
+ {'@', 'a', 'b', 'c', 'd', 'e', 'f', 'g'},
+ {'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'},
+ {'p', 'q', 'r', 's', 't', 'u', 'v', 'w'},
+ /* F1-F4 are only present on Video Genie II/Dick Smith II */
+ {'x', 'y', 'z', 0, KEY_F2, KEY_F3, KEY_F4, KEY_F1},
+ {'0', '1', '2', '3', '4', '5', '6', '7'},
+ {'8', '9', ':', ';', ',', '-', '.', '/'},
+ {KEY_ENTER, KEY_CLEAR, KEY_STOP, KEY_UP, 0 /*KEY_DOWN */ , KEY_BS, KEY_DEL, ' '},
+ /* The Model 1 only has bit 0 of this for its shift key. The Model 3
+ has bit 2 for right shift. Some add-ons used bit 4 for control,
+ other things borrowed the down arrow
+ The VideoGenie has MS on bit 1, RPT on 3 and CTRL on 4 */
+ {0, 0, 0, 0, KEY_CAPSLOCK, 0, 0, 0}
+};
+
+uint8_t shiftkeyboard[8][8] = {
+ {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G'},
+ {'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'},
+ {'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'},
+ {'X', 'Y', 'Z', 0, 0, 0, 0, 0},
+ {KEY_CAPSLOCK, '!', '"', '#', '$', '%', '&', '\''},
+ {'(', ')', '*', '+', '<', '=', '>', '?'} ,
+ {KEY_ENTER, KEY_CLEAR, KEY_STOP, KEY_UP, 0 /*KEY_DOWN */ , KEY_LEFT, KEY_RIGHT, ' '},
+ {0, 0, 0, 0, KEY_CAPSLOCK, 0, 0, 0}
+};
+
+static uint8_t capslock = 0;
+static uint8_t kbd_timer;
+
+static void keydecode(void)
+{
+ uint8_t c;
+ uint8_t m = 0;
+
+ /* Convention for capslock or the mod */
+ /* Only the model 3 has right shift (2) */
+ if (keymap[7] & 3) { /* shift (left/right) */
+ m = KEYPRESS_SHIFT;
+ c = shiftkeyboard[keybyte][keybit];
+ } else
+ c = keyboard[keybyte][keybit];
+
+ if (c == KEY_CAPSLOCK) {
+ capslock = 1 - capslock;
+ return;
+ }
+ /* The keyboard lacks some rather important symbols so remap them
+ with control (down arrow) */
+ if ((keymap[6] | keymap[7]) & 16) { /* control */
+ m |= KEYPRESS_CTRL;
+ if (keymap[7] & 3) { /* shift */
+ if (c == '(')
+ c = '{';
+ if (c == ')')
+ c = '}';
+ if (c == '-')
+ c = '_';
+ if (c == '/')
+ c = '``';
+ if (c == '<')
+ c = '^';
+ } else {
+ if (c == '8' /*'(' */ )
+ c = '[';
+ else if (c == '9' /*')' */ )
+ c = ']';
+ else if (c == '-')
+ c = '|';
+ else if (c > 31 && c < 127)
+ c &= 31;
+ }
+ } else if (capslock && c >= 'a' && c <= 'z')
+ c -= 'a' - 'A';
+ 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;
+ }
+ }
+}
+
+/* Polled 40 times a second */
+void kbd_interrupt(void)
+{
+ newkey = 0;
+ keyproc();
+ if (keysdown && keysdown < 3) {
+ if (newkey) {
+ keydecode();
+ kbd_timer = keyrepeat.first;
+ } else if (!--kbd_timer) {
+ keydecode();
+ kbd_timer = keyrepeat.continual;
+ }
+ }
+ poll_input();
+}
--- /dev/null
+#ifndef _DEVTTY_H
+#define _DEVTTY_H
+
+extern void tty_interrupt(void);
+extern void tty_poll(void);
+extern void kbd_interrupt(void);
+extern int trstty_open(uint8_t minor, uint16_t flags);
+extern int trstty_close(uint8_t minor);
+extern void trstty_probe(void);
+extern void vtbuf_init(void);
+
+/* And from the asm helper */
+extern void vtswap(void);
+
+#define KEY_ROWS 8
+#define KEY_COLS 8
+extern uint8_t keymap[8];
+extern uint8_t keyboard[8][8];
+extern uint8_t shiftkeyboard[8][8];
+
+extern uint8_t *vtbase[2];
+extern uint8_t curtty;
+#endif
--- /dev/null
+#include <kernel.h>
+#include <devhd.h>
+#include <devtty.h>
+#include <tty.h>
+#include <kdata.h>
+#include "devgfx.h"
+#include <devide.h>
+#include "trs80.h"
+
+static void vt_check_lower(void)
+{
+ *VT_BASE = 'a';
+ if (*VT_BASE == 'a')
+ video_lower = 1;
+}
+
+void device_init(void)
+{
+ vt_check_lower();
+#ifdef CONFIG_RTC
+ /* Time of day clock */
+ inittod();
+#endif
+ hd_probe();
+ devide_init();
+ trstty_probe();
+ gfx_init();
+}
+
+void map_init(void)
+{
+}
+
+/* string.c
+ * Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>
+ * This file is part of the Linux-8086 C library and is distributed
+ * under the GNU Library General Public License.
+ */
+static int strcmp(const char *d, const char *s)
+{
+ register char *s1 = (char *) d, *s2 = (char *) s, c1, c2;
+
+ while ((c1 = *s1++) == (c2 = *s2++) && c1);
+ return c1 - c2;
+}
+
+uint8_t platform_param(char *p)
+{
+ if (strcmp(p, "pcg80") == 0) {
+ trs80_udg = UDG_PCG80;
+ return 1;
+ }
+ if (strcmp(p, "80gfx") == 0) {
+ trs80_udg = UDG_80GFX;
+ return 1;
+ }
+ if (strcmp(p, "micro") == 0) {
+ trs80_udg = UDG_MICROFIRMA;
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+-mwxuy
+-i fuzix.ihx
+-b _INITIALIZER=0x37C0
+-b _BOOT=0x0100
+-b _CODE=0x4000
+-b _COMMONMEM=0x0200
+-l z80
+platform-genie-eg64/crt0.rel
+platform-genie-eg64/commonmem.rel
+platform-genie-eg64/trs80.rel
+platform-genie-eg64/trs80-bank.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+usermem.rel
+platform-genie-eg64/tricks.rel
+platform-genie-eg64/main.rel
+timer.rel
+kdata.rel
+platform-genie-eg64/devfd.rel
+platform-genie-eg64/floppy.rel
+platform-genie-eg64/devhd.rel
+platform-genie-eg64/devhd_discard.rel
+platform-genie-eg64/devices.rel
+devio.rel
+filesys.rel
+process.rel
+inode.rel
+syscall_exec16.rel
+syscall_fs.rel
+syscall_fs2.rel
+syscall_fs3.rel
+syscall_net.rel
+syscall_proc.rel
+syscall_other.rel
+tty.rel
+mm.rel
+swap.rel
+simple.rel
+vt.rel
+devsys.rel
+devinput.rel
+platform-genie-eg64/blkdev.rel
+platform-genie-eg64/devgfx.rel
+platform-genie-eg64/devide.rel
+platform-genie-eg64/devide_discard.rel
+platform-genie-eg64/devinput.rel
+platform-genie-eg64/devlpr.rel
+platform-genie-eg64/devtty.rel
+platform-genie-eg64/discard.rel
+platform-genie-eg64/mbr.rel
+usermem_std-z80.rel
+-e
--- /dev/null
+; UZI mnemonics for memory addresses etc
+
+U_DATA .equ 0x0200 ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE .equ 0x200 ; 256+256 (we don't save istack)
+
+PROGBASE .equ 0x8000
+PROGLOAD .equ 0x8000
+
+Z80_TYPE .equ 1
+
+NBUFS .equ 4
+
+Z80_MMU_HOOKS .equ 0
+
+CONFIG_SWAP .equ 1
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <rtc.h>
+#include <devtty.h>
+#include <trs80.h>
+
+uint16_t ramtop = PROGTOP;
+uint8_t trs80_model;
+uint8_t vtattr_cap;
+uint8_t need_resched;
+
+/*
+ * Called when there is no work to do. On the models without serial
+ * interrupts we poll here so that the normal case of idling while
+ * waiting for input feels ok.
+ */
+void platform_idle(void)
+{
+ irqflags_t irq;
+ irq = di();
+ tty_poll();
+ irqrestore(irq);
+}
+
+void do_beep(void)
+{
+}
+
+/* Work around SDCC bugs */
+uint8_t sdcc_bug_2753(uint8_t v) __z88dk_fastcall
+{
+ return v;
+}
+
+__sfr __at 0xE0 irqstat3;
+__sfr __at 0xEC irqack3;
+
+/* We assign these to dummy to deal with an sdcc bug (should be fixed in next
+ SDCC) */
+void platform_interrupt(void)
+{
+ uint8_t dummy;
+ uint8_t irq = *((volatile uint8_t *)0x37E0);
+
+ tty_interrupt();
+ kbd_interrupt();
+
+ if (irq & 0x40)
+ dummy = sdcc_bug_2753(*((volatile uint8_t *)0x37EC));
+ if (irq & 0x80) { /* FIXME??? */
+ timer_interrupt();
+ dummy = sdcc_bug_2753(*((volatile uint8_t *)0x37E0)); /* Ack the timer */
+ }
+}
+
+struct blkbuf *bufpool_end = &bufpool[NBUFS];
+
+void platform_discard(void)
+{
+ bufptr bp;
+ uint16_t space = (uint8_t *)0x37E0 - (uint8_t *)bufpool_end;
+ space /= BLKSIZE;
+ bufpool_end += space;
+ kprintf("Reclaiming memory.. total buffers %d\n",
+ bufpool_end - bufpool);
+ for( bp = bufpool + NBUFS; bp < bufpool_end; ++bp ){
+ bp->bf_dev = NO_DEVICE;
+ bp->bf_busy = BF_FREE;
+ }
+}
+
+#ifdef CONFIG_RTC
+
+__sfr __at 0xB0 rtc_secl;
+__sfr __at 0xB1 rtc_sech;
+__sfr __at 0xB2 rtc_minl;
+__sfr __at 0xB3 rtc_minh;
+__sfr __at 0xB4 rtc_hourl;
+__sfr __at 0xB5 rtc_hourh;
+/* day of week is B6 */
+__sfr __at 0xB7 rtc_dayl;
+__sfr __at 0xB8 rtc_dayh;
+__sfr __at 0xB9 rtc_monl;
+__sfr __at 0xBA rtc_monh;
+__sfr __at 0xBB rtc_yearl;
+__sfr __at 0xBC rtc_yearh;
+
+/* FIXME: the RTC is optional so we should test for it first */
+uint8_t platform_rtc_secs(void)
+{
+ uint8_t sl, rv;
+ /* BCD encoded */
+ do {
+ sl = rtc_secl;
+ /* RTC may be absent */
+ if (sl == 255)
+ return 255;
+ rv = sl + rtc_sech * 10;
+ } while (sl != rtc_secl);
+ return rv;
+}
+
+/* If the compiler segfaults here you need at least SDCC #10471 */
+
+int platform_rtc_read(void)
+{
+ uint16_t len = sizeof(struct cmos_rtc);
+ struct cmos_rtc cmos;
+ uint8_t *p;
+ uint8_t r, y;
+
+ if (udata.u_count < len)
+ len = udata.u_count;
+
+ if (rtc_secl == 255) {
+ udata.u_error = EOPNOTSUPP;
+ return -1;
+ }
+
+ /* We do a full set of reads and if the seconds change retry - we
+ need to retry the lost as we might read as the second changes for
+ new year */
+ do {
+ p = cmos.data.bytes;
+ r = rtc_secl;
+ y = (rtc_yearh << 4) | rtc_yearl;
+ if (y > 70)
+ *p++ = 19;
+ else
+ *p++ = 20;
+ *p++ = y;
+ *p++ = ((rtc_monh & 1)<< 4) | rtc_monl;
+ *p++ = ((rtc_dayh & 3) << 4) | rtc_dayl;
+ *p++ = ((rtc_hourh & 3) << 4) | rtc_hourl;
+ *p++ = ((rtc_minh & 7) << 4) | rtc_minl;
+ *p++ = ((rtc_sech & 7) << 4) | rtc_secl;
+ } while ((r ^ rtc_secl) & 0x0F);
+
+ cmos.type = CMOS_RTC_BCD;
+ if (uput(&cmos, udata.u_base, len) == -1)
+ return -1;
+ return len;
+}
+
+/* Yes I'm a slacker .. this wants adding but it's ugly
+ because the seconds is always just set to 0 on any change. We
+ also need to deal with leap years here */
+int platform_rtc_write(void)
+{
+ udata.u_error = EOPNOTSUPP;
+ return -1;
+}
+
+#endif
+
+/*
+ * So that we don't suck in a library routine we can't use from
+ * the runtime
+ */
+
+int strlen(const char *p)
+{
+ int len = 0;
+ while(*p++)
+ len++;
+ return len;
+}
+
+uint8_t video_lower;
+
+uint8_t vt_map_char(uint8_t x)
+{
+ if (video_lower)
+ return x;
+ if (x >= 96 && x <= 127)
+ return x - 32;
+ return x;
+}
--- /dev/null
+#define IDE_DRIVE_COUNT 2
+#define IDE_REG_CS1_BASE 0x40
+#define IDE_8BIT_ONLY
+
+#define ide_select(x)
+#define ide_deselect()
--- /dev/null
+
+extern uint8_t trs80_model;
+#define TRS80_MODEL1 0
+#define TRS80_MODEL3 1
+#define LNW80 2 /* Need to handle LNW80 model II.. */
+#define VIDEOGENIE 3 /* But colour genie is way too different.. */
+
+extern uint8_t trs80_mapper;
+#define MAP_SUPERMEM 0 /* Alpha Supermem and compatibles */
+#define MAP_SELECTOR 1 /* Selector */
+
+extern uint8_t trs80_udg; /* User defined graphics module */
+#define UDG_NONE 0
+#define UDG_PCG80 1 /* Orcim PCG80 */
+#define UDG_80GFX 2 /* Programma 80-Grafix */
+#define UDG_MICROFIRMA 3 /* External box option for UK TRS80 Model 1 */
+
+extern uint8_t video_lower; /* Lowercase available */
+
+/*
+ * Differences versus model 1
+ *
+ * LNW80 Port 0xFE control for memory, extra graphics modes,
+ * (not currently supported). 4MHz CPU mode. Memory
+ * expansions include the selector.
+ * LNW80 II Adds 4 banks of graphics memory it seems. Also adds
+ * port 0x1f which controls low system RAM
+ * (bit 1 - ROM off if set, bit 2 blocks IRQ if set)
+ * VideoGenie Printer at 0xFD not memory mapped. The earlier
+ * expansion unit is a 3 slot S100 backplane so can be
+ * fitted with banked S100 memory (only the upper 32K
+ * seems to map to the S100). Some had lower case.
+ * Model 3 Floppy moves to 0xF0-F4/F7 except for status, Sound
+ * click moves to 0x90 printer to 0xFB. 0xEF control
+ * register, NMI control, Interrupt mask register.
+ * Interrupt latch doesn't ack timer and is inverted.
+ * Lower case !!
+ *
+ * Note that the Colour Genie is a very different beast.
+ *
+ */
+
+extern void bufsetup(void);