coco2cart: COCO2 with cartridge
authorAlan Cox <alan@linux.intel.com>
Sat, 5 Nov 2016 12:23:40 +0000 (12:23 +0000)
committerAlan Cox <alan@linux.intel.com>
Sat, 5 Nov 2016 12:23:40 +0000 (12:23 +0000)
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.

29 files changed:
Kernel/platform-coco2cart/Makefile [new file with mode: 0644]
Kernel/platform-coco2cart/README [new file with mode: 0644]
Kernel/platform-coco2cart/bootstrap.s [new file with mode: 0644]
Kernel/platform-coco2cart/carts.h [new file with mode: 0644]
Kernel/platform-coco2cart/coco2.s [new file with mode: 0644]
Kernel/platform-coco2cart/commonmem.s [new file with mode: 0644]
Kernel/platform-coco2cart/config.h [new file with mode: 0644]
Kernel/platform-coco2cart/crt0.s [new file with mode: 0644]
Kernel/platform-coco2cart/devfd.h [new file with mode: 0644]
Kernel/platform-coco2cart/device.h [new file with mode: 0644]
Kernel/platform-coco2cart/devices.c [new file with mode: 0644]
Kernel/platform-coco2cart/devlpr.c [new file with mode: 0644]
Kernel/platform-coco2cart/devlpr.h [new file with mode: 0644]
Kernel/platform-coco2cart/devtty.c [new file with mode: 0644]
Kernel/platform-coco2cart/devtty.h [new file with mode: 0644]
Kernel/platform-coco2cart/discard.c [new file with mode: 0644]
Kernel/platform-coco2cart/drivewire.s [new file with mode: 0644]
Kernel/platform-coco2cart/dwread.s [new file with mode: 0644]
Kernel/platform-coco2cart/dwwrite.s [new file with mode: 0644]
Kernel/platform-coco2cart/ide.s [new file with mode: 0644]
Kernel/platform-coco2cart/libc.c [new file with mode: 0644]
Kernel/platform-coco2cart/main.c [new file with mode: 0644]
Kernel/platform-coco2cart/mini_ide.c [new file with mode: 0644]
Kernel/platform-coco2cart/mini_ide.h [new file with mode: 0644]
Kernel/platform-coco2cart/mini_ide_discard.c [new file with mode: 0644]
Kernel/platform-coco2cart/target.mk [new file with mode: 0644]
Kernel/platform-coco2cart/tricks.s [new file with mode: 0644]
Kernel/platform-coco2cart/usermem.s [new file with mode: 0644]
Kernel/platform-coco2cart/video.s [new file with mode: 0644]

diff --git a/Kernel/platform-coco2cart/Makefile b/Kernel/platform-coco2cart/Makefile
new file mode 100644 (file)
index 0000000..361874b
--- /dev/null
@@ -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 (file)
index 0000000..eb80b6f
--- /dev/null
@@ -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 (file)
index 0000000..aaa76a8
--- /dev/null
@@ -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 (file)
index 0000000..86d4941
--- /dev/null
@@ -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 (file)
index 0000000..19b3632
--- /dev/null
@@ -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 (file)
index 0000000..b820abb
--- /dev/null
@@ -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 (file)
index 0000000..36d52b0
--- /dev/null
@@ -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 (file)
index 0000000..83b4e36
--- /dev/null
@@ -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 (file)
index 0000000..b2754e6
--- /dev/null
@@ -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 (file)
index 0000000..e57c6a3
--- /dev/null
@@ -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 (file)
index 0000000..5c3689b
--- /dev/null
@@ -0,0 +1,49 @@
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devfd.h>
+#include <devdw.h>
+#include <devsys.h>
+#include <devlpr.h>
+#include <tty.h>
+#include <vt.h>
+#include <devtty.h>
+#include <mini_ide.h>
+#include <device.h>
+
+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 (file)
index 0000000..9ddfae4
--- /dev/null
@@ -0,0 +1,70 @@
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <device.h>
+#include <devlpr.h>
+
+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 (file)
index 0000000..0e078ca
--- /dev/null
@@ -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 (file)
index 0000000..1c7fc8b
--- /dev/null
@@ -0,0 +1,240 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <devtty.h>
+#include <device.h>
+#include <devfd.h>
+#include <vt.h>
+#include <tty.h>
+#include <graphics.h>
+
+#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 (file)
index 0000000..2d6f873
--- /dev/null
@@ -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 (file)
index 0000000..1791769
--- /dev/null
@@ -0,0 +1,73 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <device.h>
+#include <devtty.h>
+#include <carts.h>
+#include <blkdev.h>
+
+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 (file)
index 0000000..85962fe
--- /dev/null
@@ -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 (file)
index 0000000..1b4c587
--- /dev/null
@@ -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      <BBIN               ; check for start bit
+          beq       rxByte              ; branch if start bit detected
+          subb      #1                  ; decrement timeout lsb
+          bita      <BBIN
+          beq       rxByte
+          bcc       rx0020              ; loop until timeout lsb rolls under
+          bita      <BBIN
+          beq       rxByte
+          addb      ,s                  ; B = timeout msb - 1
+          bita      <BBIN
+          beq       rxByte
+          stb       ,s                  ; store decremented timeout msb
+          bita      <BBIN
+          bne       rx0010              ; loop if still no start bit
+
+* Read a byte
+rxByte    leay      ,-y                 ; decrement request count
+          ldd       #$ff80              ; A = timeout msb, B = shift counter
+          sta       ,s                  ; reset timeout msb for next byte
+rx0030    exg       a,a
+          nop
+          lda       <BBIN               ; read data bit
+          lsra                          ; shift into carry
+          rorb                          ; rotate into byte accumulator
+          lda       #$01                ; prep stop bit mask
+          bcc       rx0030              ; loop until all 8 bits read
+
+          stb       ,u+                 ; store received byte to memory
+          abx                           ; update checksum
+          ldb       #$ff                ; set timeout lsb for next byte
+          anda      <BBIN               ; read stop bit
+          beq       rxExit              ; exit if framing error
+          leay      ,y                  ; test request count
+          bne       rx0020              ; loop if another byte wanted
+          lda       #$03                ; setup to return SUCCESS
+
+* Clean up, set status and return
+rxExit    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
+          setdp     $00
+
+
+          ELSE
+          IFNE H6309
+*******************************************************
+* 57600 (115200) bps using 6309 native mode
+*******************************************************
+
+DWRead    clrb                          ; clear Carry (no framing error)
+          decb                          ; clear Z flag, B = $FF
+          pshs      u,x,dp,cc           ; preserve registers
+          IFEQ      NOINTMASK
+          orcc      #IntMasks           ; mask interrupts
+          ENDC
+*         ldmd      #1                  ; requires 6309 native mode
+          tfr       b,dp                ; set direct page to $FFxx
+          setdp     $ff
+          leay      -1,y                ; adjust request count
+          leau      ,x                  ; U = storage ptr
+          tfr       0,x                 ; initialize checksum
+          lda       #$01                ; A = serial in mask
+          bra       rx0030              ; go wait for start bit
+
+* Read a byte
+rxByte    sexw                          ; 4 cycle delay
+          ldw       #$006a              ; shift counter and timing flags
+          clra                          ; clear carry so next will branch
+rx0010    bcc       rx0020              ; branch if even bit number (15 cycles)
+          nop                           ; extra (16th) cycle
+rx0020    lda       <BBIN               ; read bit
+          lsra                          ; move bit into carry
+          rorb                          ; rotate bit into byte accumulator
+          lda       #0                  ; prep A for 8th data bit
+          lsrw                          ; bump shift count, timing bit to carry
+          bne       rx0010              ; loop until 7th data bit has been read
+          incw                          ; W = 1 for subtraction from Y
+          inca                          ; A = 1 for reading bit 7
+          anda      <BBIN               ; read bit 7
+          lsra                          ; move bit 7 into carry, A = 0
+          rorb                          ; byte is now complete
+          stb       ,u+                 ; store received byte to memory
+          abx                           ; update checksum
+          subr      w,y                 ; decrement request count
+          inca                          ; A = 1 for reading stop bit
+          anda      <BBIN               ; read stop bit
+          bls       rxExit              ; exit if completed or framing error
+
+* Wait for a start bit or timeout
+rx0030    clrw                          ; initialize timeout counter
+rx0040    bita      <BBIN               ; check for start bit
+          beq       rxByte              ; branch if start bit detected
+          addw      #1                  ; bump timeout counter
+          bita      <BBIN
+          beq       rxByte
+          bcc       rx0040              ; loop until timeout rolls over
+          lda       #$03                ; setup to return TIMEOUT status
+
+* Clean up, set status and return
+rxExit    beq       rx0050              ; branch if framing error
+          eora      #$02                ; toggle SUCCESS flag
+rx0050    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
+          setdp     $00
+
+
+          ELSE
+*******************************************************
+* 57600 (115200) 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
+          lda       #$01                ; A = serial in mask
+          bra       rx0030              ; go wait for start bit
+
+* Read a byte
+rxByte    leau      1,u                 ; bump storage ptr
+          leay      ,-y                 ; decrement request count
+          lda       <BBIN               ; read bit 0
+          lsra                          ; move bit 0 into Carry
+          ldd       #$ff20              ; A = timeout msb, B = shift counter
+          sta       ,s                  ; reset timeout msb for next byte
+          rorb                          ; rotate bit 0 into byte accumulator
+rx0010    lda       <BBIN               ; read bit (d1, d3, d5)
+          lsra
+          rorb
+          bita      1,s                 ; 5 cycle delay
+          bcs       rx0020              ; exit loop after reading bit 5
+          lda       <BBIN               ; read bit (d2, d4)
+          lsra
+          rorb
+          leau      ,u
+          bra       rx0010
+
+rx0020    lda       <BBIN               ; read bit 6
+          lsra
+          rorb
+          leay      ,y                  ; test request count
+          beq       rx0050              ; branch if final byte of request
+          lda       <BBIN               ; read bit 7
+          lsra
+          rorb                          ; byte is now complete
+          stb       -1,u                ; store received byte to memory
+          abx                           ; update checksum
+          lda       <BBIN               ; read stop bit
+          anda      #$01                ; mask out other bits
+          beq       rxExit              ; exit if framing error
+
+* Wait for a start bit or timeout
+rx0030    bita      <BBIN               ; check for start bit
+          beq       rxByte              ; branch if start bit detected
+          bita      <BBIN               ; again
+          beq       rxByte
+          ldb       #$ff                ; init timeout lsb
+rx0040    bita      <BBIN
+          beq       rxByte
+          subb      #1                  ; decrement timeout lsb
+          bita      <BBIN
+          beq       rxByte
+          bcc       rx0040              ; loop until timeout lsb rolls under
+          bita      <BBIN
+          beq       rxByte
+          addb      ,s                  ; B = timeout msb - 1
+          bita      <BBIN
+          beq       rxByte
+          stb       ,s                  ; store decremented timeout msb
+          bita      <BBIN
+          beq       rxByte
+          bcs       rx0030              ; loop if timeout hasn't expired
+          bra       rxExit              ; exit due to timeout
+
+rx0050    lda       <BBIN               ; read bit 7 of final byte
+          lsra
+          rorb                          ; byte is now complete
+          stb       -1,u                ; store received byte to memory
+          abx                           ; calculate final checksum
+          lda       <BBIN               ; read stop bit
+          anda      #$01                ; mask out other bits
+          ora       #$02                ; return SUCCESS if no framing error
+
+* Clean up, set status and return
+rxExit    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
+          ;setdp     $00
+
+          ENDC
+          ENDC
+          ENDC
+
diff --git a/Kernel/platform-coco2cart/dwwrite.s b/Kernel/platform-coco2cart/dwwrite.s
new file mode 100644 (file)
index 0000000..d262968
--- /dev/null
@@ -0,0 +1,196 @@
+*******************************************************
+*
+* Copied from HDB-DOS from toolshed.sf.net
+* The original code is public domain
+*
+* DWWrite
+*    Send a packet to the DriveWire server.
+*    Serial data format:  1-8-N-1
+*    4/12/2009 by Darren Atkinson
+*
+* Entry:
+*    X  = starting address of data to send
+*    Y  = number of bytes to send
+*
+* Exit:
+*    X  = address of last byte sent + 1
+*    Y  = 0
+*    All others preserved
+*
+
+
+          IFNE ARDUINO
+DWWrite   pshs      a                  ; preserve registers
+txByte
+          lda       ,x+                ; get byte from buffer
+          sta       $FF52              ; put it to PIA
+loop@     tst       $FF53              ; check status register
+          bpl       loop@              ; until CB1 is set by Arduino, continue looping
+          tst       $FF52              ; clear CB1 in status register
+          leay      -1,y                ; decrement byte counter
+          bne       txByte              ; loop if more to send
+
+          puls      a,pc                ; restore registers and return
+
+          ELSE
+
+          IFNE JMCPBCK
+DWWrite   pshs      d,cc              ; preserve registers
+          IFEQ      NOINTMASK
+          orcc      #IntMasks           ; mask interrupts
+          ENDC
+txByte
+          lda       ,x+
+          sta       $FF44
+          leay      -1,y                ; decrement byte counter
+          bne       txByte              ; loop if more to send
+
+          puls      cc,d,pc           ; restore registers and return
+
+          ELSE
+          IFNE BECKER
+          IFNDEF BCKPORT
+BCKPORT   equ   $FF42
+          ENDC
+DWWrite   pshs      d,cc              ; preserve registers
+          IFEQ      NOINTMASK
+          orcc      #IntMasks           ; mask interrupts
+          ENDC
+;          ldu       #BBOUT              ; point U to bit banger out register
+;          lda       3,u                 ; read PIA 1-B control register
+;          anda      #$f7                ; clear sound enable bit
+;          sta       3,u                 ; disable sound output
+;          fcb       $8c                 ; skip next instruction
+
+txByte
+          lda       ,x+
+          sta       BCKPORT
+          leay      -1,y                ; decrement byte counter
+          bne       txByte              ; loop if more to send
+
+          puls      cc,d,pc           ; restore registers and return
+          ENDC
+          ENDC
+          ENDC
+
+          IFEQ BECKER+JMCPBCK+ARDUINO
+          IFNE BAUD38400
+*******************************************************
+* 38400 bps using 6809 code and timimg
+*******************************************************
+
+DWWrite   pshs      u,d,cc              ; preserve registers
+          IFEQ      NOINTMASK
+          orcc      #IntMasks           ; mask interrupts
+          ENDC
+          ldu       #BBOUT              ; point U to bit banger out register
+          lda       3,u                 ; read PIA 1-B control register
+          anda      #$f7                ; clear sound enable bit
+          sta       3,u                 ; disable sound output
+          fcb       $8c                 ; skip next instruction
+
+txByte    stb       ,--u                ; send stop bit
+          leau      ,u+
+          lda       #8                  ; counter for start bit and 7 data bits
+          ldb       ,x+                 ; get a byte to transmit
+          lslb                          ; left rotate the byte two positions..
+          rolb                          ; ..placing a zero (start bit) in bit 1
+tx0010    stb       ,u++                ; send bit
+          tst       ,--u
+          rorb                          ; move next bit into position
+          deca                          ; decrement loop counter
+          bne       tx0010              ; loop until 7th data bit has been sent
+          leau      ,u
+          stb       ,u                  ; send bit 7
+          lda       ,u++
+          ldb       #$02                ; value for stop bit (MARK)
+          leay      -1,y                ; decrement byte counter
+          bne       txByte              ; loop if more to send
+
+          stb       ,--u                ; leave bit banger output at MARK
+          puls      cc,d,u,pc           ; restore registers and return
+
+          ELSE
+
+          IFNE H6309
+*******************************************************
+* 57600 (115200) bps using 6309 native mode
+*******************************************************
+
+DWWrite   pshs      u,d,cc              ; preserve registers
+          IFEQ      NOINTMASK
+          orcc      #IntMasks           ; mask interrupts
+          ENDC
+*         ldmd      #1                  ; requires 6309 native mode
+          ldu       #BBOUT+1            ; point U to bit banger out register +1
+          aim       #$f7,2,u            ; disable sound output
+          lda       #8                  ; counter for start bit and 7 data bits
+          fcb       $8c                 ; skip next instruction
+
+txByte    stb       -1,u                ; send stop bit
+tx0010    ldb       ,x+                 ; get a byte to transmit
+          lslb                          ; left rotate the byte two positions..
+          rolb                          ; ..placing a zero (start bit) in bit 1
+          bra       tx0030
+
+tx0020    bita      #1                  ; even or odd bit number ?
+          beq       tx0040              ; branch if even (15 cycles)
+tx0030    nop                           ; extra (16th) cycle
+tx0040    stb       -1,u                ; send bit
+          rorb                          ; move next bit into position
+          deca                          ; decrement loop counter
+          bne       tx0020              ; loop until 7th data bit has been sent
+          leau      ,u+
+          stb       -1,u                ; send bit 7
+          ldd       #$0802              ; A = loop counter, B = MARK value
+          leay      -1,y                ; decrement byte counter
+          bne       txByte              ; loop if more to send
+
+          stb       -1,u                ; final stop bit
+          puls      cc,d,u,pc           ; restore registers and return
+
+          ELSE
+*******************************************************
+* 57600 (115200) bps using 6809 code and timimg
+*******************************************************
+
+DWWrite   pshs      dp,d,cc             ; preserve registers
+          IFEQ      NOINTMASK
+          orcc      #IntMasks           ; mask interrupts
+          ENDC
+          ldd       #$04ff              ; A = loop counter, B = $ff
+          tfr       b,dp                ; set direct page to $FFxx
+          ;setdp     $ff
+          ldb       <$ff23              ; read PIA 1-B control register
+          andb      #$f7                ; clear sound enable bit
+          stb       <$ff23              ; disable sound output
+          fcb       $8c                 ; skip next instruction
+
+txByte    stb       <BBOUT              ; send stop bit
+          ldb       ,x+                 ; get a byte to transmit
+          nop
+          lslb                          ; left rotate the byte two positions..
+          rolb                          ; ..placing a zero (start bit) in bit 1
+tx0020    stb       <BBOUT              ; send bit (start bit, d1, d3, d5)
+          rorb                          ; move next bit into position
+          exg       a,a
+          nop
+          stb       <BBOUT              ; send bit (d0, d2, d4, d6)
+          rorb                          ; move next bit into position
+          leau      ,u
+          deca                          ; decrement loop counter
+          bne       tx0020              ; loop until 7th data bit has been sent
+
+          stb       <BBOUT              ; send bit 7
+          ldd       #$0402              ; A = loop counter, B = MARK value
+          leay      ,-y                 ; decrement byte counter
+          bne       txByte              ; loop if more to send
+
+          stb       <BBOUT              ; leave bit banger output at MARK
+          puls      cc,d,dp,pc          ; restore registers and return
+          ;setdp     $00
+
+          ENDC
+          ENDC
+          ENDC
+
diff --git a/Kernel/platform-coco2cart/ide.s b/Kernel/platform-coco2cart/ide.s
new file mode 100644 (file)
index 0000000..8a5d8cb
--- /dev/null
@@ -0,0 +1,62 @@
+;
+;      Glenside style IDE block transfer logic
+;
+
+       .module dragonide
+
+       .globl _devide_read_data
+       .globl _devide_write_data
+
+       .globl _blk_op
+
+        include "kernel.def"
+        include "../kernel09.def"
+
+       .area .common
+
+;
+;      Standard mapping for Glenside style IDE
+;
+;      We don't really support swap properly but what we do is sufficient
+;      for a simple memory mapping.
+;
+_devide_read_data:
+       pshs y,dp
+       lda #0xFF
+       tfr a,dp
+       ldx _blk_op
+       leay 512,x
+       sty endp
+       tst _blk_op+2
+       beq readword
+       jsr map_process_always
+readword:
+       lda <IDEDATA
+       ldb <IDEDATA_L          ; latched
+       std ,x++
+       cmpx endp
+       bne readword
+       jsr map_kernel
+       puls y,dp,pc
+
+_devide_write_data:
+       pshs y,dp
+       lda #0xFF
+       tfr a,dp
+       ldx _blk_op
+       leay 512,x
+       sty endp
+       tst _blk_op+2
+       beq writeword
+       jsr map_process_always
+writeword:
+       ldd ,x++
+       stb <IDEDATA_L
+       sta <IDEDATA
+       cmpx endp
+       bne writeword
+       jsr map_kernel
+       puls y,dp,pc
+
+endp:  .dw 0
+
diff --git a/Kernel/platform-coco2cart/libc.c b/Kernel/platform-coco2cart/libc.c
new file mode 100644 (file)
index 0000000..a4cb9dd
--- /dev/null
@@ -0,0 +1,25 @@
+#include "cpu.h"
+
+void *memcpy(void *d, const void *s, size_t sz)
+{
+  unsigned char *dp = d;
+  const unsigned char *sp = s;
+  while(sz--)
+    *dp++=*sp++;
+  return d;
+}
+
+void *memset(void *d, int c, size_t sz)
+{
+  unsigned char *p = d;
+  while(sz--)
+    *p++ = c;
+  return d;
+}
+
+size_t strlen(const char *p)
+{
+  const char *e = p;
+  while(*e++);
+  return e-p-1;
+}
diff --git a/Kernel/platform-coco2cart/main.c b/Kernel/platform-coco2cart/main.c
new file mode 100644 (file)
index 0000000..293b217
--- /dev/null
@@ -0,0 +1,51 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <device.h>
+#include <devtty.h>
+#include <carts.h>
+#include <blkdev.h>
+
+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 (file)
index 0000000..bbb0c93
--- /dev/null
@@ -0,0 +1,100 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#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 (file)
index 0000000..a72b491
--- /dev/null
@@ -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 (file)
index 0000000..49eff53
--- /dev/null
@@ -0,0 +1,97 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <timer.h>
+#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 (file)
index 0000000..e9abbba
--- /dev/null
@@ -0,0 +1 @@
+export CPU = 6809
diff --git a/Kernel/platform-coco2cart/tricks.s b/Kernel/platform-coco2cart/tricks.s
new file mode 100644 (file)
index 0000000..bb43618
--- /dev/null
@@ -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 (file)
index 0000000..cb32f39
--- /dev/null
@@ -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 (file)
index 0000000..ab65cb3
--- /dev/null
@@ -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