From: Alan Cox Date: Sun, 12 Aug 2018 23:39:57 +0000 (+0100) Subject: genie-eg64: New platform (in test only) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=3e3a90f8d1f28896715e05092df7c3fd47635660;p=FUZIX.git genie-eg64: New platform (in test only) The Video Genie EG64 allows you to take a Video Genie (also known as the Dick Smith System 80, the TRZ80 and a few other names) with either of the expansion units and the EG64 fitted up to 96K. It's intended for running CP/M but it's just about big enough to run Fuzix badly providing you have a swap device on hard disk. Another goal of this port is to figure out how to move a lot of TRS80 driver code into dev/trs80 to share between these two ports, a Genie III port maybe, and also potentially things like the Lobo-Max, Guepard., SysData III and so on. --- diff --git a/Kernel/platform-genie-eg64/Makefile b/Kernel/platform-genie-eg64/Makefile new file mode 100644 index 00000000..877e86f3 --- /dev/null +++ b/Kernel/platform-genie-eg64/Makefile @@ -0,0 +1,60 @@ + +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 diff --git a/Kernel/platform-genie-eg64/README b/Kernel/platform-genie-eg64/README new file mode 100644 index 00000000..142a3bca --- /dev/null +++ b/Kernel/platform-genie-eg64/README @@ -0,0 +1,104 @@ +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) diff --git a/Kernel/platform-genie-eg64/config.h b/Kernel/platform-genie-eg64/config.h new file mode 100644 index 00000000..5e545eb6 --- /dev/null +++ b/Kernel/platform-genie-eg64/config.h @@ -0,0 +1,79 @@ +/* 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); diff --git a/Kernel/platform-genie-eg64/devfd.c b/Kernel/platform-genie-eg64/devfd.c new file mode 100644 index 00000000..7a154850 --- /dev/null +++ b/Kernel/platform-genie-eg64/devfd.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +#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); +} diff --git a/Kernel/platform-genie-eg64/devfd.h b/Kernel/platform-genie-eg64/devfd.h new file mode 100644 index 00000000..672cf907 --- /dev/null +++ b/Kernel/platform-genie-eg64/devfd.h @@ -0,0 +1,15 @@ +#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__ */ diff --git a/Kernel/platform-genie-eg64/devgfx.c b/Kernel/platform-genie-eg64/devgfx.c new file mode 100644 index 00000000..96bd6347 --- /dev/null +++ b/Kernel/platform-genie-eg64/devgfx.c @@ -0,0 +1,403 @@ +/* + * 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 +#include +#include +#include +#include +#include +#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; +} diff --git a/Kernel/platform-genie-eg64/devgfx.h b/Kernel/platform-genie-eg64/devgfx.h new file mode 100644 index 00000000..185c3c5e --- /dev/null +++ b/Kernel/platform-genie-eg64/devgfx.h @@ -0,0 +1,13 @@ +#ifndef _DEV_GFX_H +#define _DEV_GFX_H + +#include + +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 diff --git a/Kernel/platform-genie-eg64/devhd.c b/Kernel/platform-genie-eg64/devhd.c new file mode 100644 index 00000000..702fb8c8 --- /dev/null +++ b/Kernel/platform-genie-eg64/devhd.c @@ -0,0 +1,178 @@ +/* + * WD1010 hard disk driver + */ + +#define _HD_PRIVATE +#include +#include +#include +#include +#include + +/* 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); +} + diff --git a/Kernel/platform-genie-eg64/devhd.h b/Kernel/platform-genie-eg64/devhd.h new file mode 100644 index 00000000..2f06e0bd --- /dev/null +++ b/Kernel/platform-genie-eg64/devhd.h @@ -0,0 +1,58 @@ +#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__ */ diff --git a/Kernel/platform-genie-eg64/devhd_discard.c b/Kernel/platform-genie-eg64/devhd_discard.c new file mode 100644 index 00000000..313b9688 --- /dev/null +++ b/Kernel/platform-genie-eg64/devhd_discard.c @@ -0,0 +1,96 @@ +#define _HD_PRIVATE +#include +#include +#include +#include +#include + +/* + * 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); +} diff --git a/Kernel/platform-genie-eg64/devices.c b/Kernel/platform-genie-eg64/devices.c new file mode 100644 index 00000000..8fa27aa2 --- /dev/null +++ b/Kernel/platform-genie-eg64/devices.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/Kernel/platform-genie-eg64/devinput.c b/Kernel/platform-genie-eg64/devinput.c new file mode 100644 index 00000000..83850552 --- /dev/null +++ b/Kernel/platform-genie-eg64/devinput.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include + +__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); +} diff --git a/Kernel/platform-genie-eg64/devinput.h b/Kernel/platform-genie-eg64/devinput.h new file mode 100644 index 00000000..5d79a9e0 --- /dev/null +++ b/Kernel/platform-genie-eg64/devinput.h @@ -0,0 +1,4 @@ + +extern void queue_input(uint8_t c); +extern void poll_input(void); +extern uint8_t vblank; diff --git a/Kernel/platform-genie-eg64/devlpr.c b/Kernel/platform-genie-eg64/devlpr.c new file mode 100644 index 00000000..717c8f3f --- /dev/null +++ b/Kernel/platform-genie-eg64/devlpr.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include + +#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; +} diff --git a/Kernel/platform-genie-eg64/devlpr.h b/Kernel/platform-genie-eg64/devlpr.h new file mode 100644 index 00000000..7765c187 --- /dev/null +++ b/Kernel/platform-genie-eg64/devlpr.h @@ -0,0 +1,8 @@ +#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 diff --git a/Kernel/platform-genie-eg64/devtty.c b/Kernel/platform-genie-eg64/devtty.c new file mode 100644 index 00000000..f6408a16 --- /dev/null +++ b/Kernel/platform-genie-eg64/devtty.c @@ -0,0 +1,432 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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(); +} diff --git a/Kernel/platform-genie-eg64/devtty.h b/Kernel/platform-genie-eg64/devtty.h new file mode 100644 index 00000000..f85d2dbc --- /dev/null +++ b/Kernel/platform-genie-eg64/devtty.h @@ -0,0 +1,23 @@ +#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 diff --git a/Kernel/platform-genie-eg64/discard.c b/Kernel/platform-genie-eg64/discard.c new file mode 100644 index 00000000..0646021c --- /dev/null +++ b/Kernel/platform-genie-eg64/discard.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include "devgfx.h" +#include +#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 + * 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; +} diff --git a/Kernel/platform-genie-eg64/fuzix.lnk b/Kernel/platform-genie-eg64/fuzix.lnk new file mode 100644 index 00000000..504ae9f8 --- /dev/null +++ b/Kernel/platform-genie-eg64/fuzix.lnk @@ -0,0 +1,53 @@ +-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 diff --git a/Kernel/platform-genie-eg64/kernel.def b/Kernel/platform-genie-eg64/kernel.def new file mode 100644 index 00000000..dcb7d3d4 --- /dev/null +++ b/Kernel/platform-genie-eg64/kernel.def @@ -0,0 +1,15 @@ +; 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 diff --git a/Kernel/platform-genie-eg64/main.c b/Kernel/platform-genie-eg64/main.c new file mode 100644 index 00000000..440e45ef --- /dev/null +++ b/Kernel/platform-genie-eg64/main.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/Kernel/platform-genie-eg64/platform_ide.h b/Kernel/platform-genie-eg64/platform_ide.h new file mode 100644 index 00000000..baf7842f --- /dev/null +++ b/Kernel/platform-genie-eg64/platform_ide.h @@ -0,0 +1,6 @@ +#define IDE_DRIVE_COUNT 2 +#define IDE_REG_CS1_BASE 0x40 +#define IDE_8BIT_ONLY + +#define ide_select(x) +#define ide_deselect() diff --git a/Kernel/platform-genie-eg64/trs80.h b/Kernel/platform-genie-eg64/trs80.h new file mode 100644 index 00000000..f6d4eb77 --- /dev/null +++ b/Kernel/platform-genie-eg64/trs80.h @@ -0,0 +1,43 @@ + +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);