From abc63619a34a432b4f62a0daa5b812beb9dcd565 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 5 Nov 2016 12:23:40 +0000 Subject: [PATCH] coco2cart: COCO2 with cartridge While fiddling with COCO2 might as well clone it and add the cartridge version I made the notes for ages ago. Untested and will need some work. --- Kernel/platform-coco2cart/Makefile | 69 +++ Kernel/platform-coco2cart/README | 66 +++ Kernel/platform-coco2cart/bootstrap.s | 107 +++++ Kernel/platform-coco2cart/carts.h | 10 + Kernel/platform-coco2cart/coco2.s | 232 ++++++++++ Kernel/platform-coco2cart/commonmem.s | 25 ++ Kernel/platform-coco2cart/config.h | 61 +++ Kernel/platform-coco2cart/crt0.s | 53 +++ Kernel/platform-coco2cart/devfd.h | 17 + Kernel/platform-coco2cart/device.h | 11 + Kernel/platform-coco2cart/devices.c | 49 +++ Kernel/platform-coco2cart/devlpr.c | 70 +++ Kernel/platform-coco2cart/devlpr.h | 11 + Kernel/platform-coco2cart/devtty.c | 240 +++++++++++ Kernel/platform-coco2cart/devtty.h | 12 + Kernel/platform-coco2cart/discard.c | 73 ++++ Kernel/platform-coco2cart/drivewire.s | 237 +++++++++++ Kernel/platform-coco2cart/dwread.s | 341 +++++++++++++++ Kernel/platform-coco2cart/dwwrite.s | 196 +++++++++ Kernel/platform-coco2cart/ide.s | 62 +++ Kernel/platform-coco2cart/libc.c | 25 ++ Kernel/platform-coco2cart/main.c | 51 +++ Kernel/platform-coco2cart/mini_ide.c | 100 +++++ Kernel/platform-coco2cart/mini_ide.h | 6 + Kernel/platform-coco2cart/mini_ide_discard.c | 97 +++++ Kernel/platform-coco2cart/target.mk | 1 + Kernel/platform-coco2cart/tricks.s | 183 ++++++++ Kernel/platform-coco2cart/usermem.s | 114 +++++ Kernel/platform-coco2cart/video.s | 424 +++++++++++++++++++ 29 files changed, 2943 insertions(+) create mode 100644 Kernel/platform-coco2cart/Makefile create mode 100644 Kernel/platform-coco2cart/README create mode 100644 Kernel/platform-coco2cart/bootstrap.s create mode 100644 Kernel/platform-coco2cart/carts.h create mode 100644 Kernel/platform-coco2cart/coco2.s create mode 100644 Kernel/platform-coco2cart/commonmem.s create mode 100644 Kernel/platform-coco2cart/config.h create mode 100644 Kernel/platform-coco2cart/crt0.s create mode 100644 Kernel/platform-coco2cart/devfd.h create mode 100644 Kernel/platform-coco2cart/device.h create mode 100644 Kernel/platform-coco2cart/devices.c create mode 100644 Kernel/platform-coco2cart/devlpr.c create mode 100644 Kernel/platform-coco2cart/devlpr.h create mode 100644 Kernel/platform-coco2cart/devtty.c create mode 100644 Kernel/platform-coco2cart/devtty.h create mode 100644 Kernel/platform-coco2cart/discard.c create mode 100644 Kernel/platform-coco2cart/drivewire.s create mode 100644 Kernel/platform-coco2cart/dwread.s create mode 100644 Kernel/platform-coco2cart/dwwrite.s create mode 100644 Kernel/platform-coco2cart/ide.s create mode 100644 Kernel/platform-coco2cart/libc.c create mode 100644 Kernel/platform-coco2cart/main.c create mode 100644 Kernel/platform-coco2cart/mini_ide.c create mode 100644 Kernel/platform-coco2cart/mini_ide.h create mode 100644 Kernel/platform-coco2cart/mini_ide_discard.c create mode 100644 Kernel/platform-coco2cart/target.mk create mode 100644 Kernel/platform-coco2cart/tricks.s create mode 100644 Kernel/platform-coco2cart/usermem.s create mode 100644 Kernel/platform-coco2cart/video.s diff --git a/Kernel/platform-coco2cart/Makefile b/Kernel/platform-coco2cart/Makefile new file mode 100644 index 00000000..361874be --- /dev/null +++ b/Kernel/platform-coco2cart/Makefile @@ -0,0 +1,69 @@ + +CSRCS = devices.c main.c + +# Code that must be present for the bootstrap +CISRCS = mini_ide.c + +C3SRCS = libc.c devtty.c + +CDSRCS = discard.c mini_ide_discard.c + +DSRCS = ../dev/devdw.c + +ASRCS = crt0.s coco2.s ide.s usermem.s bootstrap.s +ASRCS += tricks.s commonmem.s drivewire.s video.s + +COBJS = $(CSRCS:.c=$(BINEXT)) +CIOBJS = $(CISRCS:.c=$(BINEXT)) +C3OBJS = $(C3SRCS:.c=$(BINEXT)) +CDOBJS = $(CDSRCS:.c=$(BINEXT)) +AOBJS = $(ASRCS:.s=$(BINEXT)) +DOBJS = $(patsubst ../dev/%.c,%.o, $(DSRCS)) +OBJS = $(COBJS) $(C3OBJS) $(CDOBJS) $(AOBJS) $(DOBJS) $(CIOBJS) + +CROSS_CCOPTS += -I../dev/ + +all: $(OBJS) + +export CROSS_CC_SEGD=-mcode-section=.text3 + +$(COBJS): %$(BINEXT): %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG2) -c $< + +$(CIOBJS): %$(BINEXT): %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG1) -c $< + +$(C3OBJS): %$(BINEXT): %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGD) -c $< + +$(CDOBJS): %$(BINEXT): %.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(DOBJS): %$(BINEXT): ../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGD) -c $< + +$(DDOBJS): %$(BINEXT): ../dev/%.c + $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $< + +$(AOBJS): %$(BINEXT): %.s + $(CROSS_AS) $(ASOPTS) $< -o $*.o + +clean: + rm -f *.o *~ + +image: bootfuz.bin + $(CROSS_LD) -o ../fuzix.bin --map=../fuzix.map --script=fuzix.link \ + crt0.o bootstrap.o commonmem.o coco2.o discard.o ../simple.o \ + ../start.o ../version.o ../lowlevel-6809.o \ + tricks.o main.o ../timer.o ../kdata.o devices.o \ + drivewire.o devdw.o mini_ide.o mini_ide_discard.o \ + ../devio.o ../filesys.o ../process.o ../inode.o ../syscall_fs.o \ + ../syscall_proc.o ../syscall_other.o ../mm.o ../swap.o \ + ../tty.o ../devsys.o ../usermem.o usermem.o ../syscall_fs2.o \ + ../syscall_exec16.o devtty.o libc.o ../vt.o video.o ../syscall_fs3.o \ + ../font8x8.o + ../tools/decb-image <../fuzix.bin fuzix.img + +bootfuz.bin: bootloader.s + lwasm -r -b -obootfuz.bin bootloader.s + diff --git a/Kernel/platform-coco2cart/README b/Kernel/platform-coco2cart/README new file mode 100644 index 00000000..eb80b6f4 --- /dev/null +++ b/Kernel/platform-coco2cart/README @@ -0,0 +1,66 @@ +COCO2 +===== + +COCO or Dragon 64 with an IDE port, cartridge and 64K of RAM + +Supported hardware: + IDE (Glenside or compatible) + +Memory Map +========== +0000-00FF Interrupt stack +0100-01FF Vectors (then wasted - could hide libc.o in there ?) +0200-19FF Display +1A00-1BFF udata/stack +1C00-7FFF Code/Data for common plus some kernel +8000-FEFF User space +FF00-FFFF I/O etc + +Cartridge +C000-FEFF Kernel + + +Emulating in XRoar +================== + +Needs the IDE patches + +TODO +==== + +Loader code +Set the display up correctly +Support for a real time clock and clock locking +Review irq disabling. We shouldn't need much of this at all as we don't have +floppy hell to contend with, but the switchin/switchout logic needs to be +reviewed for interrupt safety and the IRQs re-enabled during swap in and +out. We should also be totally clean now on FIR blocking, so audio might +actually be doable! + +MPI switching + +Could we do a cartridge version +- Re-order so common is in low memory +- Support map_kernel/map_process flipping +- Add asm helpers for user mode copying (trivial - and since we never copy + into kernel ROM we can optimise them a lot - one map switch only) +- Fix IDE to support the map flip on transfers (needs little asm code + adding) +- How to do discard, can we just take the hit or do we need to copy the + entire cartridge into high RAM, run in high RAM with discard at say + 0xA000 and then in platform_discard switch on map_kernel cartridge + selection so we can let userspace blow away the upper 32K of the RAM + mapping. + +16K Cartridge version would give us space for +- Proper video (costs us about 8K) +- Nice loader (just 'EXEC $C000' and go) < 1K +- Probably all of sound/joysticks/printer/floppy and a few more buffers +- Full size user memory bank (so can run full v7 shell) 4K + + +Write a loader into the cartridge start so it bootstraps off disk and runs +nicely + +6847T1 lower case ? + diff --git a/Kernel/platform-coco2cart/bootstrap.s b/Kernel/platform-coco2cart/bootstrap.s new file mode 100644 index 00000000..aaa76a87 --- /dev/null +++ b/Kernel/platform-coco2cart/bootstrap.s @@ -0,0 +1,107 @@ +; +; Bootstrap for cartridge start up. For now load from block 1 (second +; block). We can refine this later. We do 51 block loads from block 1 +; for 1A00 to 7FFF and then copy 1A00 to 0100 for vectors +; +; Needs some kind of global timeout -> error handling +; +; +; Load block b +; + .module bootstrap + + .globl load_image + + .area .text + +load_block: + lda #$FF + tfr a,dp +load_block_1: + lda #$80 + bita <$FF57 ; busy + bne load_block_1 + lda #$40 + sta <$FF56 +load_block_2: + lda #$80 + bita <$FF57 ; busy + bne load_block_2 + clr <$FF55 + clr <$FF54 + stb <$FF53 ; block number to fetch + lda #1 + sta <$FF52 + + + lda #$40 +wait_drdy: + bita <$FF57 + beq wait_drdy + lda #$20 ; read command + sta <$FF57 + clrb + lda #$08 +wait_drq: + bita <$FF57 ; DRQ + beq wait_drq +copyloop: + lda <$FF50 ; read word and latch + sta ,x+ + lda <$FF58 ; latched byte + sta ,x+ + decb + bne copyloop + lda #$80 +wait_bsy: + bita <$FF57 + bne wait_bsy + lda #$01 + bita <$FF57 + bne load_error + rts + + +load_image: + ldy #$0400 ; display at this point + lda #'G' + sta ,y+ + ldb #1 + lda #51 + ldx #$1A00 +load_loop: + pshs a,b + bsr load_block + lda #'*' + sta ,y+ + puls a,b + incb + deca + bne load_loop + ldx #$1A00 + ldu #$0100 +vec_copy: + ldd ,x++ + std ,u++ + cmpu #$0200 + bne vec_copy + + ldx #$1A00 + clra + clrb +ud_wipe: + std ,x++ + cmpx #$1C00 + bne ud_wipe +endstr: rts + +load_error: + ldu #fail +copy_str: + lda ,u+ + beq endstr + sta ,y+ + bra copy_str +fail: + .ascii "Fail" + .db 0 diff --git a/Kernel/platform-coco2cart/carts.h b/Kernel/platform-coco2cart/carts.h new file mode 100644 index 00000000..86d49415 --- /dev/null +++ b/Kernel/platform-coco2cart/carts.h @@ -0,0 +1,10 @@ +#define CART_DRAGONDOS 1 /* DragonDOS floppy */ +#define CART_DELTADOS 2 /* DeltaDOS floppy */ +#define CART_RSDOS 3 /* RSDOS Cartridge */ +#define CART_ORCH90 4 /* Orchestra Sound */ + +extern int cart_find(int id); +extern uint8_t carttype[4]; +extern uint8_t cartslots; +extern uint8_t bootslot; +extern uint8_t system_id; diff --git a/Kernel/platform-coco2cart/coco2.s b/Kernel/platform-coco2cart/coco2.s new file mode 100644 index 00000000..19b3632e --- /dev/null +++ b/Kernel/platform-coco2cart/coco2.s @@ -0,0 +1,232 @@ + ; + ; COCO2 platform + ; + + .module coco2 + + ; exported + .globl _mpi_present + .globl _mpi_set_slot + .globl _cart_hash + .globl map_kernel + .globl map_process + .globl map_process_a + .globl map_process_always + .globl map_save + .globl map_restore + .globl init_early + .globl init_hardware + .globl _program_vectors + .globl _need_resched + + .globl _ramsize + .globl _procmem + + ; imported + .globl unix_syscall_entry +;FIX .globl fd_nmi_handler + .globl null_handler + .globl _vtoutput + + ; exported debugging tools + .globl _trap_monitor + .globl _trap_reboot + .globl outchar + .globl ___hard_di + .globl ___hard_ei + .globl ___hard_irqrestore + + include "kernel.def" + include "../kernel09.def" + + + .area .vectors + ; + ; At 0x100 as required by the COCO ROM + ; + jmp badswi_handler ; 0x100 + jmp badswi_handler ; 0x103 + jmp unix_syscall_entry ; 0x106 + ; FIXME: 109 needs to handle bad NMI! + jmp badswi_handler ; 0x109 + jmp interrupt_handler ; 0x10C + jmp firq_handler ; 0x10F + + .area .text + +init_early: + ldx #null_handler + stx 1 + lda #0x7E + sta 0 + sta 0x0071 ; BASIC cold boot flag (will get trashed by + ; irq stack but hopefully still look bad) + ; + ; Move the display + ; + ldx #0xFFC7 + clr ,x+ ; Set F0 + clr ,x++ ; Clear F1-F6 + clr ,x++ + clr ,x++ + clr ,x++ + clr ,x++ + clr ,x++ + rts + +init_hardware: + ldd #64 + std _ramsize + ldd #32 + std _procmem + + ; Turn on PIA CB1 (50Hz interrupt) + lda 0xFF03 + ora #1 + sta 0xFF03 + jsr _vtinit + rts + + .area .common + +_trap_reboot: + orcc #0x10 + clr 0xFFBE + lda #0x7e + sta 0x0071 ; in case IRQ left it looking valid + jmp [0xFFFE] + +_trap_monitor: + orcc #0x10 + bra _trap_monitor + +___hard_di: + tfr cc,b ; return the old irq state + orcc #0x10 + rts +___hard_ei: + andcc #0xef + rts + +___hard_irqrestore: ; B holds the data + tfr b,cc + rts + +; +; COMMON MEMORY PROCEDURES FOLLOW +; + + .area .common +; +; Our vectors live in a fixed block and are not banked out. Likewise +; No memory management for now +; +_program_vectors: +map_kernel: + pshs a + clr $ffdf ; ROM in + lda #1 +map_kr: + sta romin + puls a,pc + +map_process: +map_process_always: +map_process_a: + clr $ffde ; ROM out + clr romin + rts + +map_restore: + pshs a + lda romsave + sta romin + bne map_kr + clr $ffde + puls a,pc + +map_save: + pshs a + lda romin + sta romsave + puls a,pc +; +; Helpers for the MPI and Cartridge Detect +; + +; +; oldslot = mpi_set_slot(uint8_t newslot) +; +_mpi_set_slot: + tfr b,a + ldb 0xff7f + sta 0xff7f + rts +; +; int8_t mpi_present(void) +; +_mpi_present: + lda 0xff7f ; Save bits + ldb #0xff ; Will get back 33 from an MPI cartridge + stb 0xff7f ; if the emulator is right on this + ldb 0xff7f + cmpb #0x33 + bne nompi + clr 0xff7f ; Switch to slot 0 + ldb 0xff7f + bne nompi + incb + sta 0xff7f ; Our becker port for debug will be on the default + ; slot so put it back for now + rts ; B = 0 +nompi: ldb #0 + sta 0xff7f ; Restore bits just in case + rts + + .area .text ; Must be clear of cartridge mapping areas +; +; uint16_t cart_hash(void) +; +_cart_hash: + pshs cc + orcc #0x10 + ldx #0xC000 + ldd #0 + clr $FFBE ; Map cartridge +hashl: + addd ,x++ + cmpx #0xC200 + bne hashl + tfr d,x + clr $FFBF ; Return to normality + puls cc,pc + + + .area .common +; +; FIXME: +; +firq_handler: +badswi_handler: + rti + +; +; debug via printer port +; +outchar: + sta 0xFF02 + lda 0xFF20 + ora #0x02 + sta 0xFF20 + anda #0xFD + sta 0xFF20 + rts + + .area .common + +_need_resched: + .db 0 +romin: + .db 0 +romsave: + .db 0 diff --git a/Kernel/platform-coco2cart/commonmem.s b/Kernel/platform-coco2cart/commonmem.s new file mode 100644 index 00000000..b820abbb --- /dev/null +++ b/Kernel/platform-coco2cart/commonmem.s @@ -0,0 +1,25 @@ +; + .module commonmem + + ; exported symbols + .globl _ub + .globl _udata + .globl kstack_top + .globl istack_top + .globl istack_switched_sp + + .area .udata + +_ub: ; first 512 bytes: starts with struct u_block, with the kernel stack working down from above +_udata: +kstack_base: + zmb 512 +kstack_top: + + .area .istack + + ; next 256 bytes: 254 byte interrupt stack, then 2 byte saved stack pointer +istack_base: + zmb 254 +istack_top: +istack_switched_sp: .dw 0 diff --git a/Kernel/platform-coco2cart/config.h b/Kernel/platform-coco2cart/config.h new file mode 100644 index 00000000..36d52b06 --- /dev/null +++ b/Kernel/platform-coco2cart/config.h @@ -0,0 +1,61 @@ +/* Enable to make ^Z dump the inode table for debug */ +#undef CONFIG_IDUMP +/* Enable to make ^A drop back into the monitor */ +#undef CONFIG_MONITOR +/* Profil syscall support (not yet complete) */ +#undef CONFIG_PROFIL +/* Multiple processes in memory at once */ +#undef CONFIG_MULTI +/* Single tasking - for now while we get it booting */ +#undef CONFIG_SINGLETASK +/* Pure swap */ +#define CONFIG_SWAP_ONLY +#define CONFIG_SPLIT_UDATA +#define UDATA_BLKS 1 + +#define CONFIG_BANKS 1 +/* And swapping */ +#define SWAPDEV 0x0 /* Uses part of IDE slice 0 */ +#define SWAP_SIZE 0x40 /* 32K in 512 byte blocks */ +#define SWAPBASE 0x8000 /* We swap the lot, including stashed uarea */ +#define SWAPTOP 0xFE00 /* so it's a round number of 512 byte sectors */ +#define UDATA_SIZE 0x0200 /* one block */ +#define MAX_SWAPS 32 + +/* Permit large I/O requests to bypass cache and go direct to userspace */ +#define CONFIG_LARGE_IO_DIRECT + +/* Video terminal, not a serial tty */ +#define CONFIG_VT +#define CONFIG_FONT8X8 +#define CONFIG_FONT8X8SMALL + +extern unsigned char vt_mangle_6847(unsigned char c); +#define VT_MAP_CHAR(x) vt_mangle_6847(x) + +/* Vt definitions */ +#define VT_WIDTH 32 +#define VT_HEIGHT 24 +#define VT_RIGHT 31 +#define VT_BOTTOM 23 +#define VT_INITIAL_LINE 0 + +#define VIDEO_BASE 0x0200 + +#define TICKSPERSEC 50 /* Ticks per second */ +#define PROGBASE 0x8000 /* also data base */ +#define PROGLOAD 0x8000 /* also data base */ +#define PROGTOP 0xF000 /* Top of program */ + +/* We need a tidier way to do this from the loader */ +#define CMDLINE NULL /* Location of root dev name */ + +/* Device parameters */ +#define NUM_DEV_TTY 2 +#define NDEVS 2 /* Devices 0..NDEVS-1 are capable of being mounted */ + /* (add new mountable devices to beginning area.) */ +#define TTYDEV 513 /* Device used by kernel for messages, panics */ +#define NBUFS 6 /* Number of block buffers */ +#define NMOUNTS 2 /* Number of mounts at a time */ +#define swap_map(x) ((uint8_t *)(x)) + diff --git a/Kernel/platform-coco2cart/crt0.s b/Kernel/platform-coco2cart/crt0.s new file mode 100644 index 00000000..83b4e369 --- /dev/null +++ b/Kernel/platform-coco2cart/crt0.s @@ -0,0 +1,53 @@ + ; exported + .globl start + + ; imported symbols + .globl _fuzix_main + .globl init_early + .globl init_hardware + .globl kstack_top + .globl _system_id + + ; startup code + .area .start + +; +; Entry - cartridge mapped at C000, system ROM at 8000, RAM at low 32K +; + +start: + orcc #0x10 ; interrupts definitely off + lds #$0100 ; not written by loader + jsr load_image ; load the rest of the OS from disk + jmp main + + .area .text + +main: + lds #kstack_top + ldx #__sectionbase_.bss__ + ldy #__sectionlen_.bss__ + clra +bss_wipe: sta ,x+ + leay -1,y + bne bss_wipe + ; This might be in BSS so don't save until we've wiped! + lda #0 + ldb $80FD ; save from ROM space + cmpb #0x49 ; Dragon32 + beq identified + inca + cmpb #0x31 ; COCO1/2 + beq identified + inca + cmpb #0x32 ; COCO3 + beq identified + inca ; Beats me +identified: + sta _system_id ; what sort of a box are we ? + jsr init_early + jsr init_hardware + jsr _fuzix_main + orcc #0x10 +stop: bra stop + diff --git a/Kernel/platform-coco2cart/devfd.h b/Kernel/platform-coco2cart/devfd.h new file mode 100644 index 00000000..b2754e6b --- /dev/null +++ b/Kernel/platform-coco2cart/devfd.h @@ -0,0 +1,17 @@ +#ifndef __DEVFD_DOT_H__ +#define __DEVFD_DOT_H__ + +/* public interface */ +int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag); +int fd_open(uint8_t minor, uint16_t flag); +void fd_timer_tick(void); + +/* low level interface */ +uint8_t fd_reset(uint8_t *drive); +uint8_t fd_operation(uint8_t *cmd, uint8_t *drive); +uint8_t fd_motor_on(uint8_t drive); +uint8_t fd_motor_off(void); + +#endif /* __DEVFD_DOT_H__ */ + diff --git a/Kernel/platform-coco2cart/device.h b/Kernel/platform-coco2cart/device.h new file mode 100644 index 00000000..e57c6a38 --- /dev/null +++ b/Kernel/platform-coco2cart/device.h @@ -0,0 +1,11 @@ +#ifndef __DEVICE_DOT_H__ +#define __DEVICE_DOT_H__ + +extern uint8_t system_id; +extern uint8_t mpi_present(void); +extern uint8_t mpi_set_slot(uint8_t slot); +extern uint16_t cart_hash(void); + +extern uint8_t spi_setup(void); + +#endif /* __DEVICE_DOT_H__ */ diff --git a/Kernel/platform-coco2cart/devices.c b/Kernel/platform-coco2cart/devices.c new file mode 100644 index 00000000..5c3689bd --- /dev/null +++ b/Kernel/platform-coco2cart/devices.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct devsw dev_tab[] = /* The device driver switch table */ +{ +// minor open close read write ioctl +// ----------------------------------------------------------------- + /* 0: /dev/hd Hard disc block devices (IDE) */ + { ide_open, no_close, ide_read, ide_write, ide_ioctl }, + /* 1: /dev/fd Floppy disc block devices */ + { no_open, no_close, no_rdwr, no_rdwr , no_ioctl }, + /* 2: /dev/tty TTY devices */ + { tty_open, tty_close, tty_read, tty_write, vt_ioctl }, + /* 3: /dev/lpr Printer devices */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 4: /dev/mem etc System devices (one offs) */ + { no_open, no_close, sys_read, sys_write, sys_ioctl }, + /* Pack to 7 with nxio if adding private devices and start at 8 */ + { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 8: /dev/dw DriveWire remote disk images */ + { dw_open, no_close, dw_read, dw_write, no_ioctl }, +}; + +bool validdev(uint16_t dev) +{ + /* This is a bit uglier than needed but the right hand side is + a constant this way */ + if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255) + return false; + else + return true; +} + +void device_init(void) +{ + ide_probe(); +} diff --git a/Kernel/platform-coco2cart/devlpr.c b/Kernel/platform-coco2cart/devlpr.c new file mode 100644 index 00000000..9ddfae4b --- /dev/null +++ b/Kernel/platform-coco2cart/devlpr.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include + +volatile uint8_t * const pia0b = (uint8_t *)0xFF02; +volatile uint8_t * const pia1a = (uint8_t *)0xFF20; +volatile uint8_t * const pia1b = (uint8_t *)0xFF22; + +int lpr_open(uint8_t minor, uint16_t flag) +{ + if (minor < 2) + return 0; + udata.u_error = ENODEV; + return -1; +} + +int lpr_close(uint8_t minor) +{ + if (minor == 1) + dw_lpr_close(); + return 0; +} + +static int iopoll(int sofar) +{ + /* Ought to be a core helper for this lot ? */ + if (need_resched()) + _sched_yield(); + if (udata.u_cursig || udata.u_ptab->p_pending) { + if (sofar) + return sofar; + udata.u_error = EINTR; + return -1; + } + return 0; +} + +int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + uint8_t *p = udata.u_base; + uint8_t *pe = p + udata.u_count; + int n; + irqflags_t irq; + + if (minor == 1) { + while (p < pe) { + if ((n = iopoll(pe - p)) != 0) + return n; + dw_lpr(ugetc(p++)); + } + } else { + while (p < pe) { + /* Printer busy ? */ + while (*pia1b & 1) { + if ((n = iopoll(pe - p)) != 0) + return n; + } + /* The Dragon shares the printer and keyboard scan + lines. Make sure we don't collide */ + irq = di(); + *pia0b = ugetc(p++); + *pia1a |= 0x02; /* Strobe */ + *pia1a &= ~0x02; + irqrestore(irq); + } + } + return pe - p; +} diff --git a/Kernel/platform-coco2cart/devlpr.h b/Kernel/platform-coco2cart/devlpr.h new file mode 100644 index 00000000..0e078ca6 --- /dev/null +++ b/Kernel/platform-coco2cart/devlpr.h @@ -0,0 +1,11 @@ +#ifndef __DEVLPR_DOT_H__ +#define __DEVLPR_DOT_H__ + +extern int lpr_open(uint8_t minor, uint16_t flag); +extern int lpr_close(uint8_t minor); +extern int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag); + +extern void dw_lpr(uint8_t c); +extern void dw_lpr_close(void); + +#endif diff --git a/Kernel/platform-coco2cart/devtty.c b/Kernel/platform-coco2cart/devtty.c new file mode 100644 index 00000000..1c7fc8bf --- /dev/null +++ b/Kernel/platform-coco2cart/devtty.c @@ -0,0 +1,240 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG /* UNdefine to delete debug code sequences */ + +uint8_t *uart_data = (uint8_t *)0xFF04; /* ACIA data */ +uint8_t *uart_status = (uint8_t *)0xFF05; /* ACIA status */ +uint8_t *uart_command = (uint8_t *)0xFF06; /* ACIA command */ +uint8_t *uart_control = (uint8_t *)0xFF07; /* ACIA control */ + +unsigned char tbuf1[TTYSIZ]; +unsigned char tbuf2[TTYSIZ]; + +struct s_queue ttyinq[NUM_DEV_TTY + 1] = { /* ttyinq[0] is never used */ + {NULL, NULL, NULL, 0, 0, 0}, + {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2}, + {tbuf2, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ / 2} +}; + +uint8_t vtattr_cap = 0; +struct vt_repeat keyrepeat; +static uint8_t kbd_timer; + +/* tty1 is the screen tty2 is the serial port */ + +/* Output for the system console (kprintf etc) */ +void kputchar(char c) +{ + if (c == '\n') + tty_putc(1, '\r'); + tty_putc(1, c); +} + +ttyready_t tty_writeready(uint8_t minor) +{ + uint8_t c; + if (minor == 1) + return TTY_READY_NOW; + c = *uart_status; + return (c & 16) ? TTY_READY_NOW : TTY_READY_SOON; /* TX DATA empty */ +} + +/* For DragonPlus we should perhaps support both monitors 8) */ + +void tty_putc(uint8_t minor, unsigned char c) +{ + if (minor == 1) + vtoutput(&c, 1); + else + *uart_data = c; /* Data */ +} + +void tty_sleeping(uint8_t minor) +{ + used(minor); +} + +void tty_setup(uint8_t minor) +{ + if (minor == 2) { + /* FIXME: do proper mode setting */ + *uart_command = 0x01; /* DTR high, IRQ enabled, TX irq disabled 8N1 */ + *uart_control = 0x1E; /* 9600 baud */ + } +} + +int tty_carrier(uint8_t minor) +{ + /* The serial DCD is status bit 5 but not wired */ + return 1; +} + +void tty_interrupt(void) +{ + uint8_t r = *uart_status; + if (r & 0x8) { + r = *uart_data; + tty_inproc(2,r); + } +} + +uint8_t keymap[8]; +static uint8_t keyin[8]; +static uint8_t keybyte, keybit; +static uint8_t newkey; +static int keysdown = 0; +/* FIXME: shouldn't COCO shiftmask also differ ??? 0x02 not 0x40 ?? */ +static uint8_t shiftmask[8] = { + 0, 0x40, 0, 0, 0, 0, 0, 0x40 +}; + +static uint8_t rbit[8] = { + 0xFE, + 0xFD, + 0xFB, + 0xF7, + 0xEF, + 0xDF, + 0xBF, + 0x7F, +}; + +/* Row inputs: multiplexed with the joystick */ +static volatile uint8_t *pia_row = (uint8_t *)0xFF00; +/* Columns for scanning: multiplexed with the printer port */ +static volatile uint8_t *pia_col = (uint8_t *)0xFF02; +/* Control */ +static volatile uint8_t *pia_ctrl = (uint8_t *)0xFF03; + +static void keyproc(void) +{ + int i; + uint8_t key; + + for (i = 0; i < 8; i++) { + /* We do the scan in software on the Dragon */ + *pia_col = rbit[i]; + keyin[i] = ~*pia_row; + key = keyin[i] ^ keymap[i]; + if (key) { + int n; + int m = 1; + for (n = 0; n < 7; n++) { + if ((key & m) && (keymap[i] & m)) { + if (!(shiftmask[i] & m)) + keysdown--; + } + if ((key & m) && !(keymap[i] & m)) { + if (!(shiftmask[i] & m)) { + keysdown++; + newkey = 1; + keybyte = i; + keybit = n; + } + } + m += m; + } + } + keymap[i] = keyin[i]; + } + if (system_id) { /* COCO series */ + keybit += 2; + if (keybit > 5) + keybit -= 6; + } +} + +#ifdef CONFIG_COCO_KBD +uint8_t keyboard[8][7] = { + { '@', 'h', 'p', 'x', '0', '8', KEY_ENTER }, + { 'a', 'i', 'q', 'y', '1', '9', 0 /* clear - used as ctrl*/ }, + { 'b', 'j', 'r', 'z', '2', ':', KEY_ESC /* break (used for esc) */ }, + { 'c', 'k', 's', '^' /* up */, '3', ';' , 0 /* NC */ }, + { 'd', 'l', 't', '|' /* down */, '4', ',', 0 /* NC */ }, + { 'e', 'm', 'u', KEY_BS /* left */, '5', '-', 0 /* NC */ }, + { 'f', 'n', 'v', KEY_TAB /* right */, '6', '.', 0 /* NC */ }, + { 'g', 'o', 'w', ' ', '7', '/', 0 /* shift */ }, +}; + +uint8_t shiftkeyboard[8][7] = { + { '\\', 'H', 'P', 'X', '_', '(', KEY_ENTER }, + { 'A', 'I', 'Q', 'Y', '!', ')', 0 /* clear - used as ctrl */ }, + { 'B', 'J', 'R', 'Z', '"', '*', CTRL('C') /* break */ }, + { 'C', 'K', 'S', '[' /* up */, '#', '+', 0 /* NC */ }, + { 'D', 'L', 'T', ']' /* down */, '$', '<', 0 /* NC */ }, + { 'E', 'M', 'U', '{' /* left */, '%', '=', 0 /* NC */ }, + { 'F', 'N', 'V', '}' /* right */, '&', '>', 0 /* NC */ }, + { 'G', 'O', 'W', ' ', '\'', '?', 0 /* shift */ }, +}; +#else +uint8_t keyboard[8][7] = { + { '0', '8', '@', 'h', 'p', 'x', KEY_ENTER }, + { '1', '9', 'a', 'i', 'q', 'y', 0 /* clear - used as ctrl*/ }, + { '2', ':', 'b', 'j', 'r', 'z', KEY_ESC /* break (used for esc) */ }, + { '3', ';', 'c', 'k', 's', '^' /* up */, 0 /* NC */ }, + { '4', ',', 'd', 'l', 't', '|' /* down */, 0 /* NC */ }, + { '5', '-', 'e', 'm', 'u', KEY_BS /* left */, 0 /* NC */ }, + { '6', '.', 'f', 'n', 'v', KEY_TAB /* right */, 0 /* NC */ }, + { '7', '/', 'g', 'o', 'w', ' ', 0 /* shift */ }, +}; + +uint8_t shiftkeyboard[8][7] = { + { '_', '(', '\\', 'H', 'P', 'X', KEY_ENTER }, + { '!', ')', 'A', 'I', 'Q', 'Y', 0 /* clear - used as ctrl*/ }, + { '"', '*', 'B', 'J', 'R', 'Z', CTRL('C') /* break */ }, + { '#', '+', 'C', 'K', 'S', '[' /* up */, 0 /* NC */ }, + { '$', '<', 'D', 'L', 'T', ']' /* down */, 0 /* NC */ }, + { '%', '=', 'E', 'M', 'U', '{' /* left */, 0 /* NC */ }, + { '&', '>', 'F', 'N', 'V', '}' /* right */, 0 /* NC */ }, + { '\'', '?', 'G', 'O', 'W', ' ', 0 /* shift */ }, +}; +#endif /* COCO_KBD */ + +static void keydecode(void) +{ + uint8_t c; + + if (keymap[7] & 64) /* shift */ + c = shiftkeyboard[keybyte][keybit]; + else + c = keyboard[keybyte][keybit]; + if (keymap[1] & 64) { /* control */ + if (c > 31 && c < 127) + c &= 31; + } + tty_inproc(1, c); +} + +void platform_interrupt(void) +{ + uint8_t i = *pia_ctrl; + if (i & 0x80) { + *pia_col; + newkey = 0; + keyproc(); + if (keysdown && keysdown < 3) { + if (newkey) { + keydecode(); + kbd_timer = keyrepeat.first; + } else if (! --kbd_timer) { + keydecode(); + kbd_timer = keyrepeat.continual; + } + } +// fd_timer_tick(); + timer_interrupt(); + } +} + +/* This is used by the vt asm code, but needs to live at the top of the kernel */ +uint16_t cursorpos; + diff --git a/Kernel/platform-coco2cart/devtty.h b/Kernel/platform-coco2cart/devtty.h new file mode 100644 index 00000000..2d6f8735 --- /dev/null +++ b/Kernel/platform-coco2cart/devtty.h @@ -0,0 +1,12 @@ +#ifndef __DEVTTY_DOT_H__ +#define __DEVTTY_DOT_H__ + +#define KEY_ROWS 8 +#define KEY_COLS 7 +extern uint8_t keymap[8]; +extern uint8_t keyboard[8][7]; +extern uint8_t shiftkeyboard[8][7]; + +extern int gfx_ioctl(uint8_t minor, uarg_t arg, char *ptr); + +#endif diff --git a/Kernel/platform-coco2cart/discard.c b/Kernel/platform-coco2cart/discard.c new file mode 100644 index 00000000..17917697 --- /dev/null +++ b/Kernel/platform-coco2cart/discard.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *sysname[] = {"Dragon", "COCO", "COCO3", "Unknown"}; + +struct cart_rom_id { + uint16_t hash; + uint8_t id; + const char *name; +}; + +static const char empty[] = "(empty)"; + +struct cart_rom_id carts[] = { + { 0x72B0, CART_DRAGONDOS, "DragonDOS" }, + { 0x9063, CART_DELTADOS, "DeltaDOS" }, + { 0xB400, 0, empty }, + { 0xC248, CART_RSDOS, "RS-DOS" }, + { 0xE1BA, CART_ORCH90, "Orchestra-90 CC" }, + { 0x0000, 0, "No ROM" } +}; + +static struct cart_rom_id *cart_lookup(uint16_t hash) +{ + struct cart_rom_id *cart = carts; + do { + if (cart->hash == hash) + return cart; + } while(cart++->hash); + return NULL; +} + +void map_init(void) +{ + uint8_t i; + uint8_t bslot = 0; + uint16_t hash; + struct cart_rom_id *rom; + + kprintf("%s system.\n", sysname[system_id]); + if (mpi_present()) { + kputs("MPI cartridge detected.\n"); + cartslots = 4; + bootslot = mpi_set_slot(0); + bslot = bootslot & 3; + } + for (i = 0; i < cartslots; i++) { + mpi_set_slot((i << 4) | i); + hash = cart_hash(); + rom = cart_lookup(hash); + if (rom) { + kprintf("%d: %s %c\n", + i, rom->name, + i == bootslot ? '*':' '); + carttype[i] = rom->id; + } + else + kprintf("%d: Unknown(%x) %c\n", + i, hash, + i == bslot ? '*':' '); + } + mpi_set_slot(bootslot); + /* We put swap on the start of slice 0, but with the first 64K free + so we can keep the OS image linearly there */ + for (i = 0; i < MAX_SWAPS; i++) + swapmap_add(i + 128); +} diff --git a/Kernel/platform-coco2cart/drivewire.s b/Kernel/platform-coco2cart/drivewire.s new file mode 100644 index 00000000..85962feb --- /dev/null +++ b/Kernel/platform-coco2cart/drivewire.s @@ -0,0 +1,237 @@ +; +; DriveWire sector routines +; +; Copyright 2015 Tormod Volden +; Copyright 2008 Boisy G. Pitre +; Distributed under the GNU General Public License, version 2 or later. +; + + ; exported + .globl _dw_operation + .globl _dw_reset + .globl _dw_transaction + + .globl _dw_lpr + .globl _dw_lpr_close + .globl _dw_rtc_read + + + ; imported + .globl map_process_a + .globl map_kernel + + .area .common + +_dw_transaction: + pshs cc,y ; save caller + orcc #0x50 ; stop interrupts + tstb ; rawflag? + beq skip@ ; nope - then skip switching to process map + jsr map_process_always +skip@ ldy 5,s ; Y = number of bytes to send + beq ok@ ; no bytes to write - leave + jsr DWWrite ; send to DW + ldx 7,s ; X is receive buffer + ldy 9,s ; Y = number of bytes to receive + beq ok@ ; no bytes to send - leave + jsr DWRead ; read in that many bytes + bcs frame@ ; C set on framing error + bne part@ ; Z zet on all bytes received +ok@ ldx #0 ; no error +out@ jsr map_kernel + puls cc,y,pc ; return +frame@ ldx #-1 ; frame error + bra out@ +part@ ldx #-2 ; not all bytes received! + bra out@ + +_dw_reset: + ; maybe reinitalise PIA here? + ; and send DW_INIT request to server? + rts + +_dw_operation: + pshs y + ; get parameters from C, X points to cmd packet + lda 7,x ; page map LSB + jsr map_process_a ; same as map_process on nx32 + lda 5,x ; minor = drive number + ldb ,x ; write flag + ; buffer location into Y + ldy 3,x + ; sector number into X + ldx 1,x + tstb + bne @write + jsr dw_read_sector + bra @done +@write jsr dw_write_sector +@done bcs @err + bne @err + ldx #0 +@fin jsr map_kernel +@ret puls y,pc +@err ldx #0xFFFF + bra @fin + +; Write a sector to the DriveWire server +; Drive number in A, sector number in X, buffer location in Y +; Sets carry or non-zero flags on error +dw_write_sector: + ; header: OP, drive = A, LSN 23-16 = 0, LSN 15-8 and LSN 7-0 = X + clrb + pshs a,b,x + ldb #OP_WRITE + pshs b + ; send header + tfr s,x + pshs y ; save buffer location + ldy #5 + jsr DWWrite + ; send payload + ldx ,s + ldy #256 + jsr DWWrite + ; calculate checksum of payload, backwards + exg x,y ; Y is zero after DWWrite +@sum ldb ,-y + abx + cmpy ,s ; buffer location start + bne @sum + stx ,s ; checksum to send + tfr s,x + ldy #2 + jsr DWWrite + ; get status byte from server into following byte + ldy #1 + clra ; clear carry bit for BECKER variant + jsr DWRead + leas 7,s + bcs @ret + bne @ret + ldb -5,s ; received status byte (zero is success) +@ret rts + +; +; Based on "DoRead" by Boisy G. Pitre from DWDOS hosted on toolshed.sf.net +; Read a sector from the DriveWire server +; Drive number in A, 16-bit sector in X, buffer location in Y +; Sets carry or non-zero flags on error + +dw_read_sector: + ; header: OP, drive = A, LSN 23-16 = 0, LSN 15-8 and LSN 7-0 = X + clrb + pshs d,x,y + lda #OP_READEX +ReRead pshs a + leax ,s + ldy #$0005 + lbsr DWWrite + puls a + ldx 4,s ; get read buffer pointer + ldy #256 ; read 256 bytes + ldd #133*1 ; 1 second timeout + bsr DWRead + bcs ReadEx + bne ReadEx +; Send 2 byte checksum + pshs y + leax ,s + ldy #2 + lbsr DWWrite + ldy #1 + ldd #133*1 + bsr DWRead + leas 2,s + bcs ReadEx + bne ReadEx +; Check received status byte + lda ,s + beq ReadEx + cmpa #E_CRC + bne ReadErr + lda #OP_REREADEX + clr ,s + bra ReRead +ReadErr comb ; set carry bit +ReadEx puls d,x,y,pc + + .area .text +; +; Virtual devices on DriveWire +; +; Line printer +; RTC +; Virtual serial ??? +; +; +; dw_lpr(uint8_t c) +; +; Print a byte to drive wire printers +; +; B holds the byte. Call with interrupts off +; +_dw_lpr: + pshs y + stb lprb2 + ldx #lprb + ldy #2 +dwop: + jsr DWWrite + puls y,pc + +; +; dw_lpr_close(void) +; +; Close the printer device +; +_dw_lpr_close: + pshs y + ldx #lprb3 + ldy #1 + bra dwop + +; +; uint8_t dw_rtc_read(uint8_t *p) +; +_dw_rtc_read: + pshs y + ldy #2 + pshs x + ldx #lprrtw + jsr DWWrite + puls x + ldy #6 + clra + jsr DWRead + clrb + sbcb #0 + bra dwop + + .area .data + +lprb: .db 0x50 ; print char +lprb2: .db 0x00 ; filled in with byte to send +lprb3: .db 0x46 ; printer flush +lprrtw: .db 0x23 ; request for time + + .area .common + + +; Used by DWRead and DWWrite +IntMasks equ $50 +NOINTMASK equ 1 + +; Hardcode these for now so that we can use below files unmodified +H6309 equ 0 +BECKER equ 1 +ARDUINO equ 0 +JMCPBCK equ 0 +BAUD38400 equ 0 + +; These files are copied almost as-is from HDB-DOS + *PRAGMA nonewsource + include "dw.def" + include "dwread.s" + include "dwwrite.s" + diff --git a/Kernel/platform-coco2cart/dwread.s b/Kernel/platform-coco2cart/dwread.s new file mode 100644 index 00000000..1b4c587b --- /dev/null +++ b/Kernel/platform-coco2cart/dwread.s @@ -0,0 +1,341 @@ +******************************************************* +* +* Copied from HDB-DOS from toolshed.sf.net +* The original code is public domain +* +* DWRead +* Receive a response from the DriveWire server. +* Times out if serial port goes idle for more than 1.4 (0.7) seconds. +* Serial data format: 1-8-N-1 +* 4/12/2009 by Darren Atkinson +* +* Entry: +* X = starting address where data is to be stored +* Y = number of bytes expected +* +* Exit: +* CC = carry set on framing error, Z set if all bytes received +* X = starting address of data received +* Y = checksum +* U is preserved. All accumulators are clobbered +* + + IFNE ARDUINO +* Note: this is an optimistic routine. It presumes that the server will always be there, and +* has NO timeout fallback. It is also very short and quick. +DWRead clra ; clear Carry (no framing error) + pshs u,x,cc ; preserve registers + leau ,x + ldx #$0000 +loop@ tst $FF51 ; check for CA1 bit (1=Arduino has byte ready) + bpl loop@ ; loop if not set + ldb $FF50 ; clear CA1 bit in status register + stb ,u+ ; save off acquired byte + abx ; update checksum + leay ,-y + bne loop@ + + leay ,x ; return checksum in Y + puls cc,x,u,pc ; restore registers and return + + ELSE + + IFNE JMCPBCK +* NOTE: There is no timeout currently on here... +DWRead clra ; clear Carry (no framing error) + deca ; clear Z flag, A = timeout msb ($ff) + tfr cc,b + pshs u,x,dp,b,a ; preserve registers, push timeout msb + leau ,x + ldx #$0000 + IFEQ NOINTMASK + orcc #IntMasks + ENDC +loop@ ldb $FF4C + bitb #$02 + beq loop@ + ldb $FF44 + stb ,u+ + abx + leay ,-y + bne loop@ + + tfr x,y + ldb #0 + lda #3 + leas 1,s ; remove timeout msb from stack + inca ; A = status to be returned in C and Z + ora ,s ; place status information into the.. + sta ,s ; ..C and Z bits of the preserved CC + leay ,x ; return checksum in Y + puls cc,dp,x,u,pc ; restore registers and return + ELSE + IFNE BECKER + IFNDEF BCKSTAT +BCKSTAT equ $FF41 + ENDC + IFNDEF BCKPORT +BCKPORT equ $FF42 + ENDC +* NOTE: There is no timeout currently on here... +DWRead clra ; clear Carry (no framing error) + deca ; clear Z flag, A = timeout msb ($ff) + tfr cc,b + pshs u,x,dp,b,a ; preserve registers, push timeout msb + leau ,x + ldx #$0000 + IFEQ NOINTMASK + orcc #IntMasks + ENDC +loop@ ldb BCKSTAT + bitb #$02 + beq loop@ + ldb BCKPORT + stb ,u+ + abx + leay ,-y + bne loop@ + tfr x,y + ldb #0 + lda #3 +timeout leas 1,s ; remove timeout msb from stack + inca ; A = status to be returned in C and Z + ora ,s ; place status information into the.. + sta ,s ; ..C and Z bits of the preserved CC + leay ,x ; return checksum in Y + puls cc,dp,x,u,pc ; restore registers and return + ENDC + ENDC + ENDC + + IFEQ BECKER+JMCPBCK+ARDUINO + IFNE BAUD38400 +******************************************************* +* 38400 bps using 6809 code and timimg +******************************************************* + +DWRead clra ; clear Carry (no framing error) + deca ; clear Z flag, A = timeout msb ($ff) + tfr cc,b + pshs u,x,dp,b,a ; preserve registers, push timeout msb + IFEQ NOINTMASK + orcc #IntMasks ; mask interrupts + ENDC + tfr a,dp ; set direct page to $FFxx + setdp $ff + leau ,x ; U = storage ptr + ldx #0 ; initialize checksum + adda #2 ; A = $01 (serial in mask), set Carry + +* Wait for a start bit or timeout +rx0010 bcc rxExit ; exit if timeout expired + ldb #$ff ; init timeout lsb +rx0020 bita +#include +#include +#include +#include +#include +#include +#include + +uint8_t membanks; +uint8_t system_id; +uint8_t cartslots = 1; +uint8_t carttype[4]; +uint8_t bootslot = 0; +uint16_t swap_dev; + +void platform_idle(void) +{ +} + +uint8_t platform_param(char *p) +{ + return 0; +} + +void do_beep(void) +{ +} + +void platform_discard(void) +{ +} + +/* Find a cartridge or it's slot */ +int cart_find(int id) +{ + int i; + for (i = 0; i < id; i++) { + if (carttype[i] == id) + return i; + } + return -1; +} + +unsigned char vt_mangle_6847(unsigned char c) +{ + if (c >= 96) + c -= 32; + c &= 0x3F; + return c; +} diff --git a/Kernel/platform-coco2cart/mini_ide.c b/Kernel/platform-coco2cart/mini_ide.c new file mode 100644 index 00000000..bbb0c936 --- /dev/null +++ b/Kernel/platform-coco2cart/mini_ide.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include "mini_ide.h" + +#define MAX_HD 2 + +uint8_t ide_present = 1; + +#define data ((volatile uint8_t *)0xFF50) +#define error ((volatile uint8_t *)0xFF51) +#define count ((volatile uint8_t *)0xFF52) +#define sec ((volatile uint8_t *)0xFF53) +#define cyll ((volatile uint8_t *)0xFF54) +#define cylh ((volatile uint8_t *)0xFF55) +#define devh ((volatile uint8_t *)0xFF56) +#define status ((volatile uint8_t *)0xFF57) +#define cmd ((volatile uint8_t *)0xFF57) +#define datal ((volatile uint8_t *)0xFF58) + +/* FIXME: switch to the correct mpi slot on entry */ +static int ide_transfer(uint8_t minor, bool is_read, uint8_t rawflag) +{ + uint16_t nb = udata.u_nblock; + uint8_t *dptr = udata.u_dptr; + + if (rawflag == 1 && d_blkoff(9)) + return -1; + + while(*status & 0x80); /* Wait !BUSY */ + *devh = (minor & 0x80) ? 0x50 : 0x40 ; /* LBA, device */ + while(*status & 0x80); /* Wait !BUSY */ + /* FIXME - slices of about 4MB might be saner! */ + *cylh = minor & 0x7F; /* Slice number */ + *cyll = udata.u_block >> 8; /* Each slice is 32MB */ + *sec = udata.u_block & 0xFF; + *count = udata.u_nblock; + while(!(*status & 0x40)); /* Wait DRDY */ + *cmd = is_read ? 0x20 : 0x30; + while(*status & 0x08); /* Wait DRQ */ + while(udata.u_nblock--) { + unsigned int i; + while(!(*status & 0x08)); /* Wait DRQ */ + if (is_read) { + for (i = 0; i < 256; i++) { + *dptr++ = *data; + *dptr++ = *datal; + } + } else { + for (i = 0; i < 256; i++) { + *datal = dptr[1]; + *data = *dptr++; + dptr++; + } + } + } + while(*status & 0x80); /* Wait !BUSY */ + if (*status & 0x01) { /* Error */ + kprintf("ide%d: error %x\n", *error); + udata.u_error = EIO; + return -1; + } + return nb; + +} + +int ide_open(uint8_t minor, uint16_t flag) +{ + if(minor > 1 || !(ide_present & (1 << minor))) { + udata.u_error = ENODEV; + return -1; + } + return 0; +} + +int ide_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + return ide_transfer(minor, true, rawflag); +} + +int ide_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + return ide_transfer(minor, false, rawflag); +} + +int ide_ioctl(uint8_t minor, uarg_t request, char *unused) +{ + if (request != BLKFLSBUF) + return -1; + while(*status & 0x80); /* Wait !BUSY */ + *devh = (minor & 0x80) ? 0x10 : 0x40 ; /* LBA, device */ + while(*status & 0x80); /* Wait !BUSY */ + while(!(*status & 0x40)); /* Wait DRDY */ + *cmd = 0xE7; + while(*status & 0x80); /* Wait !BUSY */ + return 0; +} + +/* TODO: probe the devices more carefully and do EDD and LBA checks in discard + code */ diff --git a/Kernel/platform-coco2cart/mini_ide.h b/Kernel/platform-coco2cart/mini_ide.h new file mode 100644 index 00000000..a72b4919 --- /dev/null +++ b/Kernel/platform-coco2cart/mini_ide.h @@ -0,0 +1,6 @@ +extern int ide_open(uint8_t minor, uint16_t flag); +extern int ide_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +extern int ide_write(uint8_t minor, uint8_t rawflag, uint8_t flag); +extern int ide_ioctl(uint8_t minor, uarg_t request, char *data); + +extern uint8_t ide_present; diff --git a/Kernel/platform-coco2cart/mini_ide_discard.c b/Kernel/platform-coco2cart/mini_ide_discard.c new file mode 100644 index 00000000..49eff530 --- /dev/null +++ b/Kernel/platform-coco2cart/mini_ide_discard.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include "mini_ide.h" + +#define data ((volatile uint8_t *)0xFF50) +#define error ((volatile uint8_t *)0xFF51) +#define count ((volatile uint8_t *)0xFF52) +#define sec ((volatile uint8_t *)0xFF53) +#define cyll ((volatile uint8_t *)0xFF54) +#define cylh ((volatile uint8_t *)0xFF55) +#define devh ((volatile uint8_t *)0xFF56) +#define status ((volatile uint8_t *)0xFF57) +#define cmd ((volatile uint8_t *)0xFF57) +#define datal ((volatile uint8_t *)0xFF58) + +static timer_t giveup; + +static int ide_wait_op(uint8_t mask, uint8_t val) +{ + while((*status & mask) != val) { + if (timer_expired(giveup)) { + kputs(" - no response\n"); + return -1; + } + } + return 0; +} + +static int ide_wait_nbusy(void) +{ + return ide_wait_op(0x80, 0x00); +} + +static int ide_wait_drdy(void) +{ + return ide_wait_op(0x40, 0x40); +} + +static int ide_wait_drq(void) +{ + return ide_wait_op(0x08, 0x08); +} + + +static void ide_identify(int dev, uint8_t *buf) +{ + uint8_t *dptr = buf; + int i; + + giveup = set_timer_ms(2000); + + if (ide_wait_nbusy() == -1) + return; + *devh = dev << 4; /* Select */ + if (ide_wait_nbusy() == -1) + return; + if (ide_wait_drdy() == -1) + return; + *cmd = 0xEC; /* Identify */ + if (ide_wait_drq() == -1) + return; + for (i = 0; i < 256; i++) { + *dptr++ = *data; + *dptr++ = *datal; + } + if (ide_wait_nbusy() == -1) + return; + if (*status & 1) + return; + if (ide_wait_drdy() == -1) + return; + kprintf("%x : ", dev); + /* Check the LBA bit is set, and print the name */ + dptr = buf + 54; /* Name info */ + if (*dptr) + for (i = 0; i < 40; i++) + kputchar(*dptr++); + if (!(buf[99] & 0x02)) { /* No LBA ? */ + kputs(" - non-LBA\n"); + return; + } + kputs(" - OK\n"); + ide_present |= (1 << dev); +} + +int ide_probe(void) +{ + uint8_t *buf = (uint8_t *)tmpbuf(); + /* Issue an EDD if we can - timeout -> no drives */ + /* Now issue an identify for each drive */ + ide_identify(0, buf); + if (ide_present) + ide_identify(1, buf); + brelse((bufptr) buf); +} diff --git a/Kernel/platform-coco2cart/target.mk b/Kernel/platform-coco2cart/target.mk new file mode 100644 index 00000000..e9abbba1 --- /dev/null +++ b/Kernel/platform-coco2cart/target.mk @@ -0,0 +1 @@ +export CPU = 6809 diff --git a/Kernel/platform-coco2cart/tricks.s b/Kernel/platform-coco2cart/tricks.s new file mode 100644 index 00000000..bb436180 --- /dev/null +++ b/Kernel/platform-coco2cart/tricks.s @@ -0,0 +1,183 @@ +; +; TODO: udata needs to be in common space so swap needs to be smarter +; about split udata +; + .module tricks + + # imported + .globl _newproc + .globl _chksigs + .globl _getproc + .globl _trap_monitor + .globl _inint + .globl map_kernel + .globl map_process + .globl map_process_a + .globl map_process_always + .globl copybank + .globl _nready + .globl _platform_idle + + # exported + .globl _switchout + .globl _switchin + .globl _dofork + .globl _ramtop + + include "kernel.def" + include "../kernel09.def" + + .area .common + + ; ramtop must be in common although not used 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(). +; +; +; This function can have no arguments or auto variables. +_switchout: + orcc #0x10 ; irq off + jsr _chksigs + + ; save machine state, including Y and U used by our C code + ldd #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: + pshs d,y,u + sts U_DATA__U_SP ; this is where the SP is restored in _switchin + + ; find another (or same) process to run, returned in X + jsr _getproc + jsr _switchin + ; we should never get here + jsr _trap_monitor + +badswitchmsg: + .ascii "_switchin: FAIL" + .db 13 + .db 10 + .db 0 + +newpp .dw 0 + +; new process pointer is in X +_switchin: + orcc #0x10 ; irq off + + stx newpp + ; get process table + lda P_TAB__P_PAGE_OFFSET+1,x ; LSB of 16-bit page no + + cmpa #0 + bne not_swapped + jsr _swapper ; void swapper(ptptr p) + ldx newpp + lda P_TAB__P_PAGE_OFFSET+1,x + +not_swapped: + ; we have now new stacks so get new stack pointer before any jsr + lds U_DATA__U_SP + + ; get back kernel page so that we see process table + jsr map_kernel + + ldx newpp + ; check u_data->u_ptab matches what we wanted + cmpx U_DATA__U_PTAB + bne switchinfail + + lda #P_RUNNING + sta P_TAB__P_STATUS_OFFSET,x + + ; fix any moved page pointers + lda P_TAB__P_PAGE_OFFSET+1,x + sta U_DATA__U_PAGE+1 + + ldx #0 + stx _runticks + + ; restore machine state -- note we may be returning from either + ; _switchout or _dofork + lds U_DATA__U_SP + puls x,y,u ; return code and saved U and Y + + ; enable interrupts, if the ISR isn't already running + lda U_DATA__U_ININTERRUPT + bne swtchdone ; in ISR, leave interrupts off + andcc #0xef +swtchdone: + rts + +switchinfail: + jsr outx + ldx #badswitchmsg + jsr outstring + ; something went wrong and we didn't switch in what we asked for + jmp _trap_monitor + + .area .data + +fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry + + .area .common +; +; 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 + orcc #0x10 ; should already be the case ... belt and braces. + + ; new process in X, get parent pid into y + + stx fork_proc_ptr + ldx P_TAB__P_PID_OFFSET,x + + ; Save the stack pointer and critical registers (Y and U used by C). + ; When this process (the parent) is switched back in, it will be as if + ; it returns with the value of the child's pid. + pshs x,y,u ; x has p->p_pid from above, the return value in the parent + + ; 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 X (ie return code) containing the child PID. + ; Hurray. + sts U_DATA__U_SP + + ; now we're in a safe state for _switchin to return in the parent + ; process. + + ldx U_DATA__U_PTAB + jsr _swapout + cmpd #0 + bne forked_up + + ; 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. + puls x + + ldx fork_proc_ptr + jsr _newproc + + ; any calls to map process will now map the childs memory + + ; in the child process, fork() returns zero. + ldx #0 + ; runticks = 0; + stx _runticks + ; + ; 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(). + puls y,u,pc + +forked_up: ; d is already -1 + puls y,u,pc + rts \ No newline at end of file diff --git a/Kernel/platform-coco2cart/usermem.s b/Kernel/platform-coco2cart/usermem.s new file mode 100644 index 00000000..cb32f394 --- /dev/null +++ b/Kernel/platform-coco2cart/usermem.s @@ -0,0 +1,114 @@ + .module usermem + +; +; 6809 + Cartridge copy to and from userspace +; Assumes the irq handler saves/restores cartridge/ram correctly +; Assumes we never copy to/from ROM space +; + + include "kernel.def" + include "../kernel09.def" + + ; exported + .globl __ugetc + .globl __ugetw + .globl __uget + .globl __ugets + .globl __uputc + .globl __uputw + .globl __uput + .globl __uzero + + .area .common + +__ugetc: + jsr map_process_always + ldb ,x + clra + tfr d,x + jmp map_kernel + +__ugetw: + jsr map_process_always + ldx ,x + jmp map_kernel + +__uget: + pshs u,y + ldu 6,s ; user address + ldy 8,s ; count + jsr map_process_always +ugetl: + lda ,x+ + sta ,u+ + leay -1,y + bne ugetl + ldx #0 + jsr map_kernel + puls u,y,pc + +__ugets: + pshs u,y + ldu 6,s ; user address + ldy 8,s ; count + jsr map_process_always +ugetsl: + lda ,x+ + beq ugetse + sta ,u+ + leay -1,y + bne ugetsl + jsr map_kernel + ldx #0xffff ; unterminated - error + lda #0 + sta -1,u ; force termination + puls u,y,pc +ugetse: + sta ,u + ldx #0 + puls u,y,pc + + +__uputc: + ldd 2,s + jsr map_process_always + exg d,x + stb ,x + ldx #0 + jmp map_kernel + +__uputw: + ldd 2,s + jsr map_process_always + exg d,x + std ,x + ldx #0 + jmp map_kernel + +; X = source, user, size on stack +__uput: + pshs u,y + ldu 6,s ; user address + ldy 8,s ; count + jsr map_process_always +uputl: + lda ,x+ + sta ,u+ + leay -1,y + bne uputl + ldx #0 + jsr map_kernel + puls u,y,pc + +__uzero: + pshs y + lda #0 + ldy 4,s + jsr map_process_always +uzloop: + sta ,x+ + leay -1,y + bne uzloop + jsr map_kernel + ldx #0 + puls y,pc diff --git a/Kernel/platform-coco2cart/video.s b/Kernel/platform-coco2cart/video.s new file mode 100644 index 00000000..ab65cb30 --- /dev/null +++ b/Kernel/platform-coco2cart/video.s @@ -0,0 +1,424 @@ + .module dragonvideo + + ; Methods provided + .globl _vid256x192 + .globl _plot_char + .globl _scroll_up + .globl _scroll_down + .globl _clear_across + .globl _clear_lines + .globl _cursor_on + .globl _cursor_off + .globl _vtattr_notify + + .globl _video_read + .globl _video_write + .globl _video_cmd + + ; + ; Imports + ; + .globl _fontdata_8x8 + .globl _vidattr + + include "kernel.def" + include "../kernel09.def" + + .area .text + +; +; Dragon video drivers +; +; SAM V2=1 V1=1 V0=- +; 6847 A/G=1 GM2=1 GM1=1 GM0=1 +; +_vid256x192: + sta $ffc0 + sta $ffc3 + sta $ffc5 + lda $ff22 + anda #$07 + ora #$f0 + sta $ff22 + rts + +; +; Compute the video base address +; A = X, B = Y +; +vidaddr: + ldy #VIDEO_BASE + exg a,b + leay d,y ; 256 x Y + X + rts +; +; plot_char(int8_t y, int8_t x, uint16_t c) +; +_plot_char: + pshs y + lda 4,s + bsr vidaddr ; preserves X (holding the char) + tfr x,d + anda $7F ; no high font bits + suba $20 ; skip control symbols + rolb ; multiply by 8 + rola + rolb + rola + rolb + rola + tfr d,x + leax _fontdata_8x8,x ; relative to font + ldb _vtattr + andb #0x3F ; drop the bits that don't affect our video + beq plot_fast + + ; + ; General purpose plot with attributes, we only fastpath + ; the simple case + ; + clra +plot_loop: + sta _vtrow + ldb _vtattr + cmpa #7 ; Underline only applies on the bottom row + beq ul_this + andb #0xFD +ul_this: + cmpa #3 ; italic shift right for < 3 + blt ital_1 + andb #0xFB + bra maskdone +ital_1: + cmpa #5 ; italic shift right for >= 5 + blt maskdone + bitb #0x04 + bne maskdone + orb #0x40 ; spare bit borrow for bottom of italic + andb #0xFB +maskdone: + lda ,x+ ; now throw the row away for a bit + bitb #0x10 + bne notbold + lsra + ora -1,x ; shift and or to make it bold +notbold: + bitb #0x04 ; italic by shifting top and bottom + beq notital1 + lsra +notital1: + bitb #0x40 + beq notital2 + lsla +notital2: + bitb #0x02 + beq notuline + lda #0xff ; underline by setting bottom row +notuline: + bitb #0x01 ; inverse or not: we are really in inverse + bne plot_inv ; by default so we complement except if + coma ; inverted +plot_inv: + bitb #0x20 ; overstrike or plot ? + bne overstrike + sta ,y + bra plotnext +overstrike: + anda ,y + sta ,y +plotnext: + leay 32,y + lda _vtrow + inca + cmpa #8 + bne plot_loop + puls y,pc +; +; Fast path for normal attributes +; +plot_fast: + lda ,x+ ; simple 8x8 renderer for now + coma + sta 0,y + lda ,x+ + coma + sta 32,y + lda ,x+ + coma + sta 64,y + lda ,x+ + coma + sta 96,y + lda ,x+ + coma + sta 128,y + lda ,x+ + coma + sta 160,y + lda ,x+ + coma + sta 192,y + lda ,x+ + coma + sta 224,y + puls y,pc + +; +; void scroll_up(void) +; +_scroll_up: + pshs y + ldy #VIDEO_BASE + leax 256,y +vscrolln: + ; Unrolled line by line copy + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + ldd ,x++ + std ,y++ + cmpx video_endptr + bne vscrolln + puls y,pc + +; +; void scroll_down(void) +; +_scroll_down: + pshs y + ldy #VIDEO_END + leax -256,y +vscrolld: + ; Unrolled line by line loop + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + ldd ,--x + std ,--y + cmpx video_startptr + bne vscrolld + puls y,pc + +video_startptr: + .dw VIDEO_BASE +video_endptr: + .dw VIDEO_END + +; +; clear_across(int8_t y, int8_t x, uint16_t l) +; +_clear_across: + pshs y + lda 4,s ; x into A, B already has y + jsr vidaddr ; Y now holds the address + tfr x,d ; Shuffle so we are writng to X and the counter + tfr y,x ; l is in d + lda #$ff +clearnext: + sta ,x + sta 32,x + sta 64,x + sta 96,x + sta 128,x + sta 160,x + sta 192,x + sta 224,x + leax 1,x + decb + bne clearnext + puls y,pc +; +; clear_lines(int8_t y, int8_t ct) +; +_clear_lines: + pshs y + clra ; b holds Y pos already + jsr vidaddr ; y now holds ptr to line start + tfr y,x + ldd #$ffff + lsl 4,s + lsl 4,s + lsl 4,s +wipel: + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + std ,x++ + dec 4,s ; count of lines + bne wipel + puls y,pc + +_cursor_on: + pshs y + lda 4,s + jsr vidaddr + tfr y,x + puls y + stx cursor_save + ; Fall through +_cursor_off: + ldb _vtattr + bitb #0x80 + bne nocursor + ldx cursor_save + com ,x + com 32,x + com 64,x + com 96,x + com 128,x + com 160,x + com 192,x + com 224,x +nocursor: +_vtattr_notify: + rts +; +; These routines wortk in both 256x192x2 and 128x192x4 modes +; because everything in the X plane is bytewide. +; +_video_write: + clra ; clr C + bra tfr_cmd +_video_read: + coma ; set C + bra tfr_cmd ; go + +;;; This does the job of READ & WRITE +;;; takes: C = direction 0=write, 1=read +;;; takes: X = transfer buffer ptr + 2 +tfr_cmd: + pshs u,y ; save regs + orcc #$10 ; turn off interrupt - int might remap kernel + ldd #$80c0 ; this is writing + bcc c@ ; if carry clear then keep D write + exg a,b ; else flip D: now is reading +c@ sta b@+1 ; !!! self modify inner loop + stb b@+3 ; !!! + bsr vidptr ; U = screen addr + tfr x,y ; Y = ptr to Height, width + leax 4,x ; X = pixel data + ;; outter loop: iterate over pixel rows +a@ lda 3,y ; count = width + pshs u ; save screen ptr + ;; inner loop: iterate over columns + ;; modify mod+1 and mod+3 to switch directions +b@ ldb ,x+ ; get a byte from src + stb ,u+ ; save byte to dest + deca ; bump counter + bne b@ ; loop + ;; increment outer loop + puls u ; restore original screen ptr + leau 32,u ; add byte span of screen (goto next line) + dec 1,y ; bump row counter + bne a@ ; loop + puls u,y,pc ; restore regs, return + + +; +; Find the address we need on a pixel row basis +; +vidptr: + ldu #VIDEO_BASE + ldd ,x++ ; Y into B + lda #32 + mul + leau d,u + ldd ,x++ ; X + leau d,u + rts + +_video_cmd: + pshs u + bsr vidptr ; u now points to the screen +nextline: + pshs u ; save it for the next line +nextop: + ldb ,x+ ; op code, 0 = end of line + beq endline +oploop: + lda ,u ; do one screen byte + anda ,x + eora 1,x + sta ,u+ + decb + bne oploop ; keep going for run + leax 2,x + bra nextop ; next triplet +endline: + puls u ; get position back + leau 32,u ; down one scan line + ldb ,x+ ; get next op - 0,0 means end and done + bne oploop + puls u,pc + + .area .data +cursor_save: + .dw 0 +_vtrow: + .db 0 -- 2.34.1