From: Alan Cox Date: Fri, 28 Sep 2018 12:01:02 +0000 (+0100) Subject: msx1: revisit this platform X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=f28375587bf7f8e298cc94a324fa88476309cb4f;p=FUZIX.git msx1: revisit this platform Now we have the infrastructure and simpler examples of ROM based kernel with a RAM user mapping see if we can get MSX1 64K + kernel in cartridge to work using a swap device. For mappers it doesn't really make sense to keep them in this port. An MSX1 with MSX2 mapper would best be handled by fixing the MSX2 port to support all the different VDP types. MegaRAM is probably its own port but one to tackle once this works and also once we have relocatable binaries. --- diff --git a/Kernel/platform-msx1/Makefile b/Kernel/platform-msx1/Makefile index 5a595e40..83b511a0 100644 --- a/Kernel/platform-msx1/Makefile +++ b/Kernel/platform-msx1/Makefile @@ -1,27 +1,43 @@ +CROSS_CCOPTS += -I../dev/ -I../dev/net/ -CSRCS = devtty.c devfd.c devhd.c devlpr.c +CSRCS = devtty.c devfd.c devlpr.c devide_sunrise.c CSRCS += devices.c main.c -ASRCS = msx1.s crt0.s vdp.s -ASRCS += tricks.s commonmem.s bootrom.s +DISCSRCS = discard.c + +ASRCS = msx1.s crt0.s vdp.s slots.s cartridge.s +ASRCS += tricks.s commonmem.s sunrise.s + +DISCARD_DSRCS = +DSRCS = ../dev/blkdev.c ../dev/mbr.c COBJS = $(CSRCS:.c=.rel) AOBJS = $(ASRCS:.s=.rel) -OBJS = $(COBJS) $(AOBJS) +DISCOBJS = $(DISCSRCS:.c=.rel) +DISCARD_DOBJS = $(patsubst ../dev/%.c,%.rel, $(DISCARD_DSRCS)) +DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS)) -JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:.s=.lst) $(ASRCS:.s=.sym) $(CSRCS:.c=.rst) $(ASRCS:.s=.rst) +OBJS = $(COBJS) $(AOBJS) $(DISCOBJS) $(DISCARD_DOBJS) $(DOBJS) all: $(OBJS) $(COBJS): %.rel: %.c - $(CROSS_CC) $(CROSS_CCOPTS) -c $< + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG2) -c $< $(AOBJS): %.rel: %.s $(CROSS_AS) $(ASOPTS) $< +$(DISCOBJS): %.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 $< + clean: - rm -f $(OBJS) $(JUNK) core *~ + rm -f *.rel *.sym *.ihx *.lst *.rst core *~ image: - dd if=../fuzix.bin of=../fuzix.com bs=256 skip=1 - dd if=../fuzix.bin of=../fuzix.cart bs=16384 conv=sync + ../tools/cartman ../fuzix.bin ../fuzix.map fuzix.cart diff --git a/Kernel/platform-msx1/README.md b/Kernel/platform-msx1/README.md index 5ccee0b0..8a4fd90e 100644 --- a/Kernel/platform-msx1/README.md +++ b/Kernel/platform-msx1/README.md @@ -1,51 +1,85 @@ -First wild guess at an MSX1 target ---- +Idea - Fuzix in ROM -And it is exactly that a guess 8) +System constraints -No memory paging routines implemented yet so not very useful! +4 slots, each maybe 4 subslots +Proposed memory map therefore is -This will also almost certainly need udata copy on process switching at -least until someone fixes sdcc to generate bankable code. At that point we -may be able to bank the OS kernel in a cartridge. +Cartridge +0000-3FFF Fuzix +4000-7FFF Fuzix +8000-BFFF Fuzix + Bootstrap +C000-FFFF Bootloader data -Q: should we work to the following map -``` - 0x0000-0x7FFF User space / switchable with kernel - (in theory if kernel code is cartridge - could we even use RAM + megaram?) - 0x8000-0xBFFF Kernel (end of code, data, highcode, common) - 0xC000-0xFFFF -``` +Running -or do we bite the bullet about relocatable binaries (which we need to do -for the sinclair at least) and do +Kernel mode -``` - 0x0000-0x00FF Vectors - 0x0100-???? - 0x4000-0xBFFF User space/switchable with kernel rest - 0xC000-0xFFFF -``` +0000-BFFF Fuzix +C000-FFFF Kernel data + RAM helpers if needed -which would allow the use of more typs of megaram without LDIR tricks. +User mode -There are some other interesting tradeoffs on size handling if you make the -OS a cartridge as well as you can then use main memory via LDIR and lazy swap -the other 16K of app space for a 48K app so you only end up copying for -switches between big apps but get 48K apps. That might mean binaries at -0x4000-0xFFFF or 0x0000-0xBFFF, but either way means shunting the kernel -around in the pure RAM case to avoid the overlap (eg loading the kernel -at 0x4000 (with discard and bootstrap at 0x100)) +0000-BFFF Process bank +C000-FFFF Kernel data + RAM helpers +Difficulties +- Have to map 0x4000-7FFF to a device for some I/O devices so we can't + always directly do I/O to user space. Kernel buffers are high so ok. + Kernel I/O routines can live in other 16K chunks -Other mappers that might be found include +- We have to load the cartridge header at 0x4000 or 0x8000 - neither is at + all convenient! - - MSX2 mapper (in which case we should probably make the msx2 - platform detect the early type VDP and use that ?) +BIOS lives at 0:0 - - Zemina (requires LDIR copying stuff) +Disk catridges live at 4000-7FFF in some bank/sub-bank usually with their +ROM in the same 16K chunk + +You can't move blocks up and down address spaces + +Done +Add the tool to stuff the image properly into ROM for unpacking +Finish the basic catridge logic +Build the user map from the RAM map +Finish the ROM and RAM map detection logic +Disk I/O routines need bounce buffers for 4000-7FFF range due to the +limited mapping system. + +In Progress + +Move the switch helper into both banks so we can fix the FIXME in map_kernel + +Work out how map/unmap an I/O device needs to work to be usable. Do we grab +the live map and just edit it for 4000-7FFF then set that map ? Do we +precompute and save kernel and user maps for the device ? + +Sunrise IDE support + +To do + +the detection logic by ROM hash and find the sunrise etc +Sensible user copy routines: We know kernel data is always in common except +some awkward corner cases (constants) going K->U. So we can spot the to user +case of a 'low' source and bounce it or similar, while just doing a user +map and ldir for the others. We badly need the cached path walk though! +Speed up all the ei/di mess by just keeping a private variable for irqoff +state +Get common and discard and initialized in C000-FFFF of the ROM +Then on boot up move SP to BFFF, map the ROM in the top 16K, copy it into +8000-Bxxx and then restore the RAM, and copy it up. Then do the normal maps. +Otherwise we are going to run out of space at some point. +Remember current map for kernel/user so we can fast track map_save/restore +map_kernel etc by knowing if we are mapping k->k u->u or a transition and +we can label all other cases with a value meaning 'other' as we don't need +to worry about fastpaths that much + +Target initial I/O devices + +Carnivore2 and Sunrise IDE (I believe same logic) +MegaSD etc +MegaRAM (detect and use for swap for now) diff --git a/Kernel/platform-msx1/cartridge.s b/Kernel/platform-msx1/cartridge.s new file mode 100644 index 00000000..5364e74c --- /dev/null +++ b/Kernel/platform-msx1/cartridge.s @@ -0,0 +1,214 @@ +; +; We will nail this somewhere handy on a page boundary +; + .area _HEADER + .db 'A' + .db 'B' + .dw wtfami + .dw 0,0,0,0,0,0 + + .globl _fuzix_main + .globl init_early + .globl init_hardware + .globl s__DATA + .globl l__DATA + .globl s__INITIALIZER + .globl s__INITIALIZED + .globl l__INITIALIZER + .globl s__COMMONMEM + .globl l__COMMONMEM + + .globl kstack_top + +; +; We are running in an unknown subslot of an unknown bank and +; have 4000-BFFF mapped to us, BIOS (0:0) in the low 16K and +; RAM ought to be in the top 16K because the BIOS needs it. We +; can't btw even assume that all the RAM is in the same bank - what a +; mess! +; +; The BIOS has produced +; EXPTBL - bit 7 set for each expanded slot +; SLTTBL - current value of expansion slot addr +; SLTATR - byte per page for slot contents, notably devices +; +CHPUT .equ 0x00A2 +INITXT .equ 0x006F +EXPTBL .equ 0xFCC1 +SLTTBL .equ 0xFCC5 +SLTATR .equ 0xFCC9 + + +pstring: + ld a,(hl) + or a + ret z + inc hl + call CHPUT + jr pstring + +dophex: ld c,a + and #0xf0 + rrca + rrca + rrca + rrca + call pdigit + ld a,c + and #0x0f +pdigit: cp #10 + jr c,isl + add #7 +isl: add #'0' + out (0x2F),a + ret + +phex: + push af + push bc + call dophex + ld a,#' ' + out (0x2f),a + pop bc + pop af + ret + +; Ripple bits 2-3 into the whole byte +; +; Returns A holding it rippled into the low 3 banks with the +; top one unchange +; E throughout +; +ripple: + push af + and #0x0C + ld e,a + rrca + rrca + or e ; low 4 bits done + ld e,a ; and saved + rlca + rlca + rlca + rlca + or e ; all bits done + ld e,a + and #0x3F + ld d,a + pop af + and #0xC0 + or d + ret + +expan: .asciz 'expanded' + +wtfami: ld sp,#0xF000 ; random free space in known RAM + ; + ; Initialize the debug port + ; + ld a,#0x23 + out (0x2E),a + ld a,#'@' + out (0x2F),a + call INITXT ; set 40 column text mode + ld hl,#hello + call pstring + + di + ; + ; Save a few things before we give the ROM the boot + ; We keep them in the alt registers + ; + ld a,(0x002D) ; machine type + ld d,a ; d' is the machine type + ld bc,(0x0006) ; bc' is the VDP port + ld hl,(0x002B) ; hl' is the info bits + exx + + ; + ; Now find out what slot we are + ; The ROM is not very helpful here because we + ; want to take it out too + ; + in a,(0xA8) + and #0x0C + rrca + rrca ; active slot + + ld b,#0 + ld c,a + ld hl,#EXPTBL + add hl,bc + + bit 7,(hl) + jr z, not_expanded + + inc hl + inc hl + inc hl + inc hl + + ; Work out the required selects + + ld a,(hl) + call ripple + ld b,a + + in a,(0xA8) + call ripple + + ; Returns A = regs for final selection, E = regs + ; to fix sub selection + + ld c,a ; save final setting + + ld a,e ; All slots to our cartridge bank + out (0xA8),a + ld a,b + ld (0xFFFF),a ; Sub-slot 0-2 to our cartridge + ld a,c + out (0xA8),a ; Slots 0-2 to cartridge, 3 restored + + + jr mapped + +not_expanded: + in a,(0xA8) + call ripple + out (0xA8),a +mapped: + di + ; We now have RAM high and 48K of our ROM space low + ; That's enough to get us up and running. We can work out + ; where the rest of the RAM is later on + + ; Begin by setting up our RAM as a normal ROM based SDCC + ; process (for a change!) + ld hl,#s__INITIALIZER + ld de,#s__INITIALIZED + ld bc,#l__INITIALIZER + ldir + ; Now unpack the common space + ld de,#s__COMMONMEM + ld bc,#l__COMMONMEM + ldir + ; Wipe the BSS area + ld hl,#s__DATA + ld d,h + ld e,l + ld (hl),#0 + ld bc,#l__DATA + inc de + dec bc + ldir + ; Switch to the right stack + ld sp,#kstack_top + call init_early + call init_hardware + call _fuzix_main + di +stop: halt + jr stop + +hello: .ascii 'Loading FUZIX...' + .db 13,10,0 diff --git a/Kernel/platform-msx1/config.h b/Kernel/platform-msx1/config.h index bce2e00a..65c61420 100644 --- a/Kernel/platform-msx1/config.h +++ b/Kernel/platform-msx1/config.h @@ -5,43 +5,66 @@ /* Profil syscall support (not yet complete) */ #undef CONFIG_PROFIL /* Multiple processes in memory at once */ -#define CONFIG_MULTI -/* Single tasking - for now while we get it booting */ -#undef CONFIG_SINGLETASK -/* Video terminal, not a serial tty */ -#define CONFIG_VT -/* Fixed banks */ -#define CONFIG_BANK_FIXED -#define MAX_MAPS 16 -/* For now lets play simple and assume 32K banks as possible with some of the - MSX1 megafoo devices */ -#define MAP_SIZE 32768 - -/* As reported to user space - 4 banks, 16K page size */ +#undef CONFIG_MULTI +/* Swap based one process in RAM */ +#define CONFIG_SWAP_ONLY +/* Permit large I/O requests to bypass cache and go direct to userspace */ +#define CONFIG_LARGE_IO_DIRECT +/* One memory bank */ #define CONFIG_BANKS 1 +#define TICKSPERSEC 50 /* Ticks per second */ +#define PROGBASE 0x0000 /* also data base */ +#define PROGLOAD 0x0100 /* also data base */ +#define PROGTOP 0xC000 /* Top of program */ +#define KERNTOP 0xF000 /* Grow buffers to 0xF000 */ + +#define PROC_SIZE 48 /* Memory needed per process (inc udata) */ +#define SWAPDEV (swap_dev) /* A variable for dynamic, or a device major/minor */ +extern unsigned int swap_dev; +#define SWAP_SIZE 0x61 /* 48.5K in blocks (prog + udata) */ +#define SWAPBASE 0x0000 /* start at the base of user mem */ +#define SWAPTOP 0xC000 /* Swap out program */ +#define CONFIG_SPLIT_UDATA /* Adjacent addresses but different bank! */ +#define UDATA_BLKS 1 /* One block of udata */ +#define UDATA_SIZE 512 /* 512 bytes of udata */ +#define MAX_SWAPS 16 /* We will size if from the partition */ +/* Swap will be set up when a suitably labelled partition is seen */ +#define CONFIG_DYNAMIC_SWAP + +/* Video terminal, not a serial tty */ +#define CONFIG_VT +#define CONFIG_FONT6X8 /* Vt definitions */ #define VT_WIDTH 40 #define VT_HEIGHT 24 #define VT_RIGHT 39 #define VT_BOTTOM 23 -#define TICKSPERSEC 50 /* Ticks per second (actually should be dynamic FIXME) */ -#define PROGBASE 0x0000 /* also data base */ -#define PROGLOAD 0x0100 -#define PROGTOP 0x7D00 /* Top of program (uarea stash) */ - -#define BOOT_TTY (512 + 1) /* Set this to default device for stdio, stderr */ - /* In this case, the default is the first TTY device */ - /* Temp FIXME set to serial port for debug ease */ +/* + * When the kernel swaps something it needs to map the right page into + * memory using map_for_swap and then turn the user address into a + * physical address. For a simple banked setup there is no conversion + * needed so identity map it. + */ +#define swap_map(x) ((uint8_t *)(x)) /* We need a tidier way to do this from the loader */ -#define CMDLINE NULL /* Location of root dev name */ +#define CMDLINE NULL /* Location of root dev name */ +#define BOOTDEVICENAMES "hd#,fd,,rd" + +//#define CONFIG_DYNAMIC_BUFPOOL /* we expand bufpool to overwrite the _DISCARD segment at boot */ +#define NBUFS 6 /* Number of block buffers, keep in line with space reserved in zeta-v2.s */ +#define NMOUNTS 2 /* Number of mounts at a time */ + +#define MAX_BLKDEV 2 /* IDE or SD ?? */ +/* We don't use the generic IDE support due to the ghastly MSX slot mappings */ /* Device parameters */ #define NUM_DEV_TTY 2 -#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ -#define NBUFS 10 /* Number of block buffers */ -#define NMOUNTS 4 /* Number of mounts at a time */ -#define platform_discard() +/* VDP as the console */ +#define BOOT_TTY (512 + 1) +#define TTY_INIT_BAUD B115200 /* Hardwired generally */ + +#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ diff --git a/Kernel/platform-msx1/crt0.s b/Kernel/platform-msx1/crt0.s index ca309e5c..df5dec13 100644 --- a/Kernel/platform-msx1/crt0.s +++ b/Kernel/platform-msx1/crt0.s @@ -1,26 +1,24 @@ ; Ordering of segments for the linker. - ; WRS: Note we list all our segments here, even though - ; we don't use them all, because their ordering is set - ; when they are first seen. - .area _BOOT + ; ROM segments first .area _CODE + .area _LOW + .area _HEADER .area _CODE2 + .area _HOME .area _VIDEO .area _CONST - .area _DATA - ; Must be over 0x8000 for HIGHCODE - .area _HIGHCODE + .area _DISCARD + ; ROM section must end with the initializer + .area _INITIALIZER + ; These are unpacked + .area _COMMONMEM .area _INITIALIZED + .area _GSINIT + .area _GSFINAL .area _BSEG .area _BSS .area _HEAP - ; note that areas below here may be overwritten by the heap at runtime, so - ; put initialisation stuff in here - .area _INITIALIZER - .area _GSINIT - .area _GSFINAL - .area _COMMONMEM - .area _DISCARD + .area _DATA ; imported symbols .globl _fuzix_main @@ -34,50 +32,14 @@ .globl l__COMMONMEM .globl s__INITIALIZER .globl kstack_top + .globl interrupt_handler - ; Just for the benefit of the map file - .globl start - - ; startup code @0x100 - .area _CODE - + .globl rst38 ; for checking ; -; Execution begins with us correctly mapped and at 0x0x100 +; First _CODE section ; -; We assume here that the kernel packs below 48K for now we've got a few -; KBytes free but revisit as needed -; -start: di - ld a, #4 ; 0 holds 8K of stuff we might want - out (0xff), a ; if we go the OS route, so use 4 - ; plus "0" is magic so this saves - ; shifting them all by one. - ; Debug port - ld a, #0x23 - out (0x2e), a - ld a, #'@' - out (0x2f), a - ld sp, #kstack_top - ; move the common memory where it belongs - ld hl, #s__DATA - ld de, #s__COMMONMEM - ld bc, #l__COMMONMEM - ldir - ; move the discard area where it belongs - ld de, #s__DISCARD - ld bc, #l__DISCARD - ldir - ; then zero the data area - ld hl, #s__DATA - ld de, #s__DATA + 1 - ld bc, #l__DATA - 1 - ld (hl), #0 - ldir - - call init_early - call init_hardware - call _fuzix_main - di -stop: halt - jr stop + .area _CODE + .ds 0x38 +rst38: jp interrupt_handler + ; FIXME NMI etc ? diff --git a/Kernel/platform-msx1/devices.c b/Kernel/platform-msx1/devices.c index 25ffd32c..5616ae74 100644 --- a/Kernel/platform-msx1/devices.c +++ b/Kernel/platform-msx1/devices.c @@ -3,21 +3,27 @@ #include #include #include -#include +#include #include #include #include +#include #include +#include #include +#include +#include +#include +#include struct devsw dev_tab[] = /* The device driver switch table */ { - /* 0: /dev/fd Floppy disc block devices */ + /* 0: /dev/hd Hard disc block devices (and RAM etc) */ + { blkdev_open, no_close, blkdev_read, blkdev_write, no_ioctl }, + /* 1: /dev/fd Floppy disc block devices */ { fd_open, no_close, fd_read, fd_write, no_ioctl }, - /* 1: /dev/hd Hard disc block devices (and RAM etc) */ - { hd_open, no_close, hd_read, hd_write, no_ioctl }, /* 2: /dev/tty TTY devices */ - { tty_open, tty_close, tty_read, tty_write, tty_ioctl }, + { tty_open, tty_close, tty_read, tty_write, vt_ioctl }, /* 3: /dev/lpr Printer devices */ { lpr_open, no_close, no_rdwr, lpr_write, no_ioctl }, /* 4: /dev/mem etc System devices (one offs) */ @@ -44,9 +50,59 @@ bool validdev(uint16_t dev) */ void device_init(void) { +#if 0 int i; for (i = 0; i < 16; i++) { if (slot_table[i]) kprintf("%d.%d: %x\n", (i>>2) & 3, i & 3, slot_table[i]); } +#endif +#ifdef CONFIG_RTC + inittod(); +#endif + + kprintf ("Running on a "); + if (machine_type == MACHINE_MSX1) { + kprintf("MSX1\n"); + } else if (machine_type == MACHINE_MSX2) { + kprintf("MSX2 "); + } else if (machine_type == MACHINE_MSX2P) { + kprintf("MSX2+ "); + } else if (machine_type == MACHINE_MSXTR) { + kprintf("MSX TurboR "); + } + + /* keyboard layout initialization: default is international, + * localized variations overlay on top */ + memcpy(keyboard, keyboard_int, sizeof(keyboard_int)); + memcpy(shiftkeyboard, shiftkeyboard_int, sizeof(shiftkeyboard_int)); + + if ((infobits & KBDTYPE_MASK) == KBDTYPE_JPN) { + kprintf("JPN "); + memcpy(keyboard, keyboard_jp, sizeof(keyboard_jp)); + memcpy(shiftkeyboard, shiftkeyboard_jp, sizeof(shiftkeyboard_jp)); + } else if ((infobits & KBDTYPE_MASK) == KBDTYPE_UK) { + kprintf("UK "); + memcpy(&shiftkeyboard[2][0],shiftkeyboard_uk, sizeof(shiftkeyboard_uk)); + } else if ((infobits & KBDTYPE_MASK) == KBDTYPE_ES) { + kprintf("ES "); + memcpy(&keyboard[1][0], keyboard_es, sizeof(keyboard_es)); + memcpy(&shiftkeyboard[1][0], shiftkeyboard_es, sizeof(shiftkeyboard_es)); + } else { + kprintf("INT "); + } + + if ((infobits & INTFREQ_MASK) == INTFREQ_60Hz) { + kprintf("60Hz\n"); + ticks_per_dsecond = 6; + } else { + kprintf("50Hz\n"); + ticks_per_dsecond = 5; + } + + /* Default key repeat values in 10ths of seconds */ + keyrepeat.first = 2 * ticks_per_dsecond; + keyrepeat.continual = 1 * ticks_per_dsecond; + + sunrise_probe(); } diff --git a/Kernel/platform-msx1/devide_sunrise.c b/Kernel/platform-msx1/devide_sunrise.c new file mode 100644 index 00000000..57db3c36 --- /dev/null +++ b/Kernel/platform-msx1/devide_sunrise.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include +#include + +uint8_t ide_error; +uint16_t ide_base; +uint8_t *devide_buf; + +struct msx_map sunrise_u, sunrise_k; + +static void delay(void) +{ + timer_t timeout; + + timeout = set_timer_ms(25); + + while(!timer_expired(timeout)) + platform_idle(); +} + +static uint8_t sunrise_transfer_sector(void) +{ + uint8_t drive = (blk_op.blkdev->driver_data & IDE_DRIVE_NR_MASK); + uint8_t mask = drive ? 0xF0 : 0xE0; + uint8_t *addr = blk_op.addr; + + if (blk_op.is_read) + blk_op.blkdev->driver_data |= FLAG_CACHE_DIRTY; + /* Shortcut: this range can only occur for a user mode I/O */ + if (addr >= (uint8_t *)0x3E00U && addr <= (uint8_t *)0x8000U) { + blk_op.addr = tmpbuf(); + blk_op.is_user = 0; + if (do_ide_xfer(mask) == 0) + goto fail; + uput(blk_op.addr, addr, 512); + tmpfree(blk_op.addr); + return 1; + } + if (do_ide_xfer(mask)) + return 1; +fail: + if (ide_error == 0xFF) + kprintf("ide%d: timeout.\n", drive); + else + kprintf("ide%d: status %x\n", drive, ide_error); + return 0; +} + +static int sunrise_flush_cache(void) +{ + uint8_t drive; + + drive = blk_op.blkdev->driver_data & IDE_DRIVE_NR_MASK; + /* check drive has a cache and was written to since the last flush */ + if((blk_op.blkdev->driver_data & (FLAG_WRITE_CACHE | FLAG_CACHE_DIRTY)) + != (FLAG_WRITE_CACHE | FLAG_CACHE_DIRTY)) + return 0; + + if (do_ide_flush_cache(drive ? 0xF0 : 0xE0)) { + udata.u_error = EIO; + return -1; + } + return 0; +} + +static void sunrise_init_drive(uint8_t drive) +{ + uint8_t mask = drive ? 0xF0 : 0xE0; + blkdev_t *blk; + + kprintf("IDE drive %d: ", drive); + devide_buf = tmpbuf(); + if (do_ide_init_drive(mask) == NULL) + goto failout; + if (!(devide_buf[99] & 0x02)) { + kputs("LBA unsupported.\n"); + goto failout; + } + blk = blkdev_alloc(); + if (!blk) + goto failout; + blk->transfer = sunrise_transfer_sector; + blk->flush = sunrise_flush_cache; + blk->driver_data = drive; + + if( !(((uint16_t*)devide_buf)[82] == 0x0000 && ((uint16_t*)devide_buf)[83] == 0x0000) || + (((uint16_t*)devide_buf)[82] == 0xFFFF && ((uint16_t*)devide_buf)[83] == 0xFFFF) ){ + /* command set notification is supported */ + if(devide_buf[164] & 0x20){ + /* write cache is supported */ + blk->driver_data |= FLAG_WRITE_CACHE; + } + } + + /* read out the drive's sector count */ + blk->drive_lba_count = le32_to_cpu(*((uint32_t*)&devide_buf[120])); + + /* done with our temporary memory */ + tmpfree(devide_buf); + blkdev_scan(blk, SWAPSCAN); + return; +failout: + tmpfree(devide_buf); +} + +void sunrise_probe(uint8_t slot, uint8_t subslot) +{ + uint8_t i = slot; + + if (subslot != 0xFF) + i |= 0x80 | (subslot << 2); + + /* Generate and cache the needed mapping table */ + memcpy(&sunrise_k, map_slot1_kernel(i), sizeof(sunrise_k)); + memcpy(&sunrise_u, map_slot1_user(i), sizeof(sunrise_k)); + + do_ide_begin_reset(); + delay(); + do_ide_end_reset(); + delay(); + for (i = 0; i < IDE_DRIVE_COUNT; i++) + sunrise_init_drive(i); +} diff --git a/Kernel/platform-msx1/devide_sunrise.h b/Kernel/platform-msx1/devide_sunrise.h new file mode 100644 index 00000000..c9ed1123 --- /dev/null +++ b/Kernel/platform-msx1/devide_sunrise.h @@ -0,0 +1,14 @@ +extern uint8_t *do_ide_init_drive(uint8_t drive) __z88dk_fastcall; +extern void do_ide_begin_reset(void) __z88dk_fastcall; +extern void do_ide_end_reset(void) __z88dk_fastcall; +extern uint8_t do_ide_flush_cache(uint8_t drive) __z88dk_fastcall; +extern uint8_t do_ide_xfer(uint8_t drive) __z88dk_fastcall; + +extern void sunrise_probe(uint8_t slot, uint8_t subslot); + +#define IDE_DRIVE_COUNT 2 + +#define FLAG_WRITE_CACHE 0x80 +#define FLAG_CACHE_DIRTY 0x40 + +#define IDE_DRIVE_NR_MASK 0x03 diff --git a/Kernel/platform-msx1/devtty.c b/Kernel/platform-msx1/devtty.c index 84cfcffa..864142f1 100644 --- a/Kernel/platform-msx1/devtty.c +++ b/Kernel/platform-msx1/devtty.c @@ -5,10 +5,20 @@ #include #include #include +#include #undef DEBUG /* UNdefine to delete debug code sequences */ __sfr __at 0x2F tty_debug2; +__sfr __at 0xAA kbd_row_set; +__sfr __at 0xA9 kbd_row_read; + +uint8_t keyboard[11][8]; +uint8_t shiftkeyboard[11][8]; +uint8_t keymap[11]; + +struct vt_repeat keyrepeat; +uint8_t vtattr_cap; char tbuf1[TTYSIZ]; char tbuf2[TTYSIZ]; @@ -42,12 +52,12 @@ ttyready_t tty_writeready(uint8_t minor) void tty_putc(uint8_t minor, unsigned char c) { minor; + tty_debug2 = c; // // if (minor == 1) { vtoutput(&c, 1); // return; // } - tty_debug2 = c; } int tty_carrier(uint8_t minor) @@ -70,14 +80,13 @@ void tty_data_consumed(uint8_t minor) { } -#if 0 -static uint8_t keymap[10]; -static uint8_t keyin[10]; +static uint8_t kbd_timer; +static uint8_t keyin[11]; static uint8_t keybyte, keybit; static uint8_t newkey; static int keysdown = 0; -static uint8_t shiftmask[10] = { - 3, 3, 2, 0, 0, 0, 0, 0x10, 0, 0 +static uint8_t shiftmask[11] = { + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; static void keyproc(void) @@ -85,11 +94,11 @@ static void keyproc(void) int i; uint8_t key; - for (i = 0; i < 10; i++) { + for (i = 0; i < 11; i++) { key = keyin[i] ^ keymap[i]; if (key) { int n; - int m = 128; + int m = 1; for (n = 0; n < 8; n++) { if ((key & m) && (keymap[i] & m)) { if (!(shiftmask[i] & m)) @@ -103,111 +112,82 @@ static void keyproc(void) keybit = n; } } - + m += m; } } keymap[i] = keyin[i]; } } -uint8_t keyboard[10][8] = { - {0, 0, 0, 10, '?' /*left */ , 0, 0, 0}, - {0, '5', 0, 0, ' ', 27, 0, 0}, - {0, 0, 0, 0, '\t', '1', 0, 0}, - {'d', 's', 0, 'e', 'w', 'q', '2', '3'}, - {'f', 'r', 0, 'a', 'x', 'z', 0, '4'}, - {'c', 'g', 'y', 't', 'v', 'b', 0, 0}, - {'n', 'h', '/', '#', '?' /*right */ , 127, '?' /*down */ , '6'}, - {'k', 'm', 'u', 0, '?' /*up */ , '\\', '7', '='}, - {',', 'j', 'i', '\'', '[', ']', '-', '8'}, - {'.', 'o', 'l', ';', 'p', 8, '9', '0'} -}; - -uint8_t shiftkeyboard[10][8] = { - {0, 0, 0, 10, '?' /*left */ , 0, 0, 0}, - {0, '%', 0, 0, ' ', 3, 0, 0}, - {0, 0, 0, 0, '\t', '!', 0, 0}, - {'D', 'S', 0, 'E', 'W', 'Q', '"', '?' /* pound */ }, - {'F', 'R', 0, 'A', 'X', 'Z', 0, '$'}, - {'C', 'G', 'Y', 'T', 'V', 'B', 0, 0}, - {'N', 'H', '?', '~', '?' /*right */ , 127, '?' /*down */ , '^'}, - {'K', 'M', 'U', 0, '?' /*up */ , '|', '&', '+'}, - {'<', 'J', 'I', '@', '{', '}', '_', '*'}, - {'>', 'O', 'L', ':', 'P', 8, '(', ')'} -}; - static uint8_t capslock = 0; static void keydecode(void) { uint8_t c; - if (keybyte == 2 && keybit == 7) { + if (keybyte == 6 && keybit == 3) { capslock = 1 - capslock; return; } - if (keymap[0] & 3) /* shift */ + if (keymap[6] & 3 ) { /* shift or control */ c = shiftkeyboard[keybyte][keybit]; - else + /* VT switcher */ +#if 0 + if (c == KEY_F1 || c == KEY_F2 || c == KEY_F3 || c == KEY_F4) { + if (inputtty != c - KEY_F1) { + inputtty = c - KEY_F1; + vtexchange(); /* Exchange the video and backing buffer */ + } + return; + } +#endif + } else c = keyboard[keybyte][keybit]; - if (keymap[1] & 2) { /* control */ + + if (keymap[6] & 2) { /* control */ if (c > 31 && c < 127) c &= 31; } - if (keymap[1] & 1) { /* function: not yet used */ - ; - } -// kprintf("char code %d\n", c); - if (keymap[2] & 1) { /* symbol */ - ; - } + if (capslock && c >= 'a' && c <= 'z') c -= 'a' - 'A'; - if (keymap[7] & 0x10) { /* menu: not yet used */ - ; + + /* TODO: function keys (F1-F10), graph, code */ + + vt_inproc(/*inputtty +*/1, c); +} + +void update_keyboard() +{ + int n; + uint8_t r; + + /* encode keyboard row in bits 0-3 0xAA, then read status from 0xA9 */ + for (n =0; n < 11; n++) { + r = kbd_row_set & 0xf0 | n; + kbd_row_set = r; + keyin[n] = ~kbd_row_read; } - tty_inproc(1, c); } -#endif -void tty_interrupt(void) +void kbd_interrupt(void) { -#if 0 - uint8_t a = irqmap; - uint8_t c; - if (!(a & 2)) - wakeup(&ttydata[2]); - if (!(a & 1)) { - /* work around sdcc bug */ - c = uarta; - tty_inproc(2, c); - } - if (!(a & 8)) { - keyin[0] = kmap0; - keyin[1] = kmap1; - keyin[2] = kmap2; - keyin[3] = kmap3; - keyin[4] = kmap4; - keyin[5] = kmap5; - keyin[6] = kmap6; - keyin[7] = kmap7; - keyin[8] = kmap8; - keyin[9] = kmap9; /* This resets the scan for 10mS on */ - - newkey = 0; - keyproc(); - if (keysdown < 3 && newkey) + newkey = 0; + update_keyboard(); + keyproc(); + + if (keysdown && keysdown < 3) { + if (newkey) { + keydecode(); + kbd_timer = keyrepeat.first * ticks_per_dsecond; + } else if (! --kbd_timer) { keydecode(); - timer_interrupt(); + kbd_timer = keyrepeat.continual * ticks_per_dsecond; + } } - - /* clear the mask */ - irqmap = a; -#endif } /* This is used by the vt asm code, but needs to live in the kernel */ uint16_t cursorpos; - diff --git a/Kernel/platform-msx1/devtty.h b/Kernel/platform-msx1/devtty.h index c387e904..0382d74d 100644 --- a/Kernel/platform-msx1/devtty.h +++ b/Kernel/platform-msx1/devtty.h @@ -1,4 +1,12 @@ #ifndef __DEVTTY_DOT_H__ #define __DEVTTY_DOT_H__ +#define KEY_COLS 11 +#define KEY_ROWS 8 +extern uint8_t keymap[11]; +extern uint8_t keyboard[11][8]; +extern uint8_t shiftkeyboard[11][8]; + +extern void kbd_interrupt(void); + #endif diff --git a/Kernel/platform-msx1/discard.c b/Kernel/platform-msx1/discard.c new file mode 100644 index 00000000..54f6e5ae --- /dev/null +++ b/Kernel/platform-msx1/discard.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include + +extern uint8_t ramtab[], kernel_map[], current_map[], user_map[]; +extern uint8_t subslots; +extern uint16_t vdpport; +extern uint8_t vdptype; + +static const char *vdpnametab[] = { + "TMS9918A", + "V9938", + "V9958" +}; + +void map_init(void) +{ + uint8_t *bp = kernel_map; + uint8_t *kp; + uint8_t *rp; + uint8_t i; + uint8_t pp; + const char *vdpname = "??"; + + if (vdptype < 3) + vdpname = vdpnametab[vdptype]; + + kprintf("VDP %s@%x\n", vdpname, vdpport); + kprintf("Subslots %x\n", subslots); + + kprintf("Kernel map %x %x %x %x %x %x\n", + *bp, bp[1], bp[2], bp[3], bp[4], bp[5]); + + bp = current_map; + kprintf("Current map %x %x %x %x %x %x\n", + *bp, bp[1], bp[2], bp[3], bp[4], bp[5]); + + bp = ramtab; + kprintf("RAM reported at %x:%x %x:%x %x:%x\n", + *bp, bp[1], bp[2], bp[3], bp[4], bp[5]); + + bp = user_map + 1; + rp = ramtab + 4; + pp = 0; + + memcpy(user_map, kernel_map, 6); + + /* Compute the user mapping from the top down */ + for (i = 0; i < 3; i++) { + /* We found now RAM for this 16K block - fail */ + if (*rp == 0xFF) + panic("can't find RAM"); + /* This is subslotted. Add this subslot to the subslots we must + program, and remember the subslot info */ + if (rp[1] != 0xFF) { + bp[*rp] = (rp[1] << 4) | (rp[1] << 2) | rp[1] | (kernel_map[*rp + 1] & 0xC0); + user_map[0] |= 1 << *rp; + } + kp--; + pp <<= 2; + pp |= *rp; + rp -= 2; + } + /* Slot map with the common from the kernel map */ + user_map[5] = pp | (kernel_map[5] & 0xC0); + bp = user_map; + kprintf("User map %x %x %x %x %x %x\n", + *bp, bp[1], bp[2], bp[3], bp[4], bp[5]); +} + + +void platform_discard(void) +{ + /* Until we tackle the buffers */ +} + +/* + * This function is called for partitioned devices if a partition is found + * and marked as swap type. The first one found will be used as swap. We + * only support one swap device. + */ +void platform_swap_found(uint8_t letter, uint8_t m) +{ + blkdev_t *blk = blk_op.blkdev; + uint16_t n; + if (swap_dev != 0xFFFF) + return; + letter -= 'a'; + kputs("(swap) "); + swap_dev = letter << 4 | m; + n = blk->lba_count[m - 1] / SWAP_SIZE; + if (n > MAX_SWAPS) + n = MAX_SWAPS; + while(n) + swapmap_add(n--); +} diff --git a/Kernel/platform-msx1/fuzix.lnk b/Kernel/platform-msx1/fuzix.lnk index b98ed4bb..ae7c619b 100644 --- a/Kernel/platform-msx1/fuzix.lnk +++ b/Kernel/platform-msx1/fuzix.lnk @@ -1,12 +1,13 @@ -mwxuy -i fuzix.ihx -b _CODE=0x0000 --b _COMMONMEM=0xF000 --b _DISCARD=0xE000 +-b _HEADER=0x4000 +-b _COMMONMEM=0xC000 -l z80 -platform-msx1/bootrom.rel platform-msx1/crt0.rel +platform-msx1/cartridge.rel platform-msx1/commonmem.rel +platform-msx1/slots.rel platform-msx1/msx1.rel platform-msx1/vdp.rel start.rel @@ -14,13 +15,17 @@ version.rel lowlevel-z80.rel platform-msx1/tricks.rel platform-msx1/main.rel +platform-msx1/discard.rel timer.rel kdata.rel platform-msx1/devtty.rel platform-msx1/devfd.rel -platform-msx1/devhd.rel platform-msx1/devlpr.rel platform-msx1/devices.rel +platform-msx1/blkdev.rel +platform-msx1/mbr.rel +platform-msx1/devide_sunrise.rel +platform-msx1/sunrise.rel devio.rel filesys.rel process.rel @@ -33,9 +38,10 @@ syscall_proc.rel syscall_other.rel mm.rel swap.rel -bankfixed.rel +simple.rel tty.rel vt.rel +font6x8.rel devsys.rel usermem.rel usermem_std-z80.rel diff --git a/Kernel/platform-msx1/kbdmatrix.h b/Kernel/platform-msx1/kbdmatrix.h new file mode 100644 index 00000000..16ea90dd --- /dev/null +++ b/Kernel/platform-msx1/kbdmatrix.h @@ -0,0 +1,64 @@ +#ifndef __KBDMATRIX_DOT_H__ +#define __KBDMATRIX_DOT_H__ +/* + * International key matrix + */ +const uint8_t keyboard_int[11][8] = { + {'0','1','2', '3','4','5','6','7'}, + {'8','9','-','=','\\','[',']',';'}, + { 39, '`', ',', '.','/',' ','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/*SHIFT*/,0/*CTRL*/,0/*GRPH*/,0/*CAPS*/,0/*CODE*/ , KEY_F1 , KEY_F2 , KEY_F3 }, + {KEY_F4 , KEY_F5, KEY_ESC , '\t', KEY_STOP ,KEY_BS , 0 , 13}, + {32 , KEY_HOME, KEY_INSERT , KEY_DEL, KEY_LEFT , KEY_UP , KEY_DOWN , KEY_RIGHT}, + {'*','+','/','0','1' ,'2','3','4'}, + {'5','6','7','8','9' ,'-',',','.'} +}; + +const uint8_t shiftkeyboard_int[11][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/*SHIFT*/,0/*CTRL*/,0/*GRPH*/,0/*CAPS*/,0/*CODE*/, KEY_F1 , KEY_F2 , KEY_F3 }, + {KEY_F4 , KEY_F5, KEY_ESC , '\t', KEY_STOP ,KEY_BS , 0/*SELECT*/ , 13}, + {32 ,KEY_HOME, KEY_INSERT , KEY_DEL, KEY_LEFT , KEY_UP , KEY_DOWN , KEY_RIGHT}, + {'*','+','/','0','1' ,'2','3','4'}, + {'5','6','7','8','9' ,'-',',','.'} +}; + +/* + * Japan overlay + */ +const uint8_t keyboard_jp[3][8] = { + {'0','1','2', '3','4','5','6','7'}, + {'8','9','-','^',KEY_YEN,'@','[',';'}, + {':',']', ',', '.','/',' ','a','b'}}; + +const uint8_t shiftkeyboard_jp[3][8] = { + {' ','!','"', '#','$','%','&',39}, + {'(',')','=','~','|','`','{','+'}, + {'*','}','<','>','?','_','A','B'}}; +/* + * UK overlay + */ +const uint8_t shiftkeyboard_uk[1][8] = { + {39,'`', ',', '.','/',KEY_POUND,'A','B'}}; /* row 2 */ + +/* + * Spanish overlay + */ +const uint8_t keyboard_es[2][8] = { + {'8','9','-','=','\\','[',']', 'N'/* Ñ */}, /* row 1 */ + {39, ':', ',', '.','/',' ','a','b'}}; + +const uint8_t shiftkeyboard_es[2][8] = { + {'*','(','_','+','|','{','}', 'n' /* ñ */}, /* row 1 */ + {'"',':','<','>','?',' ','A','B'}}; + + +#endif diff --git a/Kernel/platform-msx1/main.c b/Kernel/platform-msx1/main.c index 0ae394b7..150ab172 100644 --- a/Kernel/platform-msx1/main.c +++ b/Kernel/platform-msx1/main.c @@ -5,8 +5,12 @@ #include /* These are set by the msx startup asm code */ -uint16_t vdpport = 0x99 + 256 * 40; +extern uint16_t vdpport; uint16_t infobits; +uint16_t swap_dev = 0xFFFF; +uint16_t ramtop = 0xC000; +uint8_t machine_type; +uint8_t vdptype; void platform_idle(void) { @@ -15,6 +19,8 @@ void platform_idle(void) __endasm; } +/* Some of this is discard stuff */ + uint8_t platform_param(char *p) { used(p); @@ -25,26 +31,12 @@ void do_beep(void) { } -/* - * We have one bank per 32K and we number them upwards from 1 as the - * core kernel code uses 0 for swapped out. - */ - -void pagemap_init(void) -{ - int i = procmem / 32; - while (i > 0) - pagemap_add(i--); -} - -void map_init(void) -{ - if (procmem == 0) - panic("MegaRAM not found.\n"); -} - - void platform_interrupt(void) { - timer_interrupt(); + uint8_t r = in((uint8_t)vdpport); + if (r & 0x80) { + kbd_interrupt(); + timer_interrupt(); + } } + diff --git a/Kernel/platform-msx1/msx.h b/Kernel/platform-msx1/msx.h new file mode 100644 index 00000000..55323422 --- /dev/null +++ b/Kernel/platform-msx1/msx.h @@ -0,0 +1,36 @@ +#ifndef __MSX_DOT_H__ +#define __MSX_DOT_H__ + +extern uint8_t machine_type; +extern uint16_t infobits; + +#define MACHINE_MSX1 0 +#define MACHINE_MSX2 1 +#define MACHINE_MSX2P 2 +#define MACHINE_MSXTR 3 + +#define CHARSET_MASK (0xF) +#define CHARSET_JPN 0 +#define CHARSET_INT 1 +#define CHARSET_KR 2 + +#define INTFREQ_MASK (1 << 7) +#define DATEFMT_MASK (7 << 4) +#define INTFREQ_60Hz 0 +#define INTFREQ_50Hz 1 + +#define KBDTYPE_MASK (0xF) +#define KBDTYPE_JPN 0 +#define KBDTYPE_INT 1 +#define KBDTYPE_FR 2 +#define KBDTYPE_UK 3 +#define KBDTYPE_DIN 4 +#define KBDTYPE_ES 6 + +struct msx_map { + uint8_t private[6]; +}; + +extern uint8_t *map_slot1_kernel(uint8_t slotinfo) __z88dk_fastcall; +extern uint8_t *map_slot1_user(uint8_t slotinfo) __z88dk_fastcall; +#endif diff --git a/Kernel/platform-msx1/msx1.s b/Kernel/platform-msx1/msx1.s index 7c4f7d77..ebd40beb 100644 --- a/Kernel/platform-msx1/msx1.s +++ b/Kernel/platform-msx1/msx1.s @@ -1,5 +1,5 @@ ; -; MSX2 hardware support +; MSX1 hardware support ; .module msx1 @@ -9,12 +9,7 @@ .globl init_hardware .globl interrupt_handler .globl _program_vectors - .globl map_kernel - .globl map_process - .globl map_process_always - .globl map_save - .globl map_restore - .globl _slot_table + .globl _set_initial_map .globl _need_resched ; video driver @@ -24,15 +19,15 @@ .globl _platform_monitor .globl outchar - ; imported symbols - .globl _ramsize - .globl _procmem - - .globl _tty_inproc .globl unix_syscall_entry .globl _platform_reboot .globl nmi_handler .globl null_handler + .globl map_process + .globl map_kernel + .globl _vdp_load_font + + .globl find_ram ; debug symbols .globl outcharhex @@ -43,7 +38,9 @@ ; stuff to save .globl _vdpport + .globl _vdptype .globl _infobits + .globl _machine_type ; ; vdp - we must initialize this bit early for the vt @@ -58,16 +55,6 @@ ; ----------------------------------------------------------------------------- .area _COMMONMEM -trapmsg: .ascii "Trapdoor: SP=" - .db 0 -trapmsg2: .ascii ", PC=" - .db 0 -tm_user_sp: .dw 0 - -tm_stack: - .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -tm_stack_top: - ; Ideally return to any debugger/monitor _platform_monitor: di @@ -89,43 +76,83 @@ _need_resched: .area _CODE init_early: - ld a, #'F' + ld a, #'*' out (0x2F), a + ; called with e'=vdp d'=machine type + ; HL info bits + exx + ld a,d + ld (_machine_type),a + ld a,c + inc a + ld (_vdpport),a + ld (_infobits),hl ret init_hardware: - ld a, #'U' + ld a, #'0' out (0x2F), a - ; save the useful bits of low memory first - ld hl, (0x2B) - ld (_infobits), a -; ld a, (0x07) -; ld (_vdpport), a - ; Size RAM - call size_memory + call _set_initial_map - ; set up interrupt vectors for the kernel mapped low page and - ; data area - ld hl, #0 - push hl - call _program_vectors - pop hl + ld a, #'1' + out (0x2F), a - ld a, #'Z' + call find_ram + + ld a, #'2' out (0x2F), a ; Program the video engine + + ld bc,(_vdpport) + ; Play with statius register 2 + dec c + ld a,#0x8F + out (c),a + nop + nop + in a,(c) + ld a,#2 + out (c),a + ld a,#0x8F + out (c),a + nop + nop +vdpwait: in a,(c) + and #0xE0 + add a + jr nz, not9918a ; bit 5 or 6 + jr nc, vdpwait ; not bit 7 + ; we vblanked out - TMS9918A + xor a + ld (_vdptype),a + jr vdp_setup +not9918a: ; Use the version register + ld a,#1 + out (c),a + ld a,#0x8F + out (c),a + nop + nop + in a,(c) + rrca + and #0x1F + inc a + ld (_vdptype),a +vdp_setup: call vdpinit + call _vdp_load_font - ld a, #'I' + ld a, #'3' out (0x2F), a im 1 ; set CPU interrupt mode call _vtinit ; init the console video - ld a, #'X' + ld a, #'4' out (0x2F), a + ret @@ -134,24 +161,6 @@ init_hardware: .area _COMMONMEM -; -; Called with interrupts off. See if we can work out how much RAM -; there is -; -size_memory: - call megaram_scan - ld l, a - ld h, #0 - add hl, hl - add hl, hl - add hl, hl ; megaram for the user space - ld (_procmem), hl - ld de, #0x40 ; main memory for kernel - add hl, de - ld (_ramsize), hl - call rom_scan - ret - _program_vectors: ; we are called, with interrupts disabled, by both newproc() and crt0 ; will exit with interrupts off @@ -190,123 +199,7 @@ _program_vectors: ld (0x0066), a ; Set vector for NMI ld hl, #nmi_handler ld (0x0067), hl - jr map_kernel - -; -; All registers preserved -; -map_process_always: - push hl - ld hl, #U_DATA__U_PAGE - call map_process_2 - pop hl - ret -; -; HL is the page table to use, A is eaten, HL is eaten -; -map_process: - ld a, h - or l - jr nz, map_process_2 -; -; Map in the kernel low area (0x0000-0x7FFF) -; -map_kernel: - push af - ld a, i - push af - di - ld a, #'K' - out (0x2f), a - ld a, (map_cache) ; Already mapped - or a - jr z, map_kernel_out - push bc - push de - push hl - call restoreslotmap - xor a - ld (map_cache), a - pop hl - pop de - pop bc -map_kernel_out: - ld a, #'k' - out (0x2f), a - pop af - jp po, map_kernel_di - ei -map_kernel_di: - pop af - ret -map_process_2: - push af - ld a, i - push af - di - push de - ld a, #'U' - out (0x2f), a - ld a, (map_cache) - or a - jr nz, inter_mega - ld a, #'M' - out (0x2f), a - call map_megaram - ld a, #'m' - out (0x2f), a -inter_mega: - ld a, (hl) - ld (map_cache), a - dec a ; turn the bank number into 0 offset - add a, a - add a, a ; 4 pages per process - out (0x8E), a - ld (0), a - inc a - ld (1), a - inc a - ld (0x4000), a - inc a - ld (0x4001), a - in a, (0x8E) - pop de - ld a, #'u' - out (0x2f), a - pop af - jp po, map_proc_di - ei -map_proc_di:pop af - ret -; -; Restore a saved mapping. We are guaranteed that we won't switch -; common copy between save and restore. Preserve all registers -; -map_restore: - push hl - push af - ld hl, #map_saved - ld a, (hl) ; Kernel ? - or a - jr nz, map_ruser - ld hl, #0 ; Do a kernel remap not a user one -map_ruser: - call map_process ; Map it - pop af - pop hl - ret -; -; Save the current mapping (trivial) -; -map_save: - push af - ld a, (map_cache) - ld (map_saved), a - pop af - ret - -map_cache: .db 0 -map_saved: .db 0 + jp map_kernel ; emulator debug port for now outchar: @@ -315,404 +208,3 @@ outchar: pop af ret -; -; On entry D is repeating slot pattern, E repeating SSLOT pattern -; -; -; We play with 0x0000-0x7FFF (for user mappings) and with -; 0xC000-0xFFFF (for the stupid sslot stuff), so this wants to live -; in 0x8000-0xBFFF. We must also be very careful with our sslot -; read/writes as while we do the sslot jiggery-pokery we have unmapped -; our stack. -; -; - .area _HIGHCODE -setslot0: - push bc - push de - ld a, #'S' - out (0x2f), a - ld a, #'0' - out (0x2f), a - ld a, d - call outcharhex - ld a, e - call outcharhex - in a, (0xA8) - and #0xFC ; For final setting - ld b, a - and #0x3C ; For setting sslot - ld c, a - ld a, d - and #0xC3 - or c - out (0xA8), a ; Map the right slot in bank 0 and 3 - ld a, e - and #0x03 - ld e, a - ld a, (0xFFFF) - cpl - and #0xFC - or e - ld (0xFFFF), a ; Set SSLOT - ld a, d - and #0x03 - or b - out (0xA8), a - call outcharhex - ld a, #'/' - out (0x2f), a - pop de - pop bc - ret - -; -; This can't live low as its used by _HIGHCODE stuff -; - -setslot1: - push bc - push de - ld a, #'S' - out (0x2f), a - ld a, #'1' - out (0x2f), a - ld a, d - call outcharhex - ld a, e - call outcharhex - in a, (0xA8) - and #0xF3 ; For final setting - ld b, a - and #0x33 ; For setting sslot - ld c, a - ld a, d - and #0xCC - or c - out (0xA8), a ; Map the right slot in bank 1 and 3 - ld a, e - and #0x0C - ld e, a - ld a, (0xFFFF) - cpl - and #0xF3 - or e - ld (0xFFFF), a ; Set SSLOT - ld a, d - and #0x0C - or b - out (0xA8), a - call outcharhex - ld a, #'/' - out (0x2f), a - pop de - pop bc - ret - -; -; Save the slot/subslot map. This requires much mucking about -; remapping the top bank -; -saveslotmap: - ld hl, #slotsave - ld bc, #0x05A8 ; count for slots (+ 1 for ini), port - ini - in d, (c) ; Load again for working - ld e, #0 ; Slot number -saveslotmap1: - ld a, d - and #0x3f ; Mask off top bank - or e - out (c), a - ld a, (0xFFFF) - cpl - ld (hl), a - inc hl - ld a, #0x40 - add e - ld e, a - djnz saveslotmap1 - out (c), d - ret - -restoreslotmap: - ld hl, #slotsave - ld bc, #0x05A8 - outi - in d, (c) - ld e, #0 ; Slot number -resslotmap1: - ld a, d - and #0x3f ; Mask off top bank - or e - out (c), a - ld a, (hl) - ld (0xFFFF), a ; Load the subslot into each - inc hl - ld a, #0x40 - add e - ld e, a - djnz resslotmap1 - out (c), d - ret - -slotsave: .db 0 - .db 0, 0, 0, 0 - -slotexpanded: - push hl - push de - in a, (0xA8) - call outcharhex - in a, (0xA8) - ld e, a - and #0x3F - ld d, a - and #0x0C - rlca - rlca - rlca - rlca - or d - push af - call outcharhex - pop af - out (0xA8), a - ld hl, #0xFFFF - ld a, (hl) ; Read cpl value - cpl ; A is now the write value - ld (hl), a ; Write it - cpl - cp (hl) - jr z, isexp - ld a, #'N' - out (0x2f), a -isexp: - ld a,e - out (0xA8), a - pop de - pop hl - ret ; Z = Expanded - -slot: .db 0 -slots: .db 0 - -; -; Scan slot 1 for all the slots and subslots. For each slot/sub -; call hl' with the registers exchanged for the scanner routines -; use -; -slotscan1: - call saveslotmap - - ld d, #0 -nextslot: - ld e, #0 - call setslot1 - exx - call callhl ; Scan - exx - call slotexpanded - jr nz, noexpanded - jr expanders -nextexpanded: - call setslot1 - exx -; call callhl ; Scan - exx -expanders: - ld a, #0x55 - add e - ld e, a - ld a, #'.' - out (0x2f), a - jr nc, nextexpanded -noexpanded: - ld a, #'|' - out (0x2f), a - ld a, d - add #0x55 - ld d, a - jr nc, nextslot - call restoreslotmap - ret - -callhl: jp (hl) - -megaset0: xor a -megaset: out (c), a ; ROM mode - ld (hl), a ; page A - in a, (c) ; RAM mode - ret -; -; Hunt for a MegaRAM (assumes c set correctly by caller) -; -; The basic idea is -; Stick it in paging mode -; Page it to 0 -; Stick it in RAM mode -; Write a value -; Stick it back in paging mode -; Write 0 -; -; If it's a MegaRAM the memory will remain as the value, if it's real RAM -; it will be zeroed by the paging write back -; -megaram_p: - ld d, (hl) - call megaset0 - ld (hl), b ; Now should store this either way - call megaset0 ; Should not change if megaram - ld a, (hl) - ld (hl), d ; Restore old bits - cp b ; Z = MegaRAM (probably) - ret - -megaram_chk: - ld hl, #0x5FFF - ld bc, #0xA58E ; A5 is the pattern we use - call megaram_p - ret nz ; Not MegaRAM - ld b, #0x5A - call megaram_p ; Paranoia check - ret - -megaram_scan_f: - push hl - call megaram_chk - pop hl - ret nz - exx - ld (megaram_i), de ; save de' (slot/subslot info) - exx - ret -megaram_i: .dw 0 - -megaram_scan: - exx - ld hl, #megaram_scan_f - exx - call slotscan1 - ld de, (megaram_i) - ld a, d - or e - jr z, megaram_no - ld a, #'M' - out (0x2f), a - call setslot1 ; select the megaram - ld bc, #0x8E - ld hl, #0x4000 - call megaset0 - ld (hl), #0x55 -megaram_size: - ld a, b - call megaset - ld a, (hl) - cp #0x55 - jr z, megawrap_maybe -megaram_size2: - inc b - jr nz, megaram_size -megaram_sized: - call restoreslotmap - ; b is the size in 8K pages - ld a, b - rra - rra - and #0x3F - ret nz - add #0x40 - ret -megaram_no: - xor a - ret - -megawrap_maybe: - call megaset0 - ld (hl), #0xAA - ld a, b - call megaset - ld a, (hl) - cp #0xAA ; both patterns worked - jr z, megaram_sized - jr megaram_size2 ; false alarm, carry on - -map_megaram: - ld de, (megaram_i) - push de - call setslot0 - pop de - call setslot1 - ret - -; -; Find each rom and insert it by hash code into the slot table. We -; can then use this to find out where things like floppy drives have -; been hidden. Right now we are only scanning 0x4000-0x7FFF, but we -; will probably need to scan 0x8000-0xBFFF as well eventually (which -; will be a right PITA as we'll need slotscan2 and all the supporting -; logic to be in 0x0000-0x3FFF!) -; -rom_scan: - ld hl, #rom_scan_f - exx - call slotscan1 - ret - -rom_scan_f: - ld a, #'*' - out (0x2f), a - ld a, (0x4000) - cp #'A' - ret nz - ld a, (0x4001) - cp #'B' - ret nz - ; - ; ROM found. Preserve HL as its the call vector - ; - ld a, #'R' - out (0x2f), a - push hl - ld hl, #0x4002 - ld bc, #2048 - ld de, #0 - ; Use the low 2K as a checksum identifier -rom_scan_1: - ld a, (hl) - add e - ld e, a - ld a, d - adc #0 - ld d, a - inc hl - djnz rom_scan_1 - dec c - jr nz,rom_scan_1 - exx - push de ;slot info - exx - pop bc - ld a, b - and #0x0C - ld b, a - ld a, c - and #0x03 - or b - add a - ld c, a - ld b, #0 - ld hl, #_slot_table - add hl, bc - ld (hl), e - inc hl - ld (hl), d - pop hl - ret - -; Needs to be outside of 0x4000-0x7FFF -_slot_table: - .dw 0,0,0,0 - .dw 0,0,0,0 - .dw 0,0,0,0 - .dw 0,0,0,0 diff --git a/Kernel/platform-msx1/rules.mk b/Kernel/platform-msx1/rules.mk new file mode 100644 index 00000000..0513d8d1 --- /dev/null +++ b/Kernel/platform-msx1/rules.mk @@ -0,0 +1,16 @@ +# +# Push the execve code high to make room +# +export CROSS_CC_SEG1=--codeseg CODE2 +export CROSS_CC_SEG2=--codeseg CODE +export CROSS_CC_SEG3=--codeseg CODE +export CROSS_CC_VIDEO=--codeseg CODE +export CROSS_CC_FONT=--codeseg CODE +# +export CROSS_CC_SYS1=--codeseg CODE +export CROSS_CC_SYS2=--codeseg CODE2 +export CROSS_CC_SYS3=--codeseg CODE2 +export CROSS_CC_SYS4=--codeseg CODE2 +export CROSS_CC_SYS5=--codeseg COMMONMEM +export CROSS_CC_SEGDISC=--codeseg DISCARD + diff --git a/Kernel/platform-msx1/slots.s b/Kernel/platform-msx1/slots.s new file mode 100644 index 00000000..564500f8 --- /dev/null +++ b/Kernel/platform-msx1/slots.s @@ -0,0 +1,637 @@ +; +; The MSX1 slot system is demented. +; + + .module bank_msx1 + + .include "kernel.def" + .include "../kernel.def" + + ; imported symbols + .globl _ramsize + .globl _procmem + .globl ___hard_di + +; +; Memory banking for a 'simple' MSX 1 system. We have Fuzix in a +; cartridge from 0000-BFFF and RAM at C000-FFFF. For user space we +; map the RAM from 0000-BFFF as well. +; +; Isolate the banking logic so that we can try and share code with +; other non MSX2 style mappers. (For an MSX2 mapper it would I think +; be better to teach the MSX2 code about different VDP options). +; +; This is our little helper that needs to live low down. It expects +; interrupts to be *off* +; +; Entry +; B = computed slot mask +; A = subslot mask to set +; +; Return +; A = result of subslot set +; + + .area _LOW + +set_sub_slot: + push bc + push de + ld c,#0xA8 + in e,(c) + out (c),b ; map the thing we need to set subslots for + ; into the top 16K + ld (0xFFFF),a ; subslot info + ld a,(0xFFFF) ; report back the effect + cpl ; remembering it's complemented + out (c),e + pop de + pop bc + ret + +get_sub_slot: + push bc + push de + ld c,#0xA8 + in e,(c) + out (c),b ; map the thing we need to set subslots for + ; into the top 16K + ld a,(0xFFFF) ; report back the effect + cpl ; remembering it's complemented + out (c),e + pop de + pop bc + ret + +; +; Our helpers put the top bank back before returning so the rest +; can be generalized in common space +; + .area _COMMONMEM + + .globl _switch_map + .globl _current_map + .globl map_kernel + .globl map_process_always + .globl map_process + .globl map_save + .globl map_restore + .globl _set_initial_map + .globl _map_slot1_kernel + .globl _map_slot1_user + .globl find_ram + .globl _ramtab + .globl _kernel_map + .globl _user_map + .globl _current_map + + .globl _subslots + +; +; Switch to the map pointed to by HL. +; +; For our use case it's assumed the builder of the tables stuffs the +; existing values in anything unchanged and for no subslot we always +; use 0xFF. +; +; HL points to the map +; 0: bitmask of slots that are expanded and within the selection +; (within the selection is an optimization only) +; 1-4: subslot masks for slots 0-3 +; 5: slot selection +; +; DE points to the current map table +; +; B is used to cycle the bits for A8 (00xxxxxx 01xxxxxx 10xxxxxx +; 11xxxxx) +; +; On return the map change is done and the +; +; We try to optimize on the basis that most machines have less dumb +; mappings than the worst case but this is still heavy by the +; standards of the usual and rather less brain dead designs. +; +_switch_map: + push af + push bc + push de + ld a,#'[' + out (0x2F),a + in a,(0xA8) + and #0x3F ; Keep the lower selections + ld b,a ; the same + ld c,(hl) + inc hl + ld de,#_current_map+1 + +subslot_next: + bit 0,c + jr z, subslot_done ; no subslots + ld a,(de) + cp (hl) + jr z, subslot_done ; same subslots + ld a,b + call phex + ld a,(hl) + call phex + ; Do set + call set_sub_slot + ld (de),a ; update map +subslot_done: + inc hl + inc de + rr c + ; + ; B is the mask of the slot code. Move on a slot in + ; the top 16K + ; + ld a,#0x40 + add b + ld b,a + jr nc, subslot_next + ; And flip the slot register + ld a,(hl) + ld (de),a + out (0xA8),a + ld a,#']' + out (0x2F),a + pop de + pop bc + pop af + ret + +_current_map: + .ds 6 +_save_map: + .ds 6 +_kernel_map: + .ds 6 +_user_map: + .ds 6 +map_id: + .db 0 ; so we can fast figure out if returning to kernel/user +_scratch_map: + .ds 6 +_subslots: + .db 0 + + .area _CODE + +; +; Set up the initial mapping table. This is slightly pessimal as we +; don't eliminate any unneeded loads in the resulting map. However we +; want that map to be a reference everything uses so it needs to be +; complete. The C code can optimize it later as it wishes. +; +; Needs to be called before we blow away all the BIOS stuff. +; +_set_initial_map: + ld hl,#0xFCC4 + ld b,#4 + xor a +subslot_mask: + add a + bit 7,(hl) + dec hl + jr z,not_exp + inc a ; A was even before so this is safe +not_exp: + djnz subslot_mask + + ld (_subslots),a + + ld hl, #0xFCC5 + ld de, #_kernel_map + ld (de),a + inc de +; +; Now deal with the subslots. We can't quite assume the ROM is +; correct because we updated some of it directly. Start by +; copying the map. We will fix this up in a bit +; + ldi + ldi + ldi + ldi + in a,(0xA8) + ld (de),a + and #0x03 ; What bank is the ROM living in ? + ld c,a + ld b,#0 + ld hl,#_kernel_map+1 + add hl,bc ; subslot pointer for the kernel cartridge + ; or could be invalid if not subslotted but + ; we don't need to care + ld a,(hl) + and #0x0C ; propogate bits 2-3 across the low 6 bits + rrca + rrca + ld c,a + rlca + rlca + or c + rlca + rlca + or c + ld c,a + ld a,(hl) + and #0xc0 + or c + ld (hl),a + + ; Set up memory information + ld hl, #64 + ld (_ramsize),hl + ld l, #48 + ld (_procmem),hl + ; Set the live table to agree with the hardware state + ld hl, #_kernel_map + ld de, #_current_map + ldi + ldi + ldi + ldi + ldi + ldi + xor a + ld (map_id),a + ret + + .area _COMMONMEM + +; Always called under interrupt disable +map_save: + push bc + push de + push hl + ld de, #_save_map + ld hl, #_current_map + ldi + ldi + ldi + ldi + ldi + ldi + pop bc + pop de + pop hl + ret + +; Always called under interrupt disable and we never try and restore +; a user map. Just one of the kernel variants. +map_restore: + push hl + ld hl,#_save_map + call _switch_map + pop hl + ret + +; This is messy because of the NMOS Z80 IRQ bug. It might be worth +; revisiting all the logic that uses this and just keeping a software flag +; instead. +; +; The expensive bank switching also means we need to write some custom +; usermem copiers. +; +; FIXME: use map_id to eliminate bogus k/k switches (need to fix up the +; mmio and other cases before we can do this). +; +map_kernel: + push af + push hl + call ___hard_di + push hl + xor a + ld (map_id),a + ; We are possibly coming from a user bank. We need to jam the + ; kernel low mapping back. FIXME: if the kernel cartridge and + ; RAM are in the same subslot this will break. Need to put the flip + ; code in the low 256 bytes of both. + ld a,(_kernel_map+5) + out (0xA8),a + ld hl,#_kernel_map +switch_pop_out: + call _switch_map + pop af + jr c, was_di +;FIXME ei +was_di: + pop hl + pop af + ret + +map_process: + ld a,h + or l + jr z, map_kernel + ; Fall through +map_process_always: + push af + push hl + call ___hard_di + push hl + ld a,#1 + ld (map_id),a + ld hl,#_user_map + jr switch_pop_out + +; +; Take a kernel or user mapping and prepare a temporary mapping +; table that maps a device into 0x4000-0x7FFF. Device drivers can then +; use this to switch mapping +; +; Called with L = slot info +; +_map_slot1_kernel: + push hl + ld hl,#_kernel_map + jr map_slot_1 +_map_slot1_user: + push hl + ld hl,#_user_map +map_slot_1: + ld de,#_scratch_map + ldi + ldi + ldi + ldi + ldi + ldi + pop hl + ld a,l + ; First step: Update the slot register in the map + and #0x03 ; slot + rlca + rlca + ld e,a + ld a, (_scratch_map + 4) ; slot register + and #0xF3 + or e + ld (_scratch_map + 4),a ; slot register with us in bank 1 + ld e,a ; Save it in E + ld a,l + bit 7,a + jr z, no_subslot_map + ; Second step: Update the subslot map for the slot in question + ; for addresses 0x4000-0x7FFF + and #0x0C ; sub slot + ld c,a ; save it + ld hl,#_scratch_map+1 + ; already holds the slot we want to use + ld d,#0 + add hl,de + ld a,(hl) + and #0xF3 + or c ; new subslot value + ld (hl),a ; update map +no_subslot_map: + ; Return a pointer to the temporary map table + ld hl,#_scratch_map + ret + + + +; +; Find the RAM. Another fine MSX mess. Cycle through each slot +; mapping it between 0x0000 and 0xBFFF then look for a ROM header +; and if not try poking it. +; +; We end up with a table of slot/subslot for each of the low 3 16K +; banks. We don't worry about duplicates - MSX can't really do +; anything with them. We could so with a different build I guess but +; while in theory MSX machines can have 64K RAM shoved into multiple +; subslots it's not normal and the 'big' memory cartridges all add an +; MSX2 type mapper so want dealing with via an improved platform-msx2. +; +find_ram: + ld ix,#0xFCC1 + ld b,#4 + ld e,#0 +next_slot: + push bc + + ; Put the slot count into the low 6 bits + ld a,e ; slot count + rlca + rlca + or e + rlca + rlca + or e ; lower bits + ld d,a + in a,(0xA8) + push af ; save the old value + and #0xC0 + or d ; A is now common with the low 48K switched + + bit 7,(ix) + jr nz, sub_scan + + out (0xA8),a + ; + ; No subslots - so just scan directly as we are now mapped + ; into the low 48K + ; + ld d,#0xff ; no subslot + call testram + jr not_sub + + ; + ; Register usage here is a bit complex + ; + ; B = counter + ; C = bits to make the bank in question appear in the top 16K + ; with kernel below + ; D = subslot number + ; E = slot numner + ; H = subslot bits for this subslot with fixed top 16K + ; L = bits to make the bank in question appear in the low 48K + ; with common in the top 16K + ; + ; + ; + ; +sub_scan: + ld l,a ; Remember our A8 bits + push de ; Save slot number (E) + + ; Work out how to get our new slot in the top and the OS below + ld a,e ; slot + rrca ; into the top two bits + rrca + ld c,a + in a,(0xA8) + and #0x3F ; with the old mapping for the low 48K + or c + ld c,a ; Mask for selecting our bank for *_sub_slot + + ; + ; Get the subslot for this slot + ; + ld b,c + call get_sub_slot + push af ; Stack it so we can put it back at the end + ; + ld h,a + and #0xc0 ; Keep the subslot bits for the top 16K + ; as it could be us (in theory anyway - unlikely) + ; low bits are 0 for subslot we want + ld h,a ; H will count through the masks we need + ld b,#4 ; Number of banks + ld d,#0 ; Count of the banks +next_sub: + push bc + ld b,c ; Mask for the bank + ld a,h ; Subslot mask + call set_sub_slot + in a,(0xA8) ; Save the map + push af + ld a,l ; Saved A8 mask for mapping this lot low + out (0xA8),a ; We now have common mapped and low 48K is our subslot + push hl + call testram + pop hl + pop af + out (0xA8),a ; Put the map back + pop bc + ld a,#0x15 ; cycle 00 15 2A 3F (plus fixed top bits) + add h + ld h,a + inc d + djnz next_sub + + ; + ; Restore the saved subslot + ; + pop af + ld b,c + call set_sub_slot + ; + ; Recover our slot count + ; + pop de + +not_sub: + ; + ; Recover the old slot map state + ; + pop af + out (0xA8),a + ; + ; Move on a slot + ; + inc e + inc ix + pop bc + djnz next_slot + + ret + +; +; See if our 16K bank is writable. If it is then we store it in the +; table, if not we just bump the pointers +; +; For 0x4000 and 0x8000 start with a ROM check. +; +writecheck_r: + ld a,(hl) + ; Check for AB or CD + cp #'A' + jr z, romcheck + cp #'C' + jr nz, writecheck +romcheck: + inc a + inc hl + cp (hl) + jr nz, writecheckd +; +; ROM header: So this 16K chunk isn't RAM +; + inc hl + ; HL now points at initptr + inc hl + inc hl + inc hl + inc hl + ; HL now points at device ptr + ld a,(hl) + inc hl + or (hl) + jr nz, notram +; +; A device ROM. Potentially interesting. Need to add code to checksum +; or otherwise identify them later +; + ;FIXME + jr notram + +writecheckd: + dec hl +; +; For 0x0000 we don't do a ROM check just a r/w check +; +writecheck: + ld a,#0x55 + ld (hl),a + cp (hl) + jr nz, notram + cpl + ld (hl),a + cp (hl) + jr nz, notram + ; Found one + ; E = slot D = subslot (or FF) + ; BC = table + ld a,e + ld (bc),a + inc bc + ld a,d + ld (bc),a + inc bc + ret +notram: + inc bc + inc bc + ret +; +; Scan a slot/subslot for RAM +; +testram: + ld bc,#_ramtab + ld hl,#0x1000 + call writecheck + ld h,#0x40 + call writecheck_r + ld h,#0x80 + call writecheck_r + ret + +_ramtab: + .ds 6 + +dophex: ld c,a + and #0xf0 + rrca + rrca + rrca + rrca + call pdigit + ld a,c + and #0x0f +pdigit: cp #10 + jr c,isl + add #7 +isl: add #'0' + out (0x2F),a + ret + +phex: + push af + push bc + call dophex + ld a,#' ' + out (0x2f),a + pop bc + pop af + ret diff --git a/Kernel/platform-msx1/sunrise.s b/Kernel/platform-msx1/sunrise.s new file mode 100644 index 00000000..4d60b489 --- /dev/null +++ b/Kernel/platform-msx1/sunrise.s @@ -0,0 +1,248 @@ +; +; Sunrise style IDE +; + + .globl _do_ide_init_drive + .globl _do_ide_begin_reset + .globl _do_ide_end_reset + .globl _do_ide_flush_cache + .globl _do_ide_xfer + + .globl _ide_error + .globl _ide_base + + .globl map_kernel + .globl _switch_map + + .globl _blk_op + .globl _ticks + .globl _devide_buf + + .globl _sunrise_u + .globl _sunrise_k + +IDE_REG_ERROR .equ 1 +IDE_REG_FEATURES .equ 1 +IDE_REG_SEC_COUNT .equ 2 +IDE_REG_LBA_0 .equ 3 +IDE_REG_LBA_1 .equ 4 +IDE_REG_LBA_2 .equ 5 +IDE_REG_LBA_3 .equ 6 +IDE_REG_DEVHEAD .equ 6 +IDE_REG_COMMAND .equ 7 +IDE_REG_STATUS .equ 7 + +; For Sunrise at least +IDE_REG_CONTROL .equ 14 + + +IDE_ERROR .equ 0 +IDE_BUSY .equ 7 + +IDE_STATUS_READY .equ 0x40 +IDE_STATUS_DATAREQUEST .equ 0x08 + +IDE_CMD_READ_SECTOR .equ 0x20 +IDE_CMD_WRITE_SECTOR .equ 0x30 +IDE_CMD_FLUSH_CACHE .equ 0xE7 +IDE_CMD_IDENTIFY .equ 0xEC + +BLK_OP_ADDR .equ 0 +BLK_OP_ISUSER .equ 2 +BLK_OP_LBA .equ 6 +BLK_OP_ISREAD .equ 12 + +devide_wait_ready: + ld a,#IDE_STATUS_READY +devide_wait: + push de + push hl + ld c,a + ld de,(_ticks) +wait_nbusy: + ld hl,(_ticks) + or a + sbc hl,de + bit 2,h ; We spin fast enough this is a safe test + jr nz, wait_timeout + ld a,IDE_REG_STATUS(ix) + bit IDE_BUSY,a + jr nz, wait_nbusy + ; Check for an error + bit IDE_ERROR,a + jr nz, error + ; Now see if it's a good status + and c + cp c + jr nz, wait_nbusy + ; We have !busy, !error and the value we wanted - done + pop hl + pop de + ret ; Z = good +wait_timeout: + ld a,#255 ; NZ is set already +error: + ld (_ide_error),a ; save the value + ; NZ set already + pop hl + pop de + ret + +; +; Flip I/O maps +; +map_sunrise_k: + push hl + ld hl,#_sunrise_k + call _switch_map + pop hl + ret +map_sunrise_u: + push hl + ld hl,#_sunrise_u + call _switch_map + pop hl + ret +; +; Caller passes L = disk, (devide_buf) = a tmpbuf in common space +; +_do_ide_init_drive: + push ix + ld ix,(_ide_base) + call map_sunrise_k + ld IDE_REG_DEVHEAD(ix),l + ld hl,#0 + call devide_wait_ready + jr nz, timeout + ld IDE_REG_COMMAND(ix),#IDE_CMD_IDENTIFY + ld a,#IDE_STATUS_DATAREQUEST + call devide_wait + jr nz, timeout + call devide_rx_buf + ; returns the tmpbuf to the caller to free +timeout: + call map_kernel + pop ix + ret + +_do_ide_begin_reset: + push ix + ld ix,(_ide_base) + call map_sunrise_k + ld IDE_REG_DEVHEAD(ix),#0xE0 + ld IDE_REG_CONTROL(ix),#0x06 ; start reset (bit 2) + call map_kernel + pop ix + ret + +_do_ide_end_reset: + push ix + ld ix,(_ide_base) + call map_sunrise_k + ld IDE_REG_CONTROL(ix),#0x02 + call map_kernel + pop ix + ret + +_do_ide_flush_cache: + push ix + ld ix,(_ide_base) + call map_sunrise_k + ld IDE_REG_LBA_3(ix),l + ld hl,#0 + call devide_wait_ready + jr nz, flush_bad + ld IDE_REG_COMMAND(ix),#IDE_CMD_FLUSH_CACHE + call devide_wait_ready + jr z,flush_good +flush_bad: + dec hl ; to -1 +flush_good: + call map_kernel + pop ix + ret + +_do_ide_xfer: + push ix + ld ix,(_ide_base) + ld a,(_blk_op + BLK_OP_ISUSER) + or a + jr nz, xfer_user + call map_sunrise_k + jr init_io +xfer_user: + call map_sunrise_u +init_io: + ld e,l + ld hl, (_blk_op + BLK_OP_LBA + 2) + ld IDE_REG_LBA_0(ix),l + ld IDE_REG_LBA_1(ix),h + ld hl, (_blk_op + BLK_OP_LBA + 3) + ld IDE_REG_LBA_2(ix),l + ld a,h + and #0x0F ; Merge drive and bits 24-27 + or e + ld IDE_REG_LBA_3(ix),a + ld hl,#0 + call devide_wait_ready + jr z, xfer_timeout + ld IDE_REG_SEC_COUNT(ix),#1 + ld a,(_blk_op + BLK_OP_ISREAD) + or a + jr z, send_cmd + ld b,#IDE_CMD_READ_SECTOR + ld IDE_REG_COMMAND(ix),#IDE_CMD_READ_SECTOR + call devide_xfer_r + ld hl,#1 + call map_kernel + pop ix + ret +send_cmd: + ld IDE_REG_COMMAND(ix),#IDE_CMD_WRITE_SECTOR + ld a,#IDE_STATUS_DATAREQUEST + call devide_wait + jr z, xfer_timeout + call devide_xfer_w + call devide_wait_ready + jr z, xfer_timeout + ld hl,#1 + call map_kernel + pop ix + ret +xfer_timeout: + ld hl,#0 + call map_kernel + pop ix + ret + +; +; For now deal with Sunrise style. If we find any others we can +; deal with it here +; +devide_xfer_r: + ld de,(_blk_op + BLK_OP_ADDR) + ; We don't have to worry about swap being special for this port + ; Our caller also is responsible for working out when to bounce +xfer_do: + ld hl,#0x7C00 + ld bc,#0x0200 + ldir + ret + +devide_xfer_w: + ld hl,(_blk_op + BLK_OP_ADDR) + ; We don't have to worry about swap being special for this port + ; Our caller also is responsible for working out when to bounce + ld de,#0x7C00 + ld bc,#0x0200 + ldir + ret + +devide_rx_buf: + ld hl,(_devide_buf) + push hl + ex de,hl + call xfer_do + pop hl + ret + diff --git a/Kernel/platform-msx1/tricks.s b/Kernel/platform-msx1/tricks.s index 52a33670..0113d75b 100644 --- a/Kernel/platform-msx1/tricks.s +++ b/Kernel/platform-msx1/tricks.s @@ -1,229 +1,5 @@ -; 2013-12-21 William R Sowerbutts - - .module tricks - - .globl _ptab_alloc - .globl _newproc - .globl _chksigs - .globl _getproc - .globl _platform_monitor - .globl trap_illegal - .globl _platform_switchout - .globl _switchin - .globl _doexec - .globl _dofork - .globl _runticks - .globl unix_syscall_entry - .globl interrupt_handler - .globl map_kernel - .globl map_process_always - .globl map_process - .globl _ramtop - - ; imported debug symbols - .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex - .include "kernel.def" .include "../kernel.def" - .area _COMMONMEM - -; ramtop must be in common for single process swapping cases -; and its a constant for the others from before init forks so it'll be fine -; here -_ramtop: - .dw 0 - -; Switchout switches out the current process, finds another that is READY, -; possibly the same process, and switches it in. When a process is -; restarted after calling switchout, it thinks it has just returned -; from switchout(). -_platform_switchout: - di - ; save machine state - - ld hl, #0 ; return code set here is ignored, but _switchin can - ; return from either _switchout OR _dofork, so they must both write - ; U_DATA__U_SP with the following on the stack: - push hl ; return code - push ix - push iy - ld (U_DATA__U_SP), sp ; this is where the SP is restored in _switchin - - ; Stash the uarea back into process memory - ; We need to flip to the process map to copy this high - call map_process_always - ld hl, #U_DATA - ld de, #U_DATA_STASH - ld bc, #U_DATA__TOTALSIZE - ldir - ; Then flip back as we need to call getproc - call map_kernel - - ; find another process to run (may select this one again) - call _getproc - - push hl - call _switchin - - ; we should never get here - call _platform_monitor - -badswitchmsg: .ascii "_switchin: FAIL" - .db 13, 10, 0 - -_switchin: - di - pop bc ; return address - pop de ; new process pointer -; -; FIXME: do we actually *need* to restore the stack ! -; - push de ; restore stack - push bc ; restore stack - - ld hl, #P_TAB__P_PAGE_OFFSET ; Common - add hl, de ; process ptr - - call map_process - - ; ------- No stack from here as we will LDIR over it - - exx ; thank goodness for exx 8) - ld hl, #U_DATA_STASH - ld de, #U_DATA - ld bc, #U_DATA__TOTALSIZE - ldir - exx - - ; ------- No stack still ------- - - ; Restore the stack into the newly copied stack udata. We do this - ; earlier than most ports as the convoluted banking means we need - ; a stack to flip back to kernel mappings - ; - ; FIXME: should look at making this the usual way around ? - ; - ld sp, (U_DATA__U_SP) - ; ------- stack good ------- - - ; Map kernel memory back so we can actually get our hands on the - ; process table data - call map_kernel - - ; check u_data->u_ptab matches what we wanted - ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab - or a ; clear carry flag - sbc hl, de ; subtract, result will be zero if DE==IX - jr nz, switchinfail - - ; wants optimising up a bit - ld hl, #P_TAB__P_STATUS_OFFSET - add hl, de - ld (hl), #P_RUNNING - - ; runticks = 0 - ld hl, #0 - ld (_runticks), hl - - pop iy - pop ix - pop hl ; return code - - ; enable interrupts, if the ISR isn't already running - ld a, (U_DATA__U_ININTERRUPT) - or a - ret nz ; in ISR, leave interrupts off - ei - ret ; return with interrupts on - -switchinfail: - call outhl - ld hl, #badswitchmsg - call outstring - ; something went wrong and we didn't switch in what we asked for - jp _platform_monitor - -fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry - -; -; Called from _fork. We are in a syscall, the uarea is live as the -; parent uarea. The kernel is the mapped object. -; -_dofork: - ; always disconnect the vehicle battery before performing maintenance - di ; should already be the case ... belt and braces. - - pop de ; return address - pop hl ; new process p_tab* - push hl - push de - - ld (fork_proc_ptr), hl - - ; prepare return value in parent process -- HL = p->p_pid; - ld de, #P_TAB__P_PID_OFFSET - add hl, de - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - - ; Save the stack pointer and critical registers. - ; When this process (the parent) is switched back in, it will be as if - ; it returns with the value of the child's pid. - push hl ; HL still has p->p_pid from above, the return value in the parent - push ix - push iy - - ; save kernel stack pointer -- when it comes back in the parent we'll be in - ; _switchin which will immediately return (appearing to be _dofork() - ; returning) and with HL (ie return code) containing the child PID. - ; Hurray. - ld (U_DATA__U_SP), sp - - ; now we're in a safe state for _switchin to return in the parent - ; process. - - ; --------- we switch stack copies in this call ----------- - call fork_copy ; copy 0x0000 to udata.u_top and the - ; uarea and return on the childs - ; common - ; We are now in the kernel child context - - ; now the copy operation is complete we can get rid of the stuff - ; _switchin will be expecting from our copy of the stack. - pop bc - pop bc - pop bc - - ; The child makes its own new process table entry, etc. - ld hl, (fork_proc_ptr) - push hl - call _newproc - pop bc - - ; any calls to map process will now map the childs memory - - ; runticks = 0; - ld hl, #0 - ld (_runticks), hl - ; in the child process, fork() returns zero. - ; - ; And we exit, with the kernel mapped, the child now being deemed - ; to be the live uarea. The parent is frozen in time and space as - ; if it had done a switchout(). - ret - -; -; Interrupts need to be off here (if not we need to fix map_save -; and map_restore -; -fork_copy: -; FIXME: copy the 32K bank from one chunk of megaram to the other -; -; See bankfork on z80pack for guidance on udata stash etc -; - ret ; this stack is copied so safe to return on + .include "../lib/z80single.s" - diff --git a/Kernel/platform-msx1/vdp.s b/Kernel/platform-msx1/vdp.s index 409eca0b..89e9eca1 100644 --- a/Kernel/platform-msx1/vdp.s +++ b/Kernel/platform-msx1/vdp.s @@ -15,6 +15,9 @@ .globl _scroll_down .globl _plot_char .globl _cursor_disable + .globl _vdp_load_font + + .globl _fontdata_6x8 ; ; VDP routines are directly hooked into the vt layer @@ -22,14 +25,31 @@ VDP_DIRECT .equ 1 .include "../dev/vdp1.s" -; -; FIXME: should use vdpport, but right now vdpport is in data not -; common space. -; + .area _COMMONMEM platform_interrupt_all: - ld c, #0x99 - in a, (c) _cursor_disable: ret + +_vdp_load_font: + ld hl,#0x4900 + ld bc,(_vdpport) + out (c),l + out (c),h + ld hl,#_fontdata_6x8 + ld de,#768 + dec c +fontloop: + ld a,(hl) + rlca + rlca + out (c),a + inc hl + dec de + ld a,d + or e + jr nz,fontloop + ret + +_vdpport: .word 0x2899 ; port 0x99, 40 byte count in fastest load