+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
-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)
--- /dev/null
+;
+; 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
/* 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 */
; 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
.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 ?
#include <version.h>
#include <kdata.h>
#include <devices.h>
-#include <devhd.h>
+#include <devide_sunrise.h>
#include <devfd.h>
#include <devlpr.h>
#include <devsys.h>
+#include <vt.h>
#include <tty.h>
+#include <keycode.h>
#include <devtty.h>
+#include <msx.h>
+#include <kbdmatrix.h>
+#include <blkdev.h>
+#include <devide_sunrise.h>
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) */
*/
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();
}
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <timer.h>
+#include <blkdev.h>
+#include <devide_sunrise.h>
+#include <msx.h>
+
+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);
+}
--- /dev/null
+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
#include <devtty.h>
#include <vt.h>
#include <tty.h>
+#include <devtty.h>
#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];
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)
{
}
-#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)
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))
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;
-
#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
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <blkdev.h>
+
+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--);
+}
-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
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
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
--- /dev/null
+#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
#include <devtty.h>
/* 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)
{
__endasm;
}
+/* Some of this is discard stuff */
+
uint8_t platform_param(char *p)
{
used(p);
{
}
-/*
- * 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();
+ }
}
+
--- /dev/null
+#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
;
-; MSX2 hardware support
+; MSX1 hardware support
;
.module msx1
.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
.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
; stuff to save
.globl _vdpport
+ .globl _vdptype
.globl _infobits
+ .globl _machine_type
;
; vdp - we must initialize this bit early for the vt
; -----------------------------------------------------------------------------
.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
.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
.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
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:
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
--- /dev/null
+#
+# 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
+
--- /dev/null
+;
+; 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
--- /dev/null
+;
+; 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
+
-; 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"
-
.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
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