Introduce new 6809 platform, multicomp09.
authorNeal Andrew Crook <neal@pippaluk.org.uk>
Wed, 18 May 2016 19:43:00 +0000 (20:43 +0100)
committerNeal Andrew Crook <neal@pippaluk.org.uk>
Wed, 18 May 2016 19:43:00 +0000 (20:43 +0100)
33 files changed:
Applications/util/multicomp09.link [new file with mode: 0644]
Kernel/Makefile
Kernel/PORTING
Kernel/dev/mbr.c
Kernel/platform-multicomp09/Makefile [new file with mode: 0644]
Kernel/platform-multicomp09/README [new file with mode: 0644]
Kernel/platform-multicomp09/boot/boot.s [new file with mode: 0644]
Kernel/platform-multicomp09/build [new file with mode: 0755]
Kernel/platform-multicomp09/commonmem.s [new file with mode: 0644]
Kernel/platform-multicomp09/config.h [new file with mode: 0644]
Kernel/platform-multicomp09/crt0.s [new file with mode: 0644]
Kernel/platform-multicomp09/device.h [new file with mode: 0644]
Kernel/platform-multicomp09/devices.c [new file with mode: 0644]
Kernel/platform-multicomp09/devsdc.c [new file with mode: 0644]
Kernel/platform-multicomp09/devsdc.h [new file with mode: 0644]
Kernel/platform-multicomp09/devtty.c [new file with mode: 0644]
Kernel/platform-multicomp09/devtty.h [new file with mode: 0644]
Kernel/platform-multicomp09/drivewire.s [new file with mode: 0644]
Kernel/platform-multicomp09/dw.def [new file with mode: 0644]
Kernel/platform-multicomp09/dwread.s [new file with mode: 0644]
Kernel/platform-multicomp09/dwwrite.s [new file with mode: 0644]
Kernel/platform-multicomp09/fuzix.link [new file with mode: 0644]
Kernel/platform-multicomp09/kernel.def [new file with mode: 0644]
Kernel/platform-multicomp09/libc.c [new file with mode: 0644]
Kernel/platform-multicomp09/main.c [new file with mode: 0644]
Kernel/platform-multicomp09/multicomp09.s [new file with mode: 0644]
Kernel/platform-multicomp09/sdc.s [new file with mode: 0644]
Kernel/platform-multicomp09/target.mk [new file with mode: 0644]
Kernel/platform-multicomp09/tricks.s [new file with mode: 0644]
Kernel/platform-multicomp09/ttydw.c [new file with mode: 0644]
Kernel/platform-multicomp09/ttydw.h [new file with mode: 0644]
Kernel/platform-multicomp09/usermem_gime.s [new file with mode: 0644]
Kernel/platform-multicomp09/videoll.s [new file with mode: 0644]

diff --git a/Applications/util/multicomp09.link b/Applications/util/multicomp09.link
new file mode 100644 (file)
index 0000000..1174016
--- /dev/null
@@ -0,0 +1,9 @@
+define basesympat __sectionbase_%s__
+define lensympat __sectionlen_%s__
+section .header load 0x0100
+section .text
+section .text.startup
+section .text.hot
+section .test.unlikely
+section .data
+section .bss
index 3974dcd..152a65f 100644 (file)
@@ -1,4 +1,4 @@
-TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-z80pack32 platform-dragon platform-tgl6502 platform-plus3 platform-zeta-v2 platform-coco3
+TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-z80pack32 platform-dragon platform-tgl6502 platform-plus3 platform-zeta-v2 platform-coco3 platform-multicomp09
 
 #export TARGET = 6809simple
 #export TARGET = 68hc11test
@@ -9,6 +9,7 @@ TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80
 #export TARGET = coco2
 #export TARGET = coco3
 #export TARGET = dragon-nx32
+#export TARGET = multicomp09
 #export TARGET = micropack
 #export TARGET = msx1
 #export TARGET = msx2
index dc5d421..90154d0 100644 (file)
@@ -3,7 +3,7 @@ Minimal Requirements
 
 - 6809 or Z80 processor
 - 128K memory for Z80 (the kernel itself wants between 32 and 40K R/O and about
-  8-16K R/W depending upon the configuration. For 6809 the kernel compiles
+  8-16K R/W depending upon the configuration). For 6809 the kernel compiles
   to about 24K so a single bank 64K machine just about works.
 - Some kind of memory banking giving a common area and multiple banks
 - A storage device, preferably hard disk or SD card or similar
@@ -26,6 +26,8 @@ to do the work) it should also be possible to run with
 
 Porting Fuzix to a new Z80 based machine generally requires the following
 
+- Pick an appropriate Kernel/platform-TARGET to clone.
+
 - Pick an output device that is easy to drive (serial or video) and write a
   mininal test output driver for it standalone. Serial is usually best if
   you don't have a character mapped console
@@ -33,7 +35,10 @@ Porting Fuzix to a new Z80 based machine generally requires the following
 - Extend that into a suitable boot loader, unless loading from existing
   firmware (see tips below for CP/M booting)
 
-- Select the appropriate model
+- A lot of standard code is available to make porting easy
+
+- Select the appropriate options (in platform-TARGET/config.h and in
+  platform-TARGET/kernel.def)
 
   CONFIG_SINGLETASK
   - single tasking (no fast swap device, limited memory). Processes are
@@ -50,9 +55,7 @@ Porting Fuzix to a new Z80 based machine generally requires the following
   - swap device number if you have a suitable device for swap. This
     is usually best done after it boots ok
 
-- Pick an appropriate platform to clone.
-
-- A lot of standard code is available to make porting easy
+ CONFIG_SWAP_ONLY selects NO user banking
 
  CONFIG_BANK16 selects support for four flexible 16K bank registers. The non
  common kernel & data parts must all fit below 0xC000, common goes higher.
@@ -96,12 +99,20 @@ Porting Fuzix to a new Z80 based machine generally requires the following
  so it  can be moved into another segment (eg to be with the video screen data).
  See pcw8256 for an example of this.
 
+ CONFIG_MBR_OFFSET tells dev/mbr.c where to find the MBR. FUZIX knows how to
+ parse a standard DOS master boot record and extract partition table entries
+ to locate and mount drives. A well-behaved disk will have its MBR at block 0.
+ If you need to put your MBR elsewhere, use this.
+
 - Set the basic system parameters
 
  TICKSPERSEC   -       clock rate
- PROGBASE      -       low memory address fo applications (should be 0x100
+ PROGBASE      -       low memory address for applications (should be 0x100
                        to run standard binaries). Some bits of start.c
                        still break if this is not 0x0100
+ PROGLOAD      -       ?? what is the distinction PROGLOAD vs PROGBASE?
+                        In most cases PROGBASE is 0 and PROGLOAD is 0x100.
+                        In all cases, PROGBASE<=PROGLOAD.
  PROGTOP       -       first byte above main memory, usually the start of
                        the udata area and common memory. Typically above
                        0xF000 but if your system has a fixed upper common
@@ -109,9 +120,9 @@ Porting Fuzix to a new Z80 based machine generally requires the following
  BOOT_TTY      -       the device you will use as your 'console' at boot
                        time. 513 is the first tty, 514 the second etc
 
- CMDLINE       -       pointer to a command line passed from the
-                       loader/firmware. If booting from CP/M (see below) use
-                       0x88. Optional.
+ CMDLINE       -       pointer to a null-terminated command line passed
+                       from the loader/firmware. If booting from CP/M (see
+                       below) use 0x88. Optional: set to NULL if none.
 
  NUM_DEV_TTY   -       number of tty devices (video consoles/serial)
 
@@ -127,7 +138,7 @@ If you have non interrupt driven serial ports then poll them in your IRQ
 handler and provide a platform_idle which also polls the ports (makes it
 feel much snappier).
 
-Provide a putchar() that writes to your console correctly either via direct
+Provide a kputchar() that writes to your console correctly either via direct
 debug code or later via the tty helper. Provide a similar (register
 preserving) assembler hook in outchar. That gives you a usable kprintf in C
 and outhl/outde/outcharhex/.. series of methods in assembler for debugging.
@@ -154,7 +165,7 @@ MAX_MAPS is the max number of pages (or banks whatever you have) that you
 can have. Set it to the largest you might need and load those present if
 your system comes in different memory sizes.
 
-If you are using swap then
+If you are using swap then:
 
 SWAPBASE       -       lowest address that will be swapped
 SWAPTOP                -       highest address that will be swapped
@@ -268,7 +279,7 @@ _cursor_off -       remove the cursor
 C interfaces
 ------------
 
-Devices.c
+devices.c
 
 dev_tab is the device table for your platform indicating what is present.
 Usually best taken from another platform and hacked up
@@ -283,16 +294,16 @@ void device_init(void)
 Called early on before init runs so devices can do initialization before
 they are opened. May well be blank
 
-Devtty.c
+devtty.c
 
 Define a TTYSIZE buffer for each device present
 Copy the struct s_queue from another filled in for your devices/buffers.
 
-Provide putchar(c) if it is not already provided by your asm code
+Provide kputchar(c) if it is not already provided by your asm code
 
 bool tty_writeready(uint8_t minor)
 
-Returns true fi you can write bytes to this port (1..n). For things like
+Returns true if you can write bytes to this port (1..n). For things like
 video consoles just return true
 
 void tty_putc(uint8_t minor, char c)
@@ -324,7 +335,7 @@ them here. Either way call tty_inproc(minor, c) with the character to queue
 it for input. This may call back to your tty_putc.
 
 
-Main.c
+main.c
 
 uint8_t *swapout_prepare_uarea(ptptr p)
 uint8_t *swapin_prepare_uarea(ptptr p)
@@ -410,7 +421,7 @@ The conventional mapping of block numbers is to start at track 0, side 0,
 sector 1 as block 0, and then follow the usual upward pattern. The OS
 reserves block 0 as a boot block, and puts the superblock at block 1.
 
-If you have a disc with a loadable kernel on it then put the kernel on the
+If you have a disc with a loadable kernel on it then put the kernel at the
 *end* of the disc. This means you get a smaller file system on the media but
 also means you don't to special case 'boot tracks' or waste any of a data
 disc.
@@ -458,7 +469,7 @@ z80pack up with your platform configuration. See the dragon-nx32 for an
 additional optimisation.
 
 You may have to decide between limited process size or being prepared to do
-copying tricks to copy big chunks of memory around. 
+copying tricks to copy big chunks of memory around.
 
 If you have only a single user bank however it's easy - see z80pack-lite 8)
 
index 0a42181..5c36cd1 100644 (file)
@@ -4,6 +4,7 @@
 #include <kdata.h>
 #include <printf.h>
 #include <blkdev.h>
+#include <config.h>
 
 typedef struct {
     uint8_t  status;
@@ -37,8 +38,11 @@ void mbr_parse(char letter)
     blk_op.is_read = true;
     blk_op.is_user = false;
     blk_op.addr = (uint8_t *)br;
+#ifdef CONFIG_MBR_OFFSET
+    blk_op.lba = CONFIG_MBR_OFFSET;
+#else
     blk_op.lba = 0;
-
+#endif
     do{
         blk_op.nblock = 1;
         if(!blk_op.blkdev->transfer() || le16_to_cpu(br->signature) != MBR_SIGNATURE)
@@ -48,7 +52,7 @@ void mbr_parse(char letter)
        if(seen >= 50)
            break;
 
-       if(seen == 1){ 
+       if(seen == 1){
            /* we just loaded the first extended boot record */
            ep_offset = blk_op.lba;
            next = 4;
@@ -56,7 +60,7 @@ void mbr_parse(char letter)
        }
 
        br_offset = blk_op.lba;
-       blk_op.lba = 0;
+        blk_op.lba = 0;
 
        for(i=0; i<MBR_ENTRY_COUNT && next < MAX_PARTITIONS; i++){
            switch(br->partition[i].type){
@@ -71,7 +75,7 @@ void mbr_parse(char letter)
                    blk_op.lba = ep_offset + le32_to_cpu(br->partition[i].lba_first);
                    if(next >= 4)
                        break;
-                   /* we include all primary partitions but we deliberately knobble the size in 
+                   /* we include all primary partitions but we deliberately knobble the size in
                       order to prevent catastrophic accidents */
                    br->partition[i].lba_count = cpu_to_le32(2L);
                    /* fall through */
diff --git a/Kernel/platform-multicomp09/Makefile b/Kernel/platform-multicomp09/Makefile
new file mode 100644 (file)
index 0000000..78d4995
--- /dev/null
@@ -0,0 +1,60 @@
+
+CSRCS = ttydw.c
+CSRCS += devices.c main.c libc.c devsdc.c
+
+CDSRCS = 
+
+DSRCS = ../dev/devdw.c ../dev/blkdev.c ../dev/mbr.c
+
+ASRCS = multicomp09.s crt0.s
+ASRCS += tricks.s commonmem.s usermem_gime.s drivewire.s sdc.s videoll.s
+
+COBJS = $(CSRCS:.c=$(BINEXT))
+AOBJS = $(ASRCS:.s=$(BINEXT))
+DOBJS = devdw.o blkdev.o mbr.o
+CDOBJS = $(CDSRCS:.c=$(BINEXT))
+OBJS  = $(COBJS) $(AOBJS) $(DOBJS) $(CDOBJS)
+
+# to save intermediate files (eg, assembler) add -save-temps=cwd
+# and do the same thing in cpu-6809/rules.mk
+CROSS_CCOPTS += -I../dev/
+
+JUNK = $(CSRCS:.c=.o) $(ASRCS:.s=.o) $(DSRCS:.c=.o)
+
+all:   $(OBJS)
+
+$(COBJS): %$(BINEXT): %.c
+       $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(CDOBJS): %$(BINEXT): %.c
+       $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+$(DOBJS): %$(BINEXT): ../dev/%.c
+       $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(AOBJS): %$(BINEXT): %.s
+       $(CROSS_AS) $(ASOPTS) $< -o $*.o
+
+
+clean:
+       rm -f $(OBJS) $(JUNK)  core *~
+
+image:
+       $(CROSS_CC) $(CROSS_CCOPTS) -O0 -c -o ../bank16k.o ../bank16k.c
+       $(CROSS_CC) $(CROSS_CCOPTS) -O0 -c -o devtty.o devtty.c
+       $(CROSS_CC) $(CROSS_CC_VIDEO) $(CROSS_CCOPTS) -O0 -c -o ../usermem.o ../usermem.c
+       $(CROSS_LD) -o ../fuzix.bin --map=../fuzix.map --script=fuzix.link \
+       crt0.o commonmem.o \
+       multicomp09.o ../start.o ../version.o ../lowlevel-6809.o \
+       tricks.o main.o ../timer.o ../kdata.o devices.o \
+       drivewire.o devdw.o ttydw.o blkdev.o mbr.o \
+       devsdc.o sdc.o \
+       ../devio.o ../filesys.o ../process.o ../inode.o ../syscall_fs.o \
+       ../syscall_proc.o ../syscall_other.o ../mm.o ../bank16k.o ../swap.o \
+       ../tty.o ../devsys.o ../usermem.o ../syscall_fs2.o ../syscall_exec16.o \
+       ../syscall_fs3.o \
+       ../usermem_std-6809.o devtty.o libc.o ../vt.o usermem_gime.o \
+       videoll.o
+
+boot.bin: boot/boot.s
+       lwasm -lboot.list --format=raw -oboot.bin boot/boot.s
diff --git a/Kernel/platform-multicomp09/README b/Kernel/platform-multicomp09/README
new file mode 100644 (file)
index 0000000..67a7442
--- /dev/null
@@ -0,0 +1,231 @@
+Fuzix for 6809 Multicomp
+
+Copyright 2016, Neal Andrew Crook, under GPL2.
+Derived from Coco3 port which is
+Copyright 2015, Brett M. Gordon, under GPL2.
+
+This port is for running FUZIX on an FPGA computer called "Multicomp"
+which was originally designed by Grant Searle. This incarnation of
+Multicomp uses a 6809 processor, thus the platform name "multicomp09".
+
+In its current form, somwhat evolved from Grant's design, multicomp09
+provides the following:
+
+- 6809 running at about 12MHz
+- boot from ROM but ROM can then be switched out of the memory map
+- ROM is CamelForth, with hooks to boot multiple other OS from SD:
+  - Microsoft BASIC, FLEX, CUBIX, Buggy, NitrOS9, FUZIX
+- RAM from 0 to 0xffff except for:
+  - I/O space from 0xffd0-0xffdf
+- 50Hz Timer interrupt
+- Hardware single-step
+- SD controller in hw state-machines
+- Virtual 6850-like UART interfacing to an 80x25 colour ANSI terminal
+  on a VGA display with PS/2 keyboard input
+- Upto 2 6850-like serial UARTs with RS232 or virtual-RS232/USB interfaces
+- coco-style memory mapping unit (2 contexts, 8K banks)
+  - upto 1MByte physical RAM
+- RTC through DS1302 bit-banged on GPIO port.
+
+More details, including PCBs, software, source code:
+
+http://searle.hostei.com/grant/Multicomp/index.html
+https://github.com/nealcrook/multicomp6809 (and visit the WIKI there)
+https://www.retrobrewcomputers.org/doku.php?id=boards:sbc:multicomp:cycloneii-c:start
+
+This port is based heavily on the coco3 port which itself is based on
+the platform-6809test and the two dragon ports by Tormod Volden.
+No disk swapping is supported
+
+*************************
+REQUIREMENTS
+*************************
+
+A 128KByte or more multicomp09 board
+
+Limited debug can be performed using exec09
+
+and for building:
+
+lwtools from William Astle: http://lwtools.projects.l-w.ca/
+gcc6809: http://toolshed.sourceforge.net/gcc/
+
+
+*************************
+BOOTING
+*************************
+
+The build process (see BUILDING below) produces the following files:
+
+fuzix.bin    -- the kernel, in DECB object file format
+fuzixfs.dsk  -- an image of the root file system in fuzix disk format.
+boot.bin     -- a bootstrap loader, in raw binary format.
+
+The kernel must be loaded and started through a platform-dependent process. When
+the kernel starts up it locates and mounts the root disk. The root disk contains
+the /dev tree as well as essential binaries like init.
+
+The DECB object file format can contain multiple sections, with different load
+addresses, and a start address. Therefore, it can represent a sparse binary.
+
+The multicomp SDcard holds multiple OS/disk images so the FUZIX stuff cannot
+start at block 0. As well as a boot loader, need to allocate space for
+the kernel, a swap file, a root disk and a second disk.
+
+The boot loader starts at block 0x3.0000 on the SDcard
+The kernel      starts at block 0x3.0400 on the SDcard
+The rootfs      starts at block 0x3.1000 on the SDcard
+Space is assigned for a 1MByte swap file at 0x3.0800
+Space is assigned for a 2nd file system  at 0x4.1000
+
+The location of these files is hard-wired as follows:
+
+- All of them are hard-wired in the script that creates the SDcard image.
+- The location of the boot-loader is hard-wired in the CamelForth word FUZIX.
+- The location of the boot-loaded is hard-wired in mbr.c through the definition
+  CONFIG_MBR_OFFSET in config.h for this platform.
+- The location of the kernel is hard-wired in the code section of the boot loader.
+- The location of the rootfs and additional file system is hard-wired in the
+  partition table that forms part of the boot-loader.
+
+The boot-loader code is in platform-multicomp09/boot/boot.s
+
+It is designed to load and start at address 0xd000. It is exactly 512 bytes in
+size and it is laid out like a standard hard drive master boot record (MBR)
+except that:
+
+- The code region contains 6809 code rather than 8086 code.
+- The 4 entries in the partition table specify the locations of the partitions
+  relative to the location of the MBR itself.
+
+The boot-loader can be loaded and started from CamelForth like this:
+
+HEX MMUMAP 3 SDLBA2 0 D000 SDRD D000 PIVOT
+
+(the latest version of CamelForth has a word FUZIX defined to do this)
+
+When control passes to the boot-loader, it locates the kernel, parses the DECB
+format to load the kernel to RAM and pass control to it at its start address
+(which is encoded into the DECB file).
+
+
+
+*************************
+KEYBOARD
+*************************
+
+The default I/O is the VGA/PS2 keyboard, *or* the RS232 serial port, depending
+upon the setting of the VDUFFD0 jumper.
+
+
+*************************
+DEVICES (so far)
+*************************
+
+node       major   minor     description
+/dev/tty1  2       1                console
+/dev/tty2  2       2         RS232 port
+/dev/tty3  2       3         USB virtual RS232 port
+/dev/tty4  2       4         Drivewire Virtual Window #0  (coming soon..)
+/dev/dw?   8       0-256     Drivewire Block Drives       (coming soon..)
+
+
+**************************
+BUILDING
+**************************
+
+# 1. Build the kernel:
+make -C Kernel TARGET=multicomp09
+
+# 2. Build the bootstrap loader
+make -C Kernel/platform-multicomp09 boot.bin
+
+# 2. Build the libraries
+make -C Library tools/syscall_6809
+make -C Library/libs -f Makefile.6809 TARGET=multicomp09
+
+# 3. Build the utils
+make -C Applications/util -f Makefile.6809 TARGET=multicomp09
+make -C Applications/V7/cmd/sh -f Makefile.6809 TARGET=multicomp09
+make -C Applications/levee -f Makefile.6809 TARGET=multicomp09
+make -C Applications/cave -f Makefile.6809 TARGET=multicomp09
+make -C Applications/dw -f Makefile.6809 TARGET=multicomp09
+make -C Applications/cpp  TARGET=multicomp09
+make -C Applications/as09 TARGET=multicomp09
+make -C Applications/ld09 TARGET=multicomp09
+make -C Applications/V7/games -f Makefile.6809 TARGET=multicomp09
+
+# 4. Build disk tools
+make -C Standalone
+
+# 5. Build boot disk image
+cd Standalone/filesystem-src && ./build-filesystem -X fuzixfs.dsk 256 65535
+
+# 6. Add extras
+cd Standalone/filesystem-src && ./add-extras -X fuzixfs.dsk
+
+
+**************************
+CREATING THE SD CARD
+**************************
+
+If using other Multicomp software, use the normal create_sd_image script. The
+FUZIX-related lines are as follows:
+
+dd if=fuzix_boot.bin of=multicomp09_sd.img obs=512K seek=192
+dd if=fuzix.bin      of=multicomp09_sd.img obs=512K seek=193
+dd if=fuzixfs.dsk of=multicomp09_sd.img obs=1M seek=98
+
+finally, use dd to transfer multicomp09_sd.img to an SD card.
+
+
+*************************
+DONE
+*************************
+
+* Fix the underlying Banking layout to better handle UDATA
+
+
+*************************
+TO DO
+*************************
+
+[NAC HACK 2016Apr23] my TO DO
+
+* Swapping to disk has not been implemented yet
+* Drivewire driver (should be simple based on Brett's work)
+* RTC driver
+* currently assume 64, 8K pages - like coco3. Want actually to do a memory size and cope with 512K
+  or 1MByte
+* unify things that need not be different from coco3
+* BUG fix exec09 core dump on register print
+* BUG separate memcpy memset out of videoll.s -- for all 6809 platforms.
+* BUG should have CONFIG_SDC in coco config.def otherwise devsdc_init doesn't get called in devices.c
+* BUG other platforms have mixture of CONFIG_IDE and DEVICE_IDE and CONFIG_SDC and DEVICE_SD
+* BUG coco3 devices.c should #include devide.h devsd.h
+* BUG fix problem of missing include file that I ended up copying
+* BUG devsd.h has devsd_init but devsdc.c had devsdc_init, so did devices.c The other routine names
+* are not uniform either..
+* BUG loader does not spot overlapped section or section that overflows address space
+* BUG Library/libs/fuzix has Makefile but this is a derived file and should
+  probably not be committed.. then remove from .gitignore
+* BUG LWASM binary output does not pad from ORG to ORG.. affects boot.s - this is kinda documented
+* BUG LWASM binary output does not pad as a result of .ds directives.. affects boot.s - this is not documented
+* BUG the pcw8256 tree has some checked-in ~ files.
+* BUG exec09 doesn't detect when an out-of-range memory address in a region
+  is accessed.. or when an out-of-range memory page is mapped.
+* BUG exec09 reported this: 01:0x299F 1026FF7B              LBNE  01:0x1291E -- address out of range!!
+* add more stuff from util - including FORTH
+* work out where advent.db ought to be.
+* Get 2nd PCB working
+* Upgrade to have FUZIX in CamelForth ROM
+
+
+*************************
+BUGS
+*************************
+
+* Things work better if you compile the userspace utilities (esp. "init")
+with standard gcc optimizations.
+
+
diff --git a/Kernel/platform-multicomp09/boot/boot.s b/Kernel/platform-multicomp09/boot/boot.s
new file mode 100644 (file)
index 0000000..03c5af5
--- /dev/null
@@ -0,0 +1,309 @@
+;;;
+;;; A Fuzix booter for the multicomp09 SDcard controller.
+;;;
+;;; Neal Crook April 2016
+;;; This code started as a frankensteinian fusion of Brett's Coco3
+;;; booter and my FLEX bootstrap loader.
+;;;
+;;; The booter is contained within a single 512-byte sector and is
+;;; formatted like a standard MSDOS master boot record (MBR) -- except
+;;; that the code is 6809 and not position-independent.
+;;;
+;;; An MBR is intended to go in sector 0 but I cannot easily arrange
+;;; that on my SD and so I have placed it elsewhere -- and added an
+;;; option to the FUZIX MBR parser to look for it at an arbitrary
+;;; location. The partition data in the MBR is still absolute, though;
+;;; *not* relative to the location of the MBR.
+;;;
+;;; The booter (the whole MBR) can live anywhere on the SD. It is loaded
+;;; to 0xd000 and entered from there -- the load address is chosen simply
+;;; to avoid the kernel; it may change in future if I adjust the memory
+;;; map.
+;;;
+;;; The booter is completely self-contained within a 512-byte footprint:
+;;; it uses no disk buffer and its small stack is allocated within its
+;;; memory footprint.
+;;;
+;;; Environment: at entry, the multicomp ROM is disabled and the
+;;; MMU is enabled and is set up for a flat (1-1) mapping, with TR=0.
+;;; Function: load and start a DECB image (the FUZIX kernel). The
+;;; location of the image on the SDcard is hard-wired by equates
+;;; klba2..klba0 below.
+
+klba2  equ $3                  ; LBA=0x0003.0400
+klba1  equ $4
+klba0  equ $0
+
+;;; --------- multicomp i/o registers
+
+;;; sdcard control registers
+sddata equ $ffd8
+sdctl  equ $ffd9
+sdlba0 equ $ffda
+sdlba1 equ $ffdb
+sdlba2 equ $ffdc
+
+;;; vdu/virtual UART
+uartdat        equ $ffd1
+uartsta        equ $ffd0
+
+
+;;; based on the memory map, this seems a safe place to load; the
+;;; kernel doesn't use any space here. That may change and require
+;;; a re-evaluation.
+start   equ $d000
+
+
+       org     start
+
+;;; entry point
+       lds     #stack
+
+       lda     #'F'            ; show user that we got here
+       bsr     tovdu
+       lda     #'U'
+       bsr     tovdu
+       lda     #'Z'
+       bsr     tovdu
+       lda     #'I'
+       bsr     tovdu
+       lda     #'X'
+       bsr     tovdu
+
+;;; decb format:
+;;;
+;;; section preamble:
+;;; offset 0 0x00
+;;;       1 length high
+;;;       2 length low
+;;;       3 load address high
+;;;       4 load address low
+;;;
+;;; image postamble:
+;;; offset 0 0xff
+;;;       1 0x00
+;;;       2 0x00
+;;;       3 exec high
+;;;       4 exec low
+
+;;; Y - counts how many bytes remain to be transferred from disk;
+;;; Start empty to trigger the initial disk load.
+       ldy     #0
+
+c@     jsr     getb            ; get a byte in A from buffer
+       cmpa    #$ff            ; postamble marker?
+       beq     post            ; yes, handle it and we're done.
+       ;; expect preamble
+       cmpa    #0              ; preamble marker?
+       lbne    abort           ; unexpected.. bad format
+       jsr     getw            ; D = length
+       tfr     d,x             ; X = length
+       jsr     getw            ; D = load address
+       tfr     d,u             ; U = load address
+       ;; load section: X bytes into memory at U
+d@     jsr     getb            ; A = byte
+       sta     ,u+             ; copy to memory
+       leax    -1,x            ; decrement byte count
+       bne     d@              ; loop for next byte if any
+       bra     c@              ; loop for next pre/post amble
+       ;; postable
+post   jsr     getw            ; get zero's
+       cmpd    #0              ; test D.. expect 0
+       lbne    abort           ; unexpected.. bad format
+       jsr     getw            ; get exec address
+       pshs    d               ; save on stack
+        jsr     drain           ; leave SD controller idle
+       rts                     ; go and never come back
+
+
+;;; Abort! Bad record format.
+abort  lda     #'B'            ; show user that we got here
+       bsr     tovdu
+       lda     #'A'
+       bsr     tovdu
+       lda     #'D'
+       bsr     tovdu
+       lda     #$0d
+       bsr     tovdu
+       lda     #$0a
+       bsr     tovdu
+abort1 bra     abort1          ; spin forever
+
+
+;;;
+;;; SUBROUTINE ENTRY POINT
+;;; send character to vdu
+;;; a: character to print
+;;; can destroy b,cc
+
+tovdu  pshs    b
+vdubiz ldb     uartsta
+       bitb    #2
+       beq     vdubiz  ; busy
+
+       sta     uartdat ; ready, send character
+       puls    b,pc
+
+
+;;;
+;;; SUBROUTINE ENTRY POINT
+;;; get next word from disk - trigger a new 512-byte read if necessary.
+;;; return word in D
+;;; must preserve Y which is a global tracking the bytes remaining
+
+getw   jsr     getb            ; A = high byte
+       tfr     a,b             ; B = high byte
+       jsr     getb            ; A = low byte
+       exg     a,b             ; flip D = next word
+       rts
+
+
+;;;
+;;; SUBROUTINE ENTRY POINT
+;;; get next byte from disk - trigger a new 512-byte read if necessary.
+;;; return byte in A
+;;; Destroys A, B.
+;;; must preserve Y which is a global tracking the bytes remaining
+
+getb   cmpy    #0              ; out of data?
+       bne     getb4           ; go read byte if not
+getb2  bsr     read            ; read next sector, reset Y
+       ldd     lba1            ; point to next linear block
+       addd    #1
+       std     lba1
+
+getb4  lda     sdctl
+       cmpa    #$e0
+       bne     getb4           ; byte not ready
+       lda     sddata          ; get byte
+        leay    -1,y
+done   rts
+
+
+;;;
+;;; SUBROUTINE ENTRY POINT
+;;; read and discard any pending bytes - to leave the SD controller in
+;;; a polite state.
+;;; Destroys A, B, Y
+
+drain  cmpy    #0              ; out of data?
+       beq     done            ; if so, finished
+
+drain1 lda     sdctl
+       cmpa    #$e0
+       bne     drain1          ; byte not ready
+       lda     sddata          ; get byte
+        leay    -1,y
+       bra     drain
+
+
+;;;
+;;; SUBROUTINE ENTRY POINT
+;;; read single 512-byte block from lba0, lba1, lba2.
+;;; Do not transfer any actual data,
+;;; return Y showing how many bytes are available
+;;; Destroys A, B
+;;;
+
+read    lda    sdctl
+       cmpa    #$80
+       bne     read            ; wait for previous command to complete
+
+       lda     lba0            ; load block address to SDcontroller
+       sta     sdlba0
+       lda     lba1
+       sta     sdlba1
+       lda     lba2
+       sta     sdlba2
+
+       clra
+       sta     sdctl           ; issue RD command to SDcontroller
+
+       lda     #'.'            ; indicate load progress
+       lbsr    tovdu
+
+       ldy     #512            ; 512 bytes available
+       rts
+
+;;; location on SDcard of kernel (24-bit LBA value)
+;;; hack!! The code here assumes NO WRAP from lba1 to lba2.
+lba2   fcb     klba2
+lba1   fcb     klba1
+lba0   fcb     klba0
+
+;;; 16 bytes of stack. Since we have plenty of space, reserve it within the
+;;; 512 bytes of the boot loader itself. By inspection, the code uses 7 bytes
+;;; of stack so this is more than generous (interrupts are disabled)
+        fcb     0,0,0,0,0,0,0,0
+        fcb     0,0,0,0,0,0,0,0
+stack  equ     .
+
+
+;;; Surprisingly, the org statement doesn't achieve this.. it
+;;; doesn't pad the binary. Instead we need to calculate the
+;;; padding that we want to introduce.
+       zmb     start+$1b4-.
+
+
+;;; For MBR and partition table formats, see:
+;;; http://wiki.osdev.org/MBR_%28x86%29
+;;; http://wiki.osdev.org/Partition_Table
+;;; The mbr start sectors are RELATIVE TO THE START OF THIS MBR
+
+        ;; offset $1b4
+mbr_uid
+       fcb     0,0,0,0,0       ; likewise, ".ds 10" does not occupy
+        fcb     0,0,0,0,0       ; any space in the binary.
+
+        ;; offset $1be
+mbr_1
+       fcb     $80             ; bootable (but flag is ignored by FUZIX)
+       fcb     $ff,$ff,$ff     ; start: "max out" CHS so LBA values will be used.
+       fcb     $01             ; system ID (not 0, not 0x5, 0xf or 0x85)
+       fcb     $ff,$ff,$ff     ; end: "max out" as before
+       ;; 32-bit values are stored little-endian: LS byte first.
+       ;; 65535-block root disk at 0x0003.1000 but this mbr
+        ;; is at 0x0003.0000 so encode 0x0000.1000
+       fcb     $00,$10,$00,$00 ; partition's starting sector
+       fcb     $fe,$ff,$00,$00 ; partition's sector count
+
+        ;; offset $1ce
+mbr_2
+       fcb     $80             ; bootable (but flag is ignored by FUZIX)
+       fcb     $ff,$ff,$ff     ; start: "max out" CHS so LBA values will be used.
+       fcb     $01             ; system ID (any non-zero value OK for FUZIX)
+       fcb     $ff,$ff,$ff     ; end: "max out" as before
+       ;; 32-bit values are stored little-endian: LS byte first.
+       ;; 65535-block additional disk at 0x0004.1000 but this mbr
+        ;; is at 0x0003.0000 so encode 0x0001.1000
+       fcb     $00,$10,$01,$00 ; partition's starting sector
+       fcb     $fe,$ff,$00,$00 ; partition's sector count
+
+
+        ;; offset $1de
+mbr_3
+       fcb     $80             ; bootable (but flag is ignored by FUZIX)
+       fcb     $ff,$ff,$ff     ; start: "max out" CHS so LBA values will be used.
+       fcb     $00             ; system ID (0=> unused)
+       fcb     $ff,$ff,$ff     ; end: "max out" as before
+       ;; 32-bit values are stored little-endian: LS byte first.
+       fcb     $00,$01,$02,$fc ; partition's starting sector
+       fcb     $00,$01,$02,$fc ; partition's sector count
+
+
+        ;; offset $1ee
+mbr_4
+       fcb     $80             ; bootable (but flag is ignored by FUZIX)
+       fcb     $ff,$ff,$ff     ; start: "max out" CHS so LBA values will be used.
+       fcb     $00             ; system ID (0=> unused)
+       fcb     $ff,$ff,$ff     ; end: "max out" as before
+       ;; 32-bit values are stored little-endian: LS byte first.
+       fcb     $00,$10,$03,$00 ; partition's starting sector
+       fcb     $fe,$ff,$00,$00 ; partition's sector count
+
+        ;; offset $1fe
+mbr_sig
+       fcb     $55
+       fcb     $aa
+
+       end     start
diff --git a/Kernel/platform-multicomp09/build b/Kernel/platform-multicomp09/build
new file mode 100755 (executable)
index 0000000..cbdcb26
--- /dev/null
@@ -0,0 +1,57 @@
+
+# remake kernel
+cd ..
+make TARGET=multicomp09 clean
+
+# specially make bank16.c (bug fix for compile fart?)
+#m6809-unknown-gcc -c -Wall -msoft-reg-count=0 -mfar-stack-param -I cpu-6809 -I platform-multicomp09 -I include -o bank16k.o bank16k.c
+
+# make rest of kernel
+make TARGET=multicomp09
+
+
+# make bootstrap loader
+cd platform-multicomp09
+make boot.bin
+
+if [ $1 = "kernel" ];  then
+   exit
+fi
+
+
+# make Libs
+cd ../../Library
+rm tools/syscall_6809
+make tools/syscall_6809
+cd libs
+make -f Makefile.6809 TARGET=multicomp09 clean
+make -f Makefile.6809 TARGET=multicomp09
+
+# build utils
+cd ../../Applications/util
+make -f Makefile.6809 TARGET=multicomp09 clean
+make -f Makefile.6809 TARGET=multicomp09
+
+# build dw command
+cd ../dw
+make -f Makefile.6809 TARGET=multicomp09 clean
+make -f Makefile.6809 TARGET=multicomp09
+
+# build sh
+cd ../../Applications/V7/cmd/sh
+make -f Makefile.6809 TARGET=multicomp09 clean
+make -f Makefile.6809 TARGET=multicomp09
+
+# build levee
+cd ../../../levee
+make -f Makefile.6809 TARGET=multicomp09 clean
+make -f Makefile.6809 TARGET=multicomp09
+
+# build utils
+cd ../../Standalone
+make
+
+# build boot disk image
+cd filesystem-src
+./build-filesystem -X fuzixfs.dsk 256 65535
+
diff --git a/Kernel/platform-multicomp09/commonmem.s b/Kernel/platform-multicomp09/commonmem.s
new file mode 100644 (file)
index 0000000..c4aed57
--- /dev/null
@@ -0,0 +1,34 @@
+;
+;      Put the udata at the start of common. We have four 16K banks so we
+; keep the non .common kernel elements below C000 and then keep bank 3 as a
+; true common bank
+;
+        .module commonmem
+
+        ; exported symbols
+        .globl _ub
+        .globl _udata
+        .globl kstack_top
+        .globl istack_top
+        .globl istack_switched_sp
+       .globl kcommon_start
+       
+        .area .udata
+
+;;; first 512 bytes: starts with struct u_block,
+;;; with the kernel stack working down from above
+_ub:
+_udata:
+kstack_base:
+       zmb 512
+kstack_top:
+
+;;; next 256 bytes: 254 byte interrupt stack, then 2 byte saved stack pointer
+istack_base:
+       zmb 254
+istack_top:
+istack_switched_sp: .dw 0
+
+;;; This helps _program_vectors know where the kernel common code starts
+;;; for copying into userspace.
+kcommon_start
\ No newline at end of file
diff --git a/Kernel/platform-multicomp09/config.h b/Kernel/platform-multicomp09/config.h
new file mode 100644 (file)
index 0000000..e0a53a7
--- /dev/null
@@ -0,0 +1,93 @@
+/* Enable to make ^Z dump the inode table for debug */
+#undef CONFIG_IDUMP
+/* Enable to make ^A drop back into the monitor */
+#undef CONFIG_MONITOR
+/* Profil syscall support (not yet complete) */
+#undef CONFIG_PROFIL
+/* Multiple processes in memory at once */
+#define CONFIG_MULTI
+/* Single tasking - for now while we get it booting */
+#undef CONFIG_SINGLETASK
+/* Use C helpers for usermem */
+#undef CONFIG_USERMEM_C
+
+/* Reclaim discard space for buffers */
+#define CONFIG_DYNAMIC_BUFPOOL
+
+/* We use flexible 16K banks so use the helper */
+#define CONFIG_BANK16
+#define CONFIG_BANKS   4
+#define MAX_MAPS 32-3
+#define MAPBASE 0x0000
+/* And swapping */
+/* #define SWAPDEV  2051        */
+#define SWAP_SIZE   0x62
+/* FIXME */
+#define SWAPBASE    0x0000     /* We swap the lot in one, include the */
+#define SWAPTOP            0xC300      /* uarea so its a round number of sectors */
+#define UDATA_BLOCKS   0       /* We swap the uarea in the data */
+#define UDATA_SWAPSIZE 0
+#define MAX_SWAPS      32
+#define swap_map(x)  ((uint8_t *)(x & 0x3fff ))
+
+/* The Drivewire block dev rawmode=1 doesn't work just now
+   with the bank16k.c memory layout (yet), so we have to
+   use legacy binary loading... */
+#define CONFIG_LEGACY_EXEC
+
+
+#define TICKSPERSEC 50      /* Ticks per second */
+#define PROGBASE    0x0100  /* also data base */
+#define PROGTOP     0xe000  /* Top of program, base of U_DATA */
+#define PROGLOAD    0x0100  /* ??? */
+
+#define BOOT_TTY (512 + 1)   /* Set this to default device for stdio, stderr */
+                          /* In this case, the default is the first TTY device */
+                            /* Temp FIXME set to serial port for debug ease */
+
+/* Boot devices */
+#define BOOTDEVICENAMES "hd#,,,,,,,,dw"
+
+/* [NAC HACK 2016Apr24] nicer if I didn't hard-wire this? */
+/* This must be a 16-bit number, not a string! See start.c for examples/encoding */
+/* so this is hda1 */
+/* Without this defined, get prompted for root device at boot time */
+#define BOOTDEVICE 0x0001
+
+/* We need a tidier way to do this from the loader */
+/* [NAC HACK 2016Apr24] 0 because we don't have one */
+#define CMDLINE         NULL     /* Location of root dev name */
+
+/* Allow MBR to be other than at block 0. If so, the start LBA of partitions
+   defined in the MBR are defined relative to the position of the MBR, not
+   relative to the start of the disk (ie, the values they'd have if the
+   MBR was in block 0)
+*/
+#define CONFIG_MBR_OFFSET (0x30000)
+
+/* Device parameters */
+#define NUM_DEV_TTY 11
+#define TTYDEV   BOOT_TTY /* Device used by kernel for messages, panics */
+#define NBUFS    6        /* Number of block buffers */
+#define NMOUNTS         4        /* Number of mounts at a time - nothing mountable! */
+
+
+/* Drivewire Defines */
+
+#define DW_VSER_NUM 4     /* No of Virtual Serial Ports */
+#define DW_VWIN_NUM 4     /* No of Virtual Window Ports */
+#define DW_MIN_OFF  3     /* Minor number offset */
+
+/* Block device define. Each block device can have upto 16 partitions */
+#define MAX_BLKDEV  1     /* 1 SD drive */
+
+
+#undef   DEVICE_IDE        /* enable if IDE interface present */
+#undef   CONFIG_IDE
+
+#define CONFIG_SD
+#define CONFIG_SDC
+#define DEVICE_SD
+#define DEVICE_SDC
+
+#define SD_DRIVE_COUNT 1  /* 1 drive */
diff --git a/Kernel/platform-multicomp09/crt0.s b/Kernel/platform-multicomp09/crt0.s
new file mode 100644 (file)
index 0000000..4413102
--- /dev/null
@@ -0,0 +1,37 @@
+;;;
+;;; The Kernel C run-time / start routine
+;;;
+
+;;;  imported symbols
+       .globl  _fuzix_main
+       .globl  init_early
+       .globl  init_hardware
+       .globl  kstack_top
+
+
+       ;; exported symbols
+       .globl  start
+
+       ;; startup code at offset 0
+       .area   .start
+start:  jmp    main
+
+       .area   .text
+
+main:  orcc    #0x10           ; interrupts definitely off
+       lds     #kstack_top
+
+       ;; zero out kernel's bss section
+       ldx     #__sectionbase_.bss__
+       ldy     #__sectionlen_.bss__
+bss_wipe:
+       clr     ,x+
+       leay    -1,y
+       bne     bss_wipe
+
+       jsr     init_early
+       jsr     init_hardware
+       jsr     _fuzix_main
+       orcc    #0x10
+stop:  bra     stop
+
diff --git a/Kernel/platform-multicomp09/device.h b/Kernel/platform-multicomp09/device.h
new file mode 100644 (file)
index 0000000..47575eb
--- /dev/null
@@ -0,0 +1,5 @@
+#ifndef __DEVICE_DOT_H__
+#define __DEVICE_DOT_H__
+
+
+#endif /* __DEVICE_DOT_H__ */
diff --git a/Kernel/platform-multicomp09/devices.c b/Kernel/platform-multicomp09/devices.c
new file mode 100644 (file)
index 0000000..1fb191d
--- /dev/null
@@ -0,0 +1,57 @@
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devdw.h>
+#include <devsys.h>
+#include <tty.h>
+#include <vt.h>
+#include <devtty.h>
+#ifdef CONFIG_IDE
+#include <devide.h>
+#endif
+#include <devsd.h>
+#include <blkdev.h>
+/* [NAC HACK 2016Apr24] add ds1302 */
+
+struct devsw dev_tab[] =  /* The device driver switch table */
+{
+// minor    open         close        read      write       ioctl
+// -----------------------------------------------------------------
+  /* 0: /dev/hd                Hard disc block devices (absent) */
+  {  blkdev_open,   no_close,    blkdev_read,  blkdev_write,   blkdev_ioctl },
+  /* 1: /dev/fd                Floppy disc block devices  */
+  {  nxio_open,     no_close,    no_rdwr,   no_rdwr,   no_ioctl },
+  /* 2: /dev/tty       TTY devices */
+  {  tty_open,     my_tty_close,   tty_read,  tty_write,  tty_ioctl },
+  /* 3: /dev/lpr       Printer devices */
+  {  nxio_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 },
+  /* /dev/dw   Drivewire */
+  {  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)
+{
+#ifdef CONFIG_IDE
+       devide_init( );
+#endif
+       /*#ifdef CONFIG_SDC [NAC HACK 2016Apr24] */
+#ifdef CONFIG_SD
+       devsd_init( );
+#endif
+}
+
diff --git a/Kernel/platform-multicomp09/devsdc.c b/Kernel/platform-multicomp09/devsdc.c
new file mode 100644 (file)
index 0000000..a5994a5
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+
+  multicomp09 SD driver
+
+  Derived from:
+
+  CoCoSDC driver
+  (c)2015 Brett M. Gordon GPL2
+
+ * Needs work on extended SDC API stuff.
+ * init / mounting stuff really needs to set/update blkdev structure for size
+ * need to get rawmode=1 and 2 working.
+
+*/
+
+#include <kernel.h>
+#include <kdata.h>
+#include <blkdev.h>
+#include <mbr.h>
+/* not to be confused with devsd.h ..!! [NAC HACK 2016Apr26] */
+#include <devsdc.h>
+#include <printf.h>
+
+
+/* multicomp09 hw registers */
+#define SDDATA  (0xffd8)
+#define SDCTL   (0xffd9)
+#define SDLBA0  (0xffda)
+#define SDLBA1  (0xffdb)
+#define SDLBA2  (0xffdc)
+
+#define SDC_IDLE_STAT (0x80)
+
+#define SDC_WR_CMD (0x01)
+#define SDC_RD_CMD (0x00)
+
+
+#define sdc_reg_ctl  *((volatile uint8_t *)SDCTL)
+#define sdc_reg_data *((volatile uint8_t *)SDDATA)
+#define sdc_reg_lba0 *((volatile uint8_t *)SDLBA0)
+#define sdc_reg_lba1 *((volatile uint8_t *)SDLBA1)
+#define sdc_reg_lba2 *((volatile uint8_t *)SDLBA2)
+
+
+/* a "simple" internal function pointer to which transfer
+   routine to use.
+*/
+typedef void (*sdc_transfer_function_t)( void *addr);
+
+
+/* blkdev method: flush drive */
+int devsdc_flush( void )
+{
+       return 0;
+}
+
+/* [NAC HACK 2016Apr26] devsd version is called devsd_transfer_sector */
+
+/* blkdev method: transfer sectors */
+uint8_t devsdc_transfer(void)
+{
+       uint8_t *ptr;                  /* points to 32 bit lba in blk op */
+       sdc_transfer_function_t fptr;  /* holds which xfer routine we want */
+        int i;
+        uint8_t tmp;
+
+       /* wait for drive to go non-busy after previous command
+          (if any)
+       */
+       while (sdc_reg_ctl != SDC_IDLE_STAT) {
+       }
+
+       /* [NAC HACK 2016May11] should not need this but real hardware seems
+          to need something here even tho CUBIX FORTH NITROS9 FLEX all work
+          without it and with seemingly equivalent code
+       */
+       for (i=0; i<1000; i++) {
+               tmp = sdc_reg_ctl;
+       }
+
+       /* load up block address. It's stored as a 32-bit value but we
+          ignore the MS byte because the SD controller only has a
+          24-bit address range
+       */
+       ptr=((uint8_t *)(&blk_op.lba))+1;
+       sdc_reg_lba2 = ptr[0]; /* MS byte of 24-bit block address */
+       sdc_reg_lba1 = ptr[1];
+       sdc_reg_lba0 = ptr[2];
+
+
+       /* send the command and set up the subroutine pointer */
+       if( blk_op.is_read ){
+               sdc_reg_ctl = SDC_RD_CMD;
+               fptr = devsdc_read;
+       }
+       else{
+               sdc_reg_ctl = SDC_WR_CMD;
+               fptr = devsdc_write;
+       }
+
+       /* do the low-level data transfer (512 bytes) */
+       fptr( blk_op.addr );
+
+       /* No mechanism for failing so assume success! */
+       return 1;
+}
+
+__attribute__((section(".discard")))
+/* Returns true if SDC hardware seems to exist */
+bool devsdc_exist()
+{
+       /* Only way to boot is through SDC so it must
+          exist!
+       */
+       return 1;
+}
+
+__attribute__((section(".discard")))
+/* Call this to initialize SDC/blkdev interface */
+void devsd_init()
+{
+       blkdev_t *blk;
+
+       kputs("SDC: ");
+       if( devsdc_exist() ){
+               /* register first drive */
+               blk=blkdev_alloc();
+               blk->driver_data = 0 ;
+               blk->transfer = devsdc_transfer;
+               blk->flush = devsdc_flush;
+               blk->drive_lba_count=-1;
+               blk->drive_lba_count=32764; /* [NAC HACK 2016Apr26]  hack!! */
+
+               /* by inspection of dev/devsd_discard.c, vital piece missing from this code:
+                  blkdev_scan(blk, 0)  - from dev/blkdev.c
+               */
+               blkdev_scan(blk, 0);
+
+               /* register second drive */
+               /*              blk=blkdev_alloc();
+               blk->driver_data = 1 ;
+               blk->transfer = devsdc_transfer;
+               blk->flush = devsdc_flush;
+               blk->drive_lba_count=-1; */
+               kputs("Ok.\n");
+       }
+       else kprintf("Not Found.\n");
+}
+
diff --git a/Kernel/platform-multicomp09/devsdc.h b/Kernel/platform-multicomp09/devsdc.h
new file mode 100644 (file)
index 0000000..b0f1e83
--- /dev/null
@@ -0,0 +1,4 @@
+
+
+void devsdc_read( char *addr );
+void devsdc_write( char *addr );
diff --git a/Kernel/platform-multicomp09/devtty.c b/Kernel/platform-multicomp09/devtty.c
new file mode 100644 (file)
index 0000000..1b7d0e5
--- /dev/null
@@ -0,0 +1,196 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <devtty.h>
+#include <device.h>
+#include <vt.h>
+#include <tty.h>
+#include <devdw.h>
+#include <ttydw.h>
+#include <graphics.h>
+
+#undef  DEBUG                  /* UNdefine to delete debug code sequences */
+
+
+/* Multicomp has 3 serial ports. Each is a cut-down 6850, with fixed BAUD rate and word size.
+   Port 0 is, by default, a virtual UART interface to a VGA output and PS/2 keyboard
+   Port 1 is, by default, a serial port
+   Port 0 and Port 1 mappings can be swapped through a jumper on the PCB.
+   Port 2 is a serial port.
+
+   Port 0 is used for tty1, Port 1 for tty2.
+*/
+static uint8_t *uart[] = {
+    0,      0,                               /* Unused */
+    (volatile uint8_t *)0xFFD1, (volatile uint8_t *)0xFFD0,    /* Virtual UART Data, Status port0, tty1 */
+    (volatile uint8_t *)0xFFD3, (volatile uint8_t *)0xFFD2,    /*         UART Data, Status port1, tty2 */
+    (volatile uint8_t *)0xFFD5, (volatile uint8_t *)0xFFD4,    /*         UART Data, Status port2, tty3 */
+};
+
+
+/* static int icount = 0; */
+/* static int imatch = 100; */
+/* static uint8_t input[] = "ls -al\nXpwd\nXps\nXwho\nX"; */
+/* static int ccount = 0; */
+
+
+#define VSECT __attribute__((section(".video")))
+#define VSECTD __attribute__((section(".videodata")))
+
+
+
+uint8_t vtattr_cap;
+
+
+uint8_t tbuf1[TTYSIZ];   /* virtual serial port 0: console */
+uint8_t tbuf2[TTYSIZ];   /*         serial port 1: UART */
+uint8_t tbuf3[TTYSIZ];   /*         serial port 2: UART */
+uint8_t tbuf4[TTYSIZ];   /* drivewire VSER 0 */
+uint8_t tbuf5[TTYSIZ];   /* drivewire VSER 1 */
+uint8_t tbuf6[TTYSIZ];   /* drivewire VSER 2 */
+uint8_t tbuf7[TTYSIZ];   /* drivewire VSER 3 */
+uint8_t tbuf8[TTYSIZ];   /* drivewire VWIN 0 */
+uint8_t tbuf9[TTYSIZ];   /* drivewire VWIN 1 */
+uint8_t tbufa[TTYSIZ];   /* drivewire VWIN 2 */
+uint8_t tbufb[TTYSIZ];   /* drivewire VWIN 3 */
+
+
+struct s_queue ttyinq[NUM_DEV_TTY + 1] = {
+       /* ttyinq[0] is never used */
+       {NULL, NULL, NULL, 0, 0, 0},
+       /* Virtual UART/Real UART Consoles */
+       {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2},
+       {tbuf2, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ / 2},
+       {tbuf3, tbuf3, tbuf3, TTYSIZ, 0, TTYSIZ / 2},
+       /* Drivewire Virtual Serial Ports */
+       {tbuf4, tbuf4, tbuf4, TTYSIZ, 0, TTYSIZ / 2},
+       {tbuf5, tbuf5, tbuf5, TTYSIZ, 0, TTYSIZ / 2},
+       {tbuf6, tbuf6, tbuf6, TTYSIZ, 0, TTYSIZ / 2},
+       {tbuf7, tbuf7, tbuf7, TTYSIZ, 0, TTYSIZ / 2},
+       /* Drivewire Virtual Window Ports */
+       {tbuf8, tbuf8, tbuf8, TTYSIZ, 0, TTYSIZ / 2},
+       {tbuf9, tbuf9, tbuf9, TTYSIZ, 0, TTYSIZ / 2},
+       {tbufa, tbufa, tbufa, TTYSIZ, 0, TTYSIZ / 2},
+       {tbufb, tbufa, tbufa, TTYSIZ, 0, TTYSIZ / 2},
+};
+
+
+
+
+/* A wrapper for tty_close that closes the DW port properly */
+int my_tty_close(uint8_t minor)
+{
+       if (minor > 3 && ttydata[minor].users == 1)
+               dw_vclose(minor);
+       return (tty_close(minor));
+}
+
+
+/* Output for the system console (kprintf etc) */
+/* [NAC HACK 2016May12] should this use minor number of BOOT_TTY or TTYDEV instead of being hard-wired to 1?? */
+void kputchar(char c)
+{
+       if (c == '\n')
+            tty_putc(minor(TTYDEV), '\r');
+       tty_putc(minor(TTYDEV), c);
+}
+
+ttyready_t tty_writeready(uint8_t minor)
+{
+       uint8_t c;
+        if ((minor < 1) || (minor > 3)) {
+            return TTY_READY_NOW;
+        }
+       c = *(uart[minor*2 + 1]); /* 2 entries per UART, +1 to get STATUS */
+       return (c & 2) ? TTY_READY_NOW : TTY_READY_SOON; /* TX DATA empty */
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+       if ((minor > 0) && (minor < 4)) {
+               *(uart[minor*2]) = c; /* UART Data */
+       }
+       if (minor > 3 ) {
+               dw_putc(minor, c);
+       }
+}
+
+void tty_sleeping(uint8_t minor)
+{
+       used(minor);
+}
+
+
+void tty_setup(uint8_t minor)
+{
+       if (minor > 3) {
+               dw_vopen(minor);
+               return;
+       }
+}
+
+
+int tty_carrier(uint8_t minor)
+{
+       if( minor > 2 ) return dw_carrier( minor );
+       return 1;
+}
+
+void tty_interrupt(void)
+{
+
+}
+
+
+
+void platform_interrupt(void)
+{
+       uint8_t c;
+       /* Check each UART for characters and dispatch if available
+          .. assuming I eventually get around to enabling serial Rx interrupts
+          this will just get perkier with no additional coding required
+          [NAC HACK 2016May05]  enable serial interrupts!!
+
+          **Really** need to get non-blocking input working on the emulator..
+       */
+        c = *(uart[1*2 + 1]);
+          if (c & 0x01) { tty_inproc(1, *(uart[1*2])); }
+       /*      c = *(uart[2*2 + 1]);
+       if (c & 0x01) { tty_inproc(2, *(uart[2*2])); }
+       c = *(uart[3*2 + 1]);
+       if (c & 0x01) { tty_inproc(3, *(uart[3*2])); } */
+
+        /* icount++; */
+        /* if (icount == imatch) { */
+       /*      imatch += 200; */
+       /*      if (input[ccount] != 0) { */
+       /*              while (input[ccount] != 'X') { */
+       /*                      tty_inproc(minor(TTYDEV), input[ccount++]); */
+       /*              } */
+       /*              ccount++; */
+       /*      } */
+        /* } */
+
+       /* [NAC HACK 2016May07] need defines for the timer */
+       c = *((volatile uint8_t *)0xFFDD);
+       if (c & 0x80) {
+               *((volatile uint8_t *)0xFFDD) = c; /* service the hardware */
+               /* tell the OS it happened */
+               timer_interrupt();
+       }
+
+       dw_vpoll();
+}
+
+
+/* Initial Setup stuff down here. */
+
+__attribute__((section(".discard")))
+void devtty_init()
+{
+       /* Reset each UART by write to STATUS register */
+       *uart[3] = 3;
+       *uart[5] = 3;
+       *uart[7] = 3;
+}
diff --git a/Kernel/platform-multicomp09/devtty.h b/Kernel/platform-multicomp09/devtty.h
new file mode 100644 (file)
index 0000000..ed24761
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+
+#include <../include/vt.h>
+#include <../include/graphics.h>
+/* [NAC HACK 2016May11] should be able to get rid of all of this */
+struct pty {
+       unsigned char *base;    /* base of buffer in cpu space */
+       unsigned char *cpos;    /* current location of cursor */
+       unsigned char csave;    /* charactor that is under the cursor */
+       struct vt_switch vt;    /* the vt.o module's state */
+       unsigned int scrloc;    /* location to put into gimme */
+       unsigned char vmod;     /* video mode */
+       unsigned char vres;     /* video register settings of this tty */   
+       unsigned char width;    /* text width of screen */
+       unsigned char height;   /* text height */
+       unsigned char right;    /* right most coord */
+       unsigned char bottom;   /* bottom most coord */
+       struct display *fdisp;  /* ptr to struct for ioctl */
+       uint8_t attr;           /* attribute byte to apply */
+};
+
+extern struct pty *curpty;
+
+int my_tty_close( uint8_t minor ); /* wrapper call to close DW ports */
+
+#endif
diff --git a/Kernel/platform-multicomp09/drivewire.s b/Kernel/platform-multicomp09/drivewire.s
new file mode 100644 (file)
index 0000000..c7ac5a3
--- /dev/null
@@ -0,0 +1,169 @@
+;;
+;; 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
+
+       .area .common
+
+;;;  Drivewire is really client (your retro 8 bitter)
+;;;  driven.  Because the original CoCo hardware lacks
+;;;  serial IRQs, and the fact that a 6809 interrupt on a ~2Mhz
+;;;  machine isn't fast enough to catch the host's reply packet
+;;;  , the write and read functions must be "close" together.
+;;;   uint16_t dw_transaction( char *send, uint16_t scnt,
+;;;                           char *recv, uint16_t rcnt )
+;;;   x=send cc y ret scnt recv rcnt
+;;;  Brett M. Gordon
+_dw_transaction:
+       pshs    cc,y            ; save caller
+       orcc    #0x50           ; stop interrupts
+       ldy     5,s             ; Y = number of bytes to send
+       beq     out@            ; no byte 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     out@            ; 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
+out@   ldx     #0              ; no error
+       puls    cc,y,pc         ; return
+frame@ ldx     #-1             ; frame error
+       puls    cc,y,pc         ; return
+part@  ldx     #-2             ; not all bytes received!
+       puls    cc,y,pc         ; return
+
+       
+_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
+       ldy 4,s         ; driveptr
+       lda ,y          ; for now, contains minor = drive number directly
+       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
+@ret   puls y,pc
+@err   ldx #0xFFFF
+       bra @ret
+
+; 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
+
+; 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
+         include "dw.def"
+         include "dwread.s"
+         include "dwwrite.s"
+
diff --git a/Kernel/platform-multicomp09/dw.def b/Kernel/platform-multicomp09/dw.def
new file mode 100644 (file)
index 0000000..677f4c7
--- /dev/null
@@ -0,0 +1,70 @@
+********************************************************************
+*
+* Copied from HDB-DOS from toolshed.sf.net
+*
+* dwdefs - DriveWire Definitions File
+*
+* $Id: dwdefs.d,v 1.10 2010/02/21 06:24:47 aaronwolfe Exp $
+*
+* Ed.    Comments                                       Who YY/MM/DD
+* ------------------------------------------------------------------
+*   1    Started                                        BGP 03/04/03
+*   2    Added DWGLOBS area                             BGP 09/12/27
+
+         nam   dwdefs
+         ttl   DriveWire Definitions File
+
+* Addresses
+BBOUT       equ    $FF20
+BBIN        equ    $FF22
+
+* Opcodes
+OP_NOP      equ    $00         No-Op
+OP_RESET1   equ    $FE         Server Reset
+OP_RESET2   equ    $FF         Server Reset
+OP_RESET3   equ    $F8         Server Reset
+OP_DWINIT      equ        'Z           DriveWire dw3 init/OS9 boot
+OP_TIME     equ    '#          Current time requested
+OP_INIT     equ    'I          Init routine called
+OP_READ     equ    'R          Read one sector
+OP_REREAD   equ    'r          Re-read one sector
+OP_READEX   equ    'R+128      Read one sector
+OP_REREADEX equ    'r+128      Re-read one sector
+OP_WRITE    equ    'W          Write one sector
+OP_REWRIT   equ    'w          Re-write one sector
+OP_GETSTA   equ    'G          GetStat routine called
+OP_SETSTA   equ    'S          SetStat routine called
+OP_TERM     equ    'T          Term routine called
+OP_SERINIT  equ    'E
+OP_SERTERM  equ    'E+128
+
+* Printer opcodes
+OP_PRINT    equ    'P          Print byte to the print buffer
+OP_PRINTFLUSH equ  'F          Flush the server print buffer
+
+* Serial opcodes
+OP_SERREAD equ 'C
+OP_SERREADM equ 'c
+OP_SERWRITE equ 'C+128
+OP_SERGETSTAT equ 'D
+OP_SERSETSTAT equ 'D+128
+
+* for dw vfm
+OP_VFM equ 'V+128
+
+* WireBug opcodes (Server-initiated)
+OP_WIREBUG_MODE  equ   'B
+* WireBug opcodes (Server-initiated)
+OP_WIREBUG_READREGS   equ  'R  Read the CoCo's registers
+OP_WIREBUG_WRITEREGS  equ  'r  Write the CoCo's registers
+OP_WIREBUG_READMEM    equ  'M  Read the CoCo's memory
+OP_WIREBUG_WRITEMEM   equ  'm  Write the CoCo's memory
+OP_WIREBUG_GO         equ  'G  Tell CoCo to get out of WireBug mode and continue execution
+
+* VPort opcodes (CoCo-initiated)
+OP_VPORT_READ         equ  'V
+OP_VPORT_WRITE        equ  'v
+
+* Error definitions
+E_CRC      equ   $F3            Same as NitrOS-9 E$CRC
+
diff --git a/Kernel/platform-multicomp09/dwread.s b/Kernel/platform-multicomp09/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-multicomp09/dwwrite.s b/Kernel/platform-multicomp09/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-multicomp09/fuzix.link b/Kernel/platform-multicomp09/fuzix.link
new file mode 100644 (file)
index 0000000..724436a
--- /dev/null
@@ -0,0 +1,17 @@
+define basesympat __sectionbase_%s__
+define lensympat __sectionlen_%s__
+section .start load 0x0200
+section .video
+section .videodata
+section .text2
+section .text
+section .text.hot
+section .test.unlikely
+section .data
+section .buffers
+section .discard
+section        .bss
+section .udata load 0xe000
+section .common
+section .cpage load 0xfe00
+entry start
diff --git a/Kernel/platform-multicomp09/kernel.def b/Kernel/platform-multicomp09/kernel.def
new file mode 100644 (file)
index 0000000..c56225a
--- /dev/null
@@ -0,0 +1,28 @@
+;;; UZI mnemonics for memory addresses etc
+
+U_DATA                      equ 0xe000       ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE           equ 0x200        ; 256+256 bytes.
+
+
+;;; We don't need these macros for we have no
+;;; SAM shenanigans on a CoCo3
+
+SAM_USER macro
+       endm
+
+SAM_KERNEL macro
+       endm
+
+SAM_SAVE macro
+       endm
+
+SAM_RESTORE macro
+       endm
+
+
+;; asm/c shared IDE stuff
+IDEDATA                            equ 0xFF50
+IDEDATA_L                  equ 0xFF58
+
+
+NBUFS                      equ 6
\ No newline at end of file
diff --git a/Kernel/platform-multicomp09/libc.c b/Kernel/platform-multicomp09/libc.c
new file mode 100644 (file)
index 0000000..7b3842b
--- /dev/null
@@ -0,0 +1,20 @@
+#include "cpu.h"
+
+
+size_t strlen(const char *p)
+{
+  const char *e = p;
+  while(*e++);
+  return e-p-1;
+}
+
+/* Until we pull out the bits of libgcc that are useful instead */
+void abort(void)
+{
+  while(1);
+}
+
+void *malloc(size_t size)
+{
+  return 0;
+}
diff --git a/Kernel/platform-multicomp09/main.c b/Kernel/platform-multicomp09/main.c
new file mode 100644 (file)
index 0000000..39fcb74
--- /dev/null
@@ -0,0 +1,61 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+
+struct blkbuf *bufpool_end = bufpool + NBUFS;
+
+void platform_discard(void)
+{
+       extern uint8_t discard_size;
+       bufptr bp = bufpool_end;
+
+       kprintf("%d buffers reclaimed from discard\n", discard_size);
+       
+       bufpool_end += discard_size;
+
+       memset( bp, 0, discard_size * sizeof(struct blkbuf) );
+
+       for( bp = bufpool + NBUFS; bp < bufpool_end; ++bp ){
+               bp->bf_dev = NO_DEVICE;
+               bp->bf_busy = BF_FREE;
+       }
+}
+
+
+void platform_idle(void)
+{
+}
+
+void do_beep(void)
+{
+}
+
+/*
+ Map handling: We have flexible paging. Each map table consists
+ of a set of pages with the last page repeated to fill any holes.
+ */
+
+void pagemap_init(void)
+{
+    int i;
+    /*  We have 64 8k pages for a CoCo3 so insert every other one
+     *  into the kernel allocator map.
+     */
+    for (i = 10; i < 64; i+=2)
+        pagemap_add(i);
+    /* add common page last so init gets it */
+    pagemap_add(6);
+}
+
+void map_init(void)
+{
+}
+
+
+uint8_t platform_param(unsigned char *p)
+{
+       return 0;
+}
diff --git a/Kernel/platform-multicomp09/multicomp09.s b/Kernel/platform-multicomp09/multicomp09.s
new file mode 100644 (file)
index 0000000..2088356
--- /dev/null
@@ -0,0 +1,514 @@
+;;;
+;;; Multicomp 6809 FPGA-based computer
+;;;
+;;;    low level routines, but not the tricky ones.
+;;;    see tricks.s for those.
+
+;;; coco3:
+;;; $ff91 writeonly
+;;; $ffa0
+;;; $ff90
+;;; $ffd9   high-speed poke
+;;; $ff9c   scroll register
+;;; $ffae   super basic in MMU
+;;; $c033   BASIC mirror of video reg
+;;; $ff98   video row setup
+;;; $ff99   video col setup
+;;; $ff9d   video map setup
+;;; $ffb0   video colour
+;;; $ffb0   video colour
+;;; $ffb8   video colour
+;;; $ffb0   video colour
+;;; $ffb0   video colour
+
+;;; coco3 MMU
+;;; accessed through registers $ff91 and $ffa0-$ffa7
+;;; 2 possible memory maps: map0, map1 selected by $ff91[0]
+;;; map0 is used for Kernel mode, map1 is used for User mode.
+;;; map1 is selected at boot (ie, now).
+;;; when 0, select map0 using pages stored in $ffa0-$ffa7
+;;; when 1, select map1 using pages stored in $ffa8-$ffaf
+;;; a 512K system has 64 blocks, numbered $00 to $3f
+;;; write the block number to the paging register. On readback,
+;;; only bits 5:0 are valid; the other bits can contain junk.
+
+;;; multicomp09 MMU
+;;; accessed through two WRITE-ONLY registers MMUADR, MMUDAT
+;;; 2 possible memory maps: map0, map1 selected by MMUADR[6]
+;;; map0 is used for Kernel mode, map1 is used for User mode.
+;;; map0 is selected at boot (ie, now)
+;;; [NAC HACK 2016Apr23] to avoid pointless divergence from
+;;; coco3, the first hardware setup step will be to flip to
+;;; map1.
+;;; [NAC HACK 2016Apr23] in the future, may handle this in
+;;; forth or in the bootstrap
+;;; when 0, select map0 using MAPSEL values 0-7
+;;; when 1, select map1 using MAPSEL values 8-15
+;;; MAPSEL is MMUADR[3:0]
+;;; a  512K system has  64 blocks, numbered $00 to $3f
+;;; a 1024K system has 128 blocks, numbered $00 to $7f
+;;; Write the block number to MMUDAT[6:0]
+;;; MMUDAT[7]=1 write-protects the selected block - NOT USED HERE!
+
+;;; coco3: at the time the boot loader passes control this the code here,
+;;; map1 is selected (Kernel space) and the map1 mapping
+;;; registers are selecting blocks 0-7.
+;;; map0 is selecting blocks $38-$3f.
+
+;;; multicomp09: at the time the boot loader passes control this the code here,
+;;; map0 is selected (user space) and the map0 mapping
+;;; registers are selecting blocks 0-7.
+;;; map1 mapping registers are uninitialised.
+
+
+;;; multicomp09 HW registers
+MMUADR equ     $ffde
+MMUDAT equ     $ffdf
+TIMER   equ     $ffdd
+
+;;; bit-fields
+MMUADR_ROMDIS  equ $80         ; 0 after reset, 1 when FUZIX boots. Leave at 1.
+MMUADR_TR      equ $40         ; 0 after reset, 0 when FUZIX boots. 0=map0, 1=map1
+MMUADR_MMUEN   equ $20         ; 0 after reset, 1 when FUZIX boots. Leave at 1.
+MMUADR_NMI     equ $10         ; 0 after reset, 0 when FUZIX boots. Do not write 1.
+MMUADR_MAPSEL  equ $0f         ; last-written value is UNDEFINED.
+;;; the only two useful values for the upper nibble
+MMU_MAP0       equ     (MMUADR_ROMDIS|MMUADR_MMUEN)
+MMU_MAP1       equ     (MMUADR_ROMDIS|MMUADR_MMUEN|MMUADR_TR)
+
+TIMER_ON        equ $02
+TIMER_OFF       equ $00
+TIMER_INT       equ $80
+
+
+            .module multicomp09
+
+            ; exported symbols
+            .globl init_early
+            .globl init_hardware
+            .globl interrupt_handler
+            .globl _program_vectors
+           .globl map_kernel
+           .globl map_process
+           .globl map_process_always
+           .globl map_save
+           .globl map_restore
+           .globl _need_resched
+           .globl _bufpool
+           .globl _discard_size
+            .globl _krn_mmu_map
+            .globl _usr_mmu_map
+           .globl curr_tr
+
+            ; exported debugging tools
+            .globl _trap_monitor
+           .globl _trap_reboot
+            .globl outchar
+           .globl _di
+           .globl _ei
+           .globl _irqrestore
+
+            ; imported symbols
+            .globl _ramsize
+            .globl _procmem
+            .globl unix_syscall_entry
+           .globl nmi_handler
+           .globl null_handler
+
+            include "kernel.def"
+            include "../kernel09.def"
+
+
+       .area   .buffers
+
+_bufpool:
+       .ds     BUFSIZE*NBUFS
+
+       .area   .discard
+_discard_size:
+       .db     __sectionlen_.discard__/BUFSIZE
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK
+; -----------------------------------------------------------------------------
+       .area .common
+
+
+saved_tr
+       .db 0           ; the saved state of the TR bit etc (MMU_MAP0 or MMUMAP1)
+curr_tr
+       .db 0           ; the current state of the TR bit etc (MMU_MAP0 or MMUMAP1)
+_need_resched
+       .db 0           ; scheduler flag
+
+;;; multicomp09 mmu registers are write-only so need to store
+;;; a copy of the mappings.
+;;; Each byte represents one of the 8Kbyte physical address regions.
+;;; In general, pages are assigned in pairs, but that is not true for the top
+;;; 8Kbyte of address space, so my first attempt (just to store 4 bytes per map)
+;;; came unstuck.
+;;; [NAC HACK 2016May07] may not need to store usr_mmu_map at all.. review later.
+;;;
+;;; Need to write these values any time we're changing the MMU mapping.. UNLESS
+;;; it's clear that the routine is subsequently going to restore a value that is
+;;; currently stored here.
+;;;
+;;; The values of 0-7 set here for _krn_mmu_map are used to initialise the MMU
+;;; mappings for the kernel. Don't change them!!
+;;; The values of 0-7 set here for _usr_mmu_map reflect how the user mappings
+;;; are set up when the kernel is started - so don't change them either!
+_krn_mmu_map
+       .db     0,1,2,3,4,5,6,7
+_usr_mmu_map
+       .db     0,1,2,3,4,5,6,7
+
+
+_trap_monitor:
+       orcc    #0x10
+       bra     _trap_monitor
+
+_trap_reboot:
+       orcc    #0x10           ; turn off interrupts
+        bra     _trap_reboot    ; [NAC HACK 2016May07] endless loop
+
+
+        lda    #0x38           ; put RAM block in memory
+       sta     0xffa8          ;
+       ;; copy reboot bounce routine down
+       ldx     #0              ;
+       ldu     #bounce@
+loop@  lda     ,u+
+       sta     ,x+
+       cmpu    #bounce_end@
+       bne     loop@
+       jmp     0               ;
+       ;; this code is PIC and gets copied down to
+       ;; low memory on reboot to bounce to the reset
+       ;; vector.
+bounce@
+       ;; [NAC HACK 2016May01] todo
+       lda     #0x06           ; reset GIME (map in internal 32k rom)
+       sta     0xff90
+       clr     0xff91
+       clr     0x72
+       jmp     [0xfffe]        ; jmp to reset vector
+bounce_end@
+
+
+
+;;; Turn off interrupts
+;;;    takes: nothing
+;;;    returns: B = original irq (cc) state
+_di:
+       tfr     cc,b            ; return the old irq state
+       orcc    #0x10
+       rts
+
+;;; Turn on interrupts
+;;;   takes: nothing
+;;;   returns: nothing
+_ei:
+       andcc   #0xef
+       rts
+
+;;; Restore interrupts to saved setting
+;;;   takes: B = saved state (as returned from _di )
+;;;   returns: nothing
+_irqrestore:                   ; B holds the data
+       tfr     b,cc
+       rts
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK
+; -----------------------------------------------------------------------------
+
+        .area .discard
+
+;;;  Stuff to initialize *before* hardware
+;;;    takes: nothing
+;;;    returns: nothing
+init_early:
+       ldx     #null_handler   ; [NAC HACK 2016Apr23] what's this for??
+       stx     1
+       lda     #0x7E
+       sta     0
+        rts
+
+
+
+;;; Initialize Hardware !
+;;;    takes: nothing
+;;;    returns: nothing
+init_hardware:
+       ;; [NAC HACK 2016Apr23] todo: size the memory. For now, assume 512K like coco3
+       ;; set system RAM size
+       ldd     #512
+       std     _ramsize
+       ldd     #512-64
+       std     _procmem
+
+;;; Enable timer interrupt
+        lda     #TIMER_ON
+        sta     TIMER
+
+;;; [NAC HACK 2016Apr23] coco3 at this point sets up physical blocks 0-7 for user mode.
+;;; ..which is the same mapping that is in use for kernel mode.
+
+;;; multicomp09 currently has map0 selected and blocks 0-7 mapped.
+;;; To match the coco3 set-up need to select blocks 0-7 for map1 then switch to map1.
+;;; Want to end with _krn_mmu_map and _usr_mmu_map and curr_tr all correct.
+;;;
+
+       ;; set up the map1 registers (MAPSEL=8..f) to use pages 0-7
+       ;; ..to match the pre-existing setup of map0.
+       ;; _krn_mmu_map is set up with the required values.
+       ;; while doing this, were careful to keep MMUADR_MAP1 *clear* because we are using
+       ;; map0 and don't want to switch the map yet.
+       lda     #(MMU_MAP0|8)   ; stay in map0, select 1st mapping register for map1
+       ldx     #MMUADR
+
+       ldy     #_krn_mmu_map
+       ldb     ,y+             ; page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=8, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=9, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=a, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=b, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=c, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=d, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=e, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=f, then write B to MMUDAT
+
+       ;; swap to map1
+       ;; the two labels generate entries in the map file that are useful
+       ;; when debugging: did we get past this step successfully.
+gomap1:        lda     #MMU_MAP1
+       sta     ,x
+       sta     curr_tr
+atmap1:
+
+
+       ;; Multicomp09 has RAM up at the hardware vector positions
+       ;; so we can write the addresses directly, 2 bytes per vector;
+       ;; no need for a jump op-code.
+       ldx     #0xfff2         ; address of SWI3 vector
+       ldy     #badswi_handler
+       sty     ,x++            ; SWI3 handler
+       sty     ,x++            ; SWI2 handler
+       ldy     #firq_handler
+       sty     ,x++            ; FIRQ handler
+       ldy     #interrupt_handler
+       sty     ,x++            ; IRQ  handler
+       ldy     #unix_syscall_entry
+       sty     ,x++            ; SWI  handler
+       ldy     #nmi_handler
+       sty     ,x++            ; NMI  handler
+
+       jsr     _devtty_init
+xinihw:        rts
+
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+       .area .common
+
+;;; Platform specific userspace setup
+;;;   We're going to borrow this to copy the common bank
+;;;   into the userspace too.
+;;;   takes: X = page table pointer
+;;;   returns: nothing
+;;; [NAC HACK 2016May01] but.. X (page table pointer) not used??
+;;; [NAC HACK 2016May01] this can be smaller and tidier. Point to MMUADR and do indexed access.
+;;; [NAC HACK 2016May07] pushes x,u but uses neither. Could be smaller if we used X to point
+;;; to MMUADR... not so!! the indirect access grabs the value of X from the stack.
+_program_vectors:
+       ;; copy the common section into user-space
+       pshs    x,u
+
+       ;; setup the null pointer / sentinal bytes in low process memory
+       lda     #(MMU_MAP1|8)
+       sta     MMUADR
+        ;; [NAC HACK 2016May07] access stacked X
+       lda     [0,s]        ; get process's blk address for address 0
+       sta     MMUDAT       ; put in our mmu ( at address 0 )
+       lda     #0x7E
+       sta     0
+
+       ;; restore the MMU mapping that we trampled on
+       lda     #(MMU_MAP1|8)
+       sta     MMUADR
+       lda     _krn_mmu_map
+       sta     MMUDAT
+
+       puls    pc,x,u       ; restore reg and return
+
+
+
+;;;  FIXME:  these interrupt handlers should prolly do something
+;;;  in the future.
+firq_handler:
+badswi_handler:
+       rti
+
+
+;;; Userspace mapping pages 7+  kernel mapping pages 3-5, first common 6
+;;; All registers preserved
+map_process_always:
+       pshs    x,y,u
+       ldx     #U_DATA__U_PAGE
+       jsr     map_process_2
+       puls    x,y,u,pc
+
+;;; Maps a page table into cpu space
+;;;   takes: X - pointer page table ( ptptr )
+;;;   returns: nothing
+;;;   modifies: nothing
+map_process:
+       cmpx    #0              ; is zero?
+       bne     map_process_2   ; no then map process; else: map the kernel
+       ;; !!! fall-through to below
+
+;;; Maps the Kernel into CPU space
+;;;   takes: nothing
+;;;   returns: nothing
+;;;   modifies: nothing
+;;;    Map in the kernel below the current common, all registers preserved
+map_kernel:
+       pshs    a
+       lda     #MMU_MAP1       ; flip to mmu map 1 (kernel)
+       sta     MMUADR
+       sta     curr_tr         ; save copy
+       puls    a,pc
+
+;;; User is in MAP0 with the top 8K as common
+;;; As the core code currently does 16K happily but not 8 we just pair
+;;; up pages
+
+;;; Maps a page table into the MMU
+;;;   takes: X = pointer to page table
+;;;   returns: nothing
+;;;   modifies: nothing
+map_process_2:
+       pshs    x,y,a,b
+
+       ;; first, copy entries from page table to usr_mmu_map
+       ldy     #_usr_mmu_map
+
+       lda     ,x+             ; get byte from page table
+       sta     ,y+             ; copy to usr_mmu_map
+       inca                    ; increment to get next 8K block
+       sta     ,y+             ; copy to usr_mmu_map
+
+       lda     ,x+             ; get byte from page table
+       sta     ,y+             ; copy to usr_mmu_map
+       inca                    ; increment to get next 8K block
+       sta     ,y+             ; copy to usr_mmu_map
+
+       lda     ,x+             ; get byte from page table
+       sta     ,y+             ; copy to usr_mmu_map
+       inca                    ; increment to get next 8K block
+       sta     ,y+             ; copy to usr_mmu_map
+
+       lda     ,x+             ; bank all but common memory
+       sta     ,y              ;
+
+       ;; now, update MMU with those values
+       lda     #(MMU_MAP1|0)   ; stay in map1, select mapsel=0
+       ldx     #MMUADR
+       ldy     #_usr_mmu_map   ; go back to start [NAC HACK 2016May07] or could use offsets above..
+
+       ldb     ,y+             ; page from usr_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=8, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from usr_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=9, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from usr_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=a, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from usr_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=b, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from usr_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=c, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from usr_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=d, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from usr_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=e, then write B to MMUDAT
+
+       lda     #MMU_MAP0
+       sta     ,x              ; new mapping goes live here
+       sta     curr_tr         ; and remember new TR setting
+       puls    x,y,a,b,pc      ; so had better include common!
+
+;;;
+;;;    Restore a saved mapping. We are guaranteed that we won't switch
+;;;    common copy between save and restore. Preserve all registers
+;;;
+;;;    We cheat somewhat. We have two mapping sets, so just remember
+;;;    which space we were in. Note: we could be in kernel in either
+;;;    space while doing user copies
+;;;
+map_restore:
+       pshs    a
+       lda     saved_tr
+       sta     curr_tr
+       sta     MMUADR
+       puls    a,pc
+
+;;; Save current mapping
+;;;   takes: nothing
+;;;   returns: nothing
+map_save:
+       pshs    a
+       lda     curr_tr
+       sta     saved_tr
+       puls    a,pc
+
+;;; Maps the memory for swap transfers
+;;;   takes: A = swap token ( a page no. )
+;;;   returns: nothing
+;;; [NAC HACK 2016May01] maps 16K into kernel space
+;;; [NAC HACK 2016May15] coco3 has this in .text instead of .common - is that correct?
+map_for_swap
+       ldb     curr_tr
+       orb     #8
+       stb     MMUADR          ; select first 8K in kernel mapping mapsel=8
+       sta     MMUDAT
+       incb
+       stb     MMUADR          ; select second 8K in kernel mapping mapsel=9
+       sta     MMUDAT
+       rts
+
+;;; multicomp09 HW registers
+;;; vdu/virtual UART
+UARTDAT        equ $ffd1
+UARTSTA        equ $ffd0
+
+;;;  Print a character to debugging
+;;;   takes: A = character
+;;;   returns: nothing
+outchar:
+       pshs    b,cc
+vdubiz  ldb     UARTSTA
+        bitb    #2
+        beq     vdubiz ; busy
+
+       sta     UARTDAT ; ready, send character
+       puls    b,cc,pc
diff --git a/Kernel/platform-multicomp09/sdc.s b/Kernel/platform-multicomp09/sdc.s
new file mode 100644 (file)
index 0000000..9183aea
--- /dev/null
@@ -0,0 +1,59 @@
+;;;
+;;;  Multicomp SDC Driver
+;;;
+
+;;; imported
+       .globl blk_op           ; blk operation arguments
+
+;;; exported
+       .globl _devsdc_write
+       .globl _devsdc_read
+
+
+;;; multicomp09 hw registers
+SDDATA         equ $FFD8
+SDCTL          equ $FFD9
+SDLBA0         equ $FFDA       ; not used here
+SDLBA1         equ $FFDB       ; not used here
+SDLBA2         equ $FFDC       ; not used here
+
+       section .common
+
+;;; Write 512 bytes to SDC
+;;; the address and command have already been loaded: this
+;;; only handles the data transfer.
+;;;
+;;; entry: x=data source
+;;; can corrupt: a, b, cc, x
+;;; must preserve: y, u
+_devsdc_write
+       pshs    y
+       ldy     #512            ; 512 bytes
+WrBiz  lda     SDCTL
+       cmpa    #$a0
+       bne     WrBiz           ; space not available
+       lda     ,x+             ; get byte from sector buffer
+       sta     SDDATA          ; store to SD
+       leay    -1,y
+       bne     WrBiz           ; next
+       puls    y,pc
+
+
+;;; Read 512 bytes from SDC
+;;; the address and command have already been loaded: this
+;;; only handles the data transfer.
+;;;
+;;; entry: x=data destination
+;;; can corrupt: a, b, cc, x
+;;; must preserve: y, u
+_devsdc_read
+       pshs    y
+       ldy     #512            ; 512 bytes
+RdBiz  lda     SDCTL
+       cmpa    #$e0
+       bne     RdBiz           ; byte not available
+       lda     SDDATA          ; get byte from SD
+       sta     ,x+             ; store byte in sector buffer
+       leay    -1,y
+       bne     RdBiz           ; next
+       puls    y,pc
diff --git a/Kernel/platform-multicomp09/target.mk b/Kernel/platform-multicomp09/target.mk
new file mode 100644 (file)
index 0000000..e9abbba
--- /dev/null
@@ -0,0 +1 @@
+export CPU = 6809
diff --git a/Kernel/platform-multicomp09/tricks.s b/Kernel/platform-multicomp09/tricks.s
new file mode 100644 (file)
index 0000000..f8b3b39
--- /dev/null
@@ -0,0 +1,326 @@
+;;;
+;;; CoCo3 ghoulish tricks (boo!) ported to multicomp09
+;;;
+
+
+;;; [NAC HACK 2016May01] want all the hw reg definitions in a common place.
+
+;;; multicomp09 HW registers
+MMUADR equ     $ffde
+MMUDAT equ     $ffdf
+
+;;; bit-fields
+MMUADR_ROMDIS  equ $80         ; 0 after reset, 1 when FUZIX boots. Leave at 1.
+MMUADR_TR      equ $40         ; 0 after reset, 0 when FUZIX boots. 0=map0, 1=map1
+MMUADR_MMUEN   equ $20         ; 0 after reset, 1 when FUZIX boots. Leave at 1.
+MMUADR_NMI     equ $10         ; 0 after reset, 0 when FUZIX boots. Do not write 1.
+MMUADR_MAPSEL  equ $0f         ; last-written value is UNDEFINED.
+;;; the only two useful values for the upper nibble
+MMU_MAP0       equ     (MMUADR_ROMDIS|MMUADR_MMUEN)
+MMU_MAP1       equ     (MMUADR_ROMDIS|MMUADR_MMUEN|MMUADR_TR)
+
+
+
+
+        .module tricks
+
+       ;; imported
+        .globl _newproc
+        .globl _chksigs
+        .globl _getproc
+        .globl _trap_monitor
+        .globl _krn_mmu_map
+        .globl _usr_mmu_map
+       .globl curr_tr
+
+       ;; exported
+        .globl _switchout
+        .globl _switchin
+        .globl _dofork
+       .globl _ramtop
+
+        include "kernel.def"
+        include "../kernel09.def"
+
+
+       .area .data
+;;; _ramtop cannot be in common, as this memory becomes per-process
+;;; when we add better udata handling.
+_ramtop:
+       .dw 0
+
+_swapstack
+       .dw     0
+       .dw     0
+
+fork_proc_ptr:
+       .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
+
+       .area .common
+
+;;; 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().
+;;;
+;;; FIXME: make sure we optimise the switch to self case higher up the stack!
+;;;
+;;; This function can have no arguments or auto variables.
+_switchout:
+       orcc    #0x10           ; irq off
+        jsr    _chksigs        ; check for signals
+
+        ;; save machine state
+        ldd    #0              ; return zero
+       ;; 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    ; save SP
+
+        jsr    _getproc        ; X = next process ptr
+        jsr    _switchin       ; and switch it in
+        ; we should never get here
+        jsr    _trap_monitor
+
+
+badswitchmsg:
+       .ascii  "_switchin: FAIL"
+        .db    13
+       .db     10
+       .db     0
+
+;;; Switch in a process
+;;;   takes: X = process
+;;;   returns: shouldn't
+_switchin:
+        orcc   #0x10           ; irq off
+
+       stx     _swapstack      ; save passed page table *
+
+;;; [NAC HACK 2016May03] this is only flipping in top 8K .. as is coco3.
+       lda     curr_tr         ; [NAC HACK 2016May07] I assume we're running in krn but
+                               ; I'm not 100% sure..
+       ora     #7              ; top 8K of usr map
+       sta     MMUADR
+
+       ;; flip in the newly choosen task's common page to usr map
+       lda     P_TAB__P_PAGE_OFFSET+3,x
+       inca
+       sta     MMUDAT
+       sta     _usr_mmu_map+7  ; keep the mirror in sync.
+
+       lda     curr_tr         ; [NAC HACK 2016May07] I assume we're running in krn but
+                               ; I'm not 100% sure..
+       ora     #$f             ; top 8K of krn map
+       sta     MMUADR
+
+       ;; flip in the newly choosen task's common page to krn map
+       lda     P_TAB__P_PAGE_OFFSET+3,x
+       inca
+       sta     MMUDAT
+       sta     _krn_mmu_map+7  ; keep the mirror in sync.
+
+       ;; --------- No Stack ! --------------
+
+        ; check u_data->u_ptab matches what we wanted
+       ldx     _swapstack
+       cmpx    U_DATA__U_PTAB
+        bne    switchinfail
+
+       lda     #P_RUNNING
+       sta     P_TAB__P_STATUS_OFFSET,x
+
+       ;; clear the 16 bit tick counter
+       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
+       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
+
+
+;;;
+;;;    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.
+        ;; When this process (the parent) is switched back in, it will be as if
+        ;; it returns with the value of the child's pid.
+       ;; Y has p->p_pid from above, the return value in the parent
+        pshs   x,y,u
+
+        ;; save kernel stack pointer -- when it comes back in the
+       ;; parent we'll be in
+       ;; _switchin which will immediately return (appearing to be _dofork()
+       ;; returning) and with HL (ie return code) containing the child PID.
+        ;; Hurray.
+        sts    U_DATA__U_SP
+
+        ;; now we're in a safe state for _switchin to return in the parent
+       ;; process.
+
+       ;; --------- we switch stack copies in this call -----------
+       jsr     fork_copy       ; copy 0x000 to udata.u_top and the
+                               ; uarea and return on the childs
+                               ; common
+       ;; We are now in the kernel child context
+
+        ;; now the copy operation is complete we can get rid of the stuff
+        ;; _switchin will be expecting from our copy of the stack.
+       puls    x
+
+        ldx    fork_proc_ptr   ; get forked process
+        jsr    _newproc        ; and set it up
+
+       ;; any calls to map process will now map the childs memory
+
+       ldx     #0              ; zero out process's tick counter
+       stx     _runticks
+        ;; in the child process, fork() returns zero.
+       ;;
+       ;; And we exit, with the kernel mapped, the child now being deemed
+       ;; to be the live uarea. The parent is frozen in time and space as
+       ;; if it had done a switchout().
+        puls y,u,pc
+
+
+;;; copy the process memory to the new process
+;;; and stash parent uarea to old bank
+fork_copy:
+       ;; calculate how many regular pages we need to copy
+       ldd     U_DATA__U_TOP          ; get size of process
+       tfr     d,y                    ; make copy of progtop
+       anda    #$3f                   ; mask off remainder
+       pshs    cc                     ; push onto stack ( remcc )
+       tfr     y,d                    ; get copy of progtop
+       rola                           ; make low bits = whole pages
+       rola                           ;
+       rola                           ;
+       anda    #$3                    ; bottom two bits are now whole pages
+       puls    cc                     ; get remainder's cc (  )
+       beq     skip@                  ; skip round-up if zero
+       inca                           ; round up
+       cmpa    #4                     ; is 4th bank copied?
+       pshs    cc,a                   ; and put on stack ( 4th?  no )
+       ;; copy parent's whole pages to child's
+skip@  ldx     fork_proc_ptr
+       leax    P_TAB__P_PAGE_OFFSET,x ; X = * new process page tables (dest)
+       ldu     #U_DATA__U_PAGE        ; parent process page tables (src)
+loop@  ldb     ,x+                    ; B = child's next page
+       lda     ,u+                    ; A = parent's next page
+       jsr     copybank               ; copy bank
+       dec     1,s                    ; bump counter
+       bne     loop@
+       ;; copy UDATA + common (if needed)
+       ldx     fork_proc_ptr          ; X = new process ptr
+       ldb     P_TAB__P_PAGE_OFFSET+3,x ;  B = child's UDATA/common page
+       puls    cc,a                   ; pull 4th? condition codes
+       beq     skip2@                 ; 4th bank already copied?
+       lda     U_DATA__U_PAGE+3         ;  A = parent's UDATA/common page
+       jsr     copybank                 ; copy it
+       ;; remap common page in MMU to new process
+skip2@ incb
+        ;;
+        ;;
+        ;;     stb     0xffaf
+        ;;     stb     0xffa7
+       lda     curr_tr         ; [NAC HACK 2016May07] I assume we're running in krn but
+                               ; I'm not 100% sure..
+       ora     #$f             ; top 8K of krn map
+        sta     MMUADR
+        stb     MMUDAT
+       stb     _krn_mmu_map+7  ; keep the mirror in sync.
+
+       lda     curr_tr         ; [NAC HACK 2016May07] I assume we're running in krn but
+                               ; I'm not 100% sure..
+       ora     #7              ; top 8K of usr map
+        sta     MMUADR
+        stb     MMUDAT
+       stb     _usr_mmu_map+7  ; keep the mirror in sync.
+       ;;
+       ; --- we are now on the stack copy, parent stack is locked away ---
+       rts     ; this stack is copied so safe to return on
+
+;;; Copy data from one bank to another
+;;;   takes: B = dest bank, A = src bank
+;;; uses low 32Kbyte of kernel address space as the "window" for this
+;;; use map 8,9 (0x0000-0x3fff) for dest, map 10,11 (0x4000-0x7fff) for source
+copybank
+       pshs    d,x,u,y         ; changing this will affect "ldb 1,s" below
+       ;; map in dest
+       ldx     #MMUADR         ; for storing
+       lda     #(MMU_MAP1+8)   ; mapsel=8, for dest, in B
+       std     ,x              ; mapsel=8, page in B
+       inca                    ; mapsel=9
+       incb                    ; adjacent page
+       std     ,x
+       ;; map in src
+       inca                    ; mapsel=a
+       ldb     0,s             ; stacked value of A into B
+       std     ,x
+       inca                    ; mapsel=b
+       incb                    ; adjacent page
+       std     ,x
+       ;; loop setup
+       ldx     #0              ; dest address
+       ldu     #0x4000         ; src address
+       ;; unrolled: 16 bytes at a time
+a@     ldd     ,u++
+       std     ,x++
+       ldd     ,u++
+       std     ,x++
+       ldd     ,u++
+       std     ,x++
+       ldd     ,u++
+       std     ,x++
+       ldd     ,u++
+       std     ,x++
+       ldd     ,u++
+       std     ,x++
+       ldd     ,u++
+       std     ,x++
+       ldd     ,u++
+       std     ,x++
+       cmpx    #0x4000         ; end of copy?
+       bne     a@              ; no repeat
+       ;; restore mmu
+       ldy     #_krn_mmu_map   ; kernel's mmu ptr.. for reading
+       ldx     #MMUADR         ; for storing
+       lda     #(MMU_MAP1+8)
+       ldb     ,y+             ; page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=8, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=9, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=a, then write B to MMUDAT
+       inca                    ; next mapsel
+       ldb     ,y+             ; next page from krn_mmu_map
+       std     ,x              ; Write A to MMUADR to set MAPSEL=b, then write B to MMUDAT
+       ;; return
+       puls    d,x,u,y,pc      ; return
diff --git a/Kernel/platform-multicomp09/ttydw.c b/Kernel/platform-multicomp09/ttydw.c
new file mode 100644 (file)
index 0000000..c0a7464
--- /dev/null
@@ -0,0 +1,283 @@
+/* Drivewire TTY's
+   
+   config.h defines:
+
+   DW_VSER_NUM - number of virtual serial ports ( 0-14 )
+   DW_VWIN_NUM - number of virtual "9server" windows ( 0-14 )
+   DW_MIN_OFF  - tty device minor offset 
+   
+   example:
+
+   #define DW_VSER_NUM 4    
+   #define DW_VWIN_NUM 4     
+   #define DW_MIN_OFF  3    
+   #define NUM_DEV_TTY 10
+
+   then this module will map drivewire tty's to the following minors:
+
+   minor no    description
+   0-2         not mapped.
+   3-6         Drivewire Virtual Serial Ports 0-3
+   7-10        Drivewire Virtual Window Ports 0-3
+
+   Modify your platform's devtty.c to call the following external
+   functions: (don't forget to include ttydw.h)
+
+   Make sure to up your NUM_DEV_TTY define in your config.h to cover
+   all the added tty's !
+
+   External/Public Functions:
+
+   void dw_val( uint8_t minor );
+   void dw_putc( uint8_t minor, unsigned char c );
+   void dw_vopen( uint8_t minor );
+   void dw_vclose( uint8_t minor );
+   int dw_carrier( uint8_t minor );
+   void dw_vpoll( );
+
+   How you switch to these driver function/methods from your devtty.c
+   is up to you...
+
+   dw_vpoll should be called from the timer interrupt.  To conserve
+   cpu time, this fuction only actually polls the DW server every .25
+   seconds
+
+   Finally, you will have to provide a platform dependent
+   "dw_transaction" routine/function:
+
+   int16_t dw_transaction( char *send, uint16_t scnt,
+                            char *recv, uint16_t rcnt )
+
+   where "send" is a data buffer to send
+         "scnt" is the size of the send buffer
+         "recv" is a data buffer for the received reply
+        "rcnt" is the size of the recv buffer
+   returns:  0 on no error
+            -1 on DW reception framing error (too slow!!)
+            -2 on not all bytes received
+
+
+   Usage Notes:
+   
+   You cannot stick a "sh" or "getty" on Virtual Serial ports
+   directly, as these Drivewire ports speak a higher level, but
+   simple, API for do TCP, DW control, MIDI etc.  However, the Window
+   ports, are just that... you can "sh", "getty", or whatever them
+   directly. (stick 'em in your inittab)
+
+   Unfortunately, the current Drivewire server does not issue a close
+   port upon the closing of the actual window!  It will, however, open
+   them on you server's machine upon Fuzix's opening of the port.
+
+   A stock DriveWire4 installation sets up the Virtual Serial Ports like this:
+
+   ports 1-13 : generic ports
+   ports 14 : direct connection to DriveWire's General MIDI
+*/
+
+#include <kernel.h>
+#include <printf.h>
+#include <tty.h>
+#include <devdw.h>
+
+#define DW_FASTWRITE 0x80
+#define DW_SETSTAT   0xC4
+#define DW_SERREAD   0x43
+#define DW_SERREADM  0x63
+#define DW_INIT      0x5a
+
+#define DW_VOPEN     0x29
+#define DW_VCLOSE    0x2A
+
+#define DW_NS_OFF    ( DW_MIN_OFF + DW_VSER_NUM )
+
+
+/* Internal Structure to represent state of DW ports */
+struct dw_in{
+       uint8_t flags;  /* flags for port */
+       /* outgoing buffer here! */
+};
+
+/* port flags */
+#define DW_FLG_OPEN 1     /* is port open? */
+
+
+/* and a table of the above structures */
+struct dw_in dwtab[ DW_VSER_NUM + DW_VWIN_NUM ];
+
+/* How many vsync ticks to wait until polling again, if
+   DW reports no data is waiting. */
+#define MAX_WAIT     TICKSPERSEC / 4
+
+int wait=MAX_WAIT;
+
+/* Number of ports open. IF zero then polling routine
+   will not poll */
+int open_ports=0;
+
+
+/* buffer for receiving multiple bytes from vport channels */
+char tbuf[256];
+
+
+int mini( int a, int b ){
+       if( a < b ) return a;
+       return b;
+}
+
+
+/* Gets dw_tab entry for given minor */
+struct dw_in *dw_gettab( uint8_t minor ){
+       return &dwtab[ minor - DW_MIN_OFF ] ;
+}
+
+/* Translates a DW port no. to a proper minor no */
+int dw_minor( uint8_t port ){
+       if( port >= 16 ) return port - 16 + DW_NS_OFF  ;
+       int ret = port + DW_MIN_OFF - 1 ;
+       return ret;
+                                       
+}
+
+
+/* Translates a Minor to a port no */
+int dw_port( uint8_t minor ){
+       int ret = minor - DW_MIN_OFF + 1;
+       if( minor >= DW_NS_OFF ) 
+               return  minor + 16 - DW_NS_OFF ;
+       return ret;
+}
+
+
+
+/* Put a character to the DriveWire port */
+void dw_putc( uint8_t minor, unsigned char c ){
+       unsigned char buf[2];
+       buf[0]=DW_FASTWRITE | dw_port( minor ) ;
+       buf[1]=c;
+       dw_transaction( buf, 2, NULL, 0 );
+}
+
+
+
+/* Open a DriveWire port */
+void dw_vopen( uint8_t minor ){
+       struct dw_in *p=dw_gettab( minor );
+       unsigned char buf[3];
+       buf[0]=DW_SETSTAT;
+       buf[1]=dw_port( minor );
+       buf[2]=DW_VOPEN;
+       if( ! ( p->flags & DW_FLG_OPEN ) ){
+               dw_transaction( buf, 3, NULL, 0 );
+               open_ports++;
+       }
+       p->flags |= DW_FLG_OPEN;
+}
+
+/* Close a DriveWire port */
+void dw_vclose( uint8_t minor){
+       struct dw_in *p=dw_gettab( minor );
+       unsigned char buf[3];
+       buf[0]=DW_SETSTAT;
+       buf[1]=dw_port( minor );
+       buf[2]=DW_VCLOSE;
+       if( p->flags & DW_FLG_OPEN ){
+               dw_transaction( buf, 3, NULL, 0 );
+       }
+}
+
+
+
+/* Return number of byte in tty's input queue */
+int qfree( uint8_t minor ){
+       queue_t *q = &ttyinq[minor];
+       return q->q_size - q->q_count;
+}
+
+
+
+/* Poll and add chars (if any) to input q
+ */
+void dw_vpoll( ){
+       unsigned char buf[2];
+       int i;
+       /* don't waste time polling of no ports are open*/
+       if( ! open_ports ) return ;
+       /* check ticks - don't poll until our delay is done */
+       if( --wait ) return;
+       /* up to four transactions at a poll */
+       for( i=0; i<4; i++){
+               buf[0]=DW_SERREAD;
+               dw_transaction( buf, 1, buf, 2 );
+               /* nothing waiting ? */
+               if( ! (buf[0] & 0x7f) ) {
+                       wait=MAX_WAIT;
+                       break;
+               }
+               /* VSER Channel single datum */
+               if( buf[0]<16 ){
+                       int minor=dw_minor( buf[0] );
+                       tty_inproc( minor, buf[1] );
+                       continue;
+               }
+               /* VSER Channel closed? */
+               if( buf[0] == 16 ){
+                       int minor=dw_minor( buf[1] );
+                       struct dw_in *p=dw_gettab( minor );
+                       if( p->flags & DW_FLG_OPEN ){
+                               p->flags &= ~DW_FLG_OPEN;
+                               open_ports--;
+                               if( ttydata[minor].users )
+                                       tty_carrier_drop( minor);
+                       }
+                       continue;
+               }
+               /* VSER channel multiple data */
+               if( buf[0] < 32 ){
+                       int i;
+                       unsigned char b[3];
+                       int min;
+                       int minor=dw_minor( buf[0]-17 );
+                       b[0]=DW_SERREADM;
+                       b[1]=buf[0]-17;
+                       min=mini( buf[1], qfree( minor ) );
+                       b[2]=min;
+                       if( !min ){
+                               wait=1;
+                               break;
+                       }
+                       dw_transaction( b,3,tbuf, min );
+                       for( i=0; i<min; i++){
+                               tty_inproc( minor, tbuf[i] );
+                       }
+                       wait=1;
+                       break;
+               }
+               /* VWIN channel single datum */
+               if( buf[0] < 144 ){
+                       int minor=dw_minor( buf[0]-48 );
+                       tty_inproc( minor, buf[1] );
+                       continue;
+               }
+               /* something we don't handle? */
+               kprintf("out of band data\n");
+       }
+}
+
+
+
+/* Tests DriveWire port for being open */
+/*   returns: 1 on open, 0 on closed */
+int dw_carrier( uint8_t minor ){
+       struct dw_in *p=dw_gettab( minor );
+       return p->flags & DW_FLG_OPEN ;
+}
+
+
+/* (re) Initializes DW */
+void dw_init( ){
+       char buf[2];
+       buf[0]=DW_INIT;
+       buf[1]=0x42;
+       dw_transaction( buf,2,buf,1 );
+}
diff --git a/Kernel/platform-multicomp09/ttydw.h b/Kernel/platform-multicomp09/ttydw.h
new file mode 100644 (file)
index 0000000..68e0e43
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __TTYDW_DOT_H__
+#define __TTYDW_DOT_H__
+
+void dw_putc( uint8_t minor, unsigned char c );
+void dw_vopen( uint8_t minor );
+void dw_vclose( uint8_t minor );
+int dw_carrier( uint8_t minor );
+void dw_vpoll( );
+#endif
diff --git a/Kernel/platform-multicomp09/usermem_gime.s b/Kernel/platform-multicomp09/usermem_gime.s
new file mode 100644 (file)
index 0000000..0036746
--- /dev/null
@@ -0,0 +1,342 @@
+       .module usermem
+
+;;;
+;;;    6809 copy to and from userspace via
+;;;     Color Computer 3 GIME mmu
+;;;
+
+;;; multicomp09 HW registers
+;;; [NAC HACK 2016May03] maybe I should define these all as offsets from IOBASE..
+;;; then index any of them from a single register.
+MMUADR equ     $ffde
+MMUDAT equ     $ffdf
+
+;;; bit-fields
+MMUADR_ROMDIS  equ $80         ; 0 after reset, 1 when FUZIX boots. Leave at 1.
+MMUADR_TR      equ $40         ; 0 after reset, 0 when FUZIX boots. 0=map0, 1=map1
+MMUADR_MMUEN   equ $20         ; 0 after reset, 1 when FUZIX boots. Leave at 1.
+MMUADR_NMI     equ $10         ; 0 after reset, 0 when FUZIX boots. Do not write 1.
+MMUADR_MAPSEL  equ $0f         ; last-written value is UNDEFINED.
+;;; the only two useful values for the upper nibble
+MMU_MAP0       equ     (MMUADR_ROMDIS|MMUADR_MMUEN)
+MMU_MAP1       equ     (MMUADR_ROMDIS|MMUADR_MMUEN|MMUADR_TR)
+
+       include "kernel.def"
+        include "../kernel09.def"
+
+       ;; window xfer treshhold - any user/krn space xtfers
+       ;; bigger than this will be routed to the banking/windowing
+       ;; transfers, rather than the original slower routines
+WINTR  equ     256             ; window xfer threshold
+
+       ; exported
+       .globl  __ugetc
+       .globl  __ugetw
+       .globl  __uget
+       .globl  __ugets
+       .globl  __uputc
+       .globl  __uputw
+       .globl  __uput
+       .globl  __uzero
+
+       ; imported
+       .globl  map_process_always
+       .globl  map_kernel
+       .globl _krn_mmu_map
+
+       .area   .common
+
+__ugetc:
+       pshs    cc      ; save IRQ state
+       orcc    #0x10
+       jsr     map_process_always
+       ldb     ,x
+       jsr     map_kernel
+       clra
+       tfr     d,x
+       puls    cc,pc   ; back and return
+
+__ugetw:
+       pshs    cc
+       orcc    #0x10
+       jsr     map_process_always
+       ldx     ,x
+       jsr     map_kernel
+       puls    cc,pc
+
+__uget:
+       pshs    u,y,cc
+       orcc    #0x10
+       ldd     9,s             ; save count ptr
+       cmpd    #WINTR          ; are we smaller than threshold?
+       blo     __uget1         ; yes then goto simple tranfer
+       std     count   
+       ldd     7,s             ; save kernel address
+       std     krn
+       stx     usr             ; save kernel ptr
+       clr     way             ; xfer direction: to kernel
+       com     way             ; 
+       jmp     uxfer
+       
+__uget1:
+       ldu     7,s     ; user address
+       ldy     9,s     ; count
+ugetl:
+       jsr     map_process_always
+       lda     ,x+
+       jsr     map_kernel
+       sta     ,u+
+       leay    -1,y
+       bne     ugetl
+       ldx     #0
+       puls    u,y,cc,pc
+
+__ugets:
+       pshs    u,y,cc
+       ldu     7,s     ; user address
+       ldy     9,s     ; count
+       orcc    #0x10
+ugetsl:
+       jsr     map_process_always
+       lda     ,x+
+       beq     ugetse
+       jsr     map_kernel
+       sta     ,u+
+       leay    -1,y
+       bne     ugetsl
+       ldx     #0xffff ; unterminated - error
+       lda     #0
+       sta     -1,u    ; force termination
+       puls    u,y,cc,pc
+ugetse:
+       jsr     map_kernel
+       sta     ,u
+       ldx     #0
+       puls    u,y,cc,pc
+
+
+__uputc:
+       pshs    cc
+       orcc    #0x10
+       ldd     3,s
+       jsr     map_process_always
+       exg     d,x
+       stb     ,x
+       jsr     map_kernel
+       ldx     #0
+       puls    cc,pc
+
+__uputw:
+       pshs    cc
+       orcc    #0x10
+       ldd     3,s
+       jsr     map_process_always
+       exg     d,x
+       std     ,x
+       jsr     map_kernel
+       ldx     #0
+       puls    cc,pc
+
+
+__uput:
+       pshs    u,y,cc
+       orcc    #0x10
+       ldd     9,s             ; save count
+       cmpd    #WINTR          ; are we smaller than threshold?
+       blo     __uput1         ; yes then do old routine
+       std     count
+       ldd     7,s             ; save user address
+       std     usr
+       stx     krn             ; save krnel address
+       clr     way             ; xfer direction: to userpace
+       jmp     uxfer           ; go transfer
+
+       
+;;;    X = source, user, size on stack
+__uput1:
+       ldu     7,s             ; user address
+       ldy     9,s             ; count
+uputl:
+       lda     ,x+
+       jsr     map_process_always
+       sta     ,u+
+       jsr     map_kernel
+       leay    -1,y
+       bne     uputl
+       ldx     #0
+       puls    u,y,cc,pc
+
+__uzero:
+       pshs    y,cc
+       lda     #0
+       ldy     5,s
+       orcc    #0x10
+       jsr     map_process_always
+uzloop:
+       sta     ,x+
+       leay    -1,y
+       bne     uzloop
+       jsr     map_kernel
+       ldx     #0
+       puls    y,cc,pc
+
+
+
+icount .dw     0               ; no of bytes to copy without changing mmu
+count  .dw     0               ; total no of bytes left to copy
+krn    .dw     0               ; kernel address
+usr    .dw     0               ; user address
+way    .db     0               ; which way to xtfer:
+                               ;   0  = to userspace
+                               ;   !0 = to kernel
+
+;;; find max no of byte copyable in 16k bank window
+;;;   takes: D = address
+;;;   returns: max copyable pushed onto U stack
+;;;   modifies: D
+max:   anda    #$3f    
+       pshu    d
+       ldd     #$4000
+       subd    ,u
+       std     ,u
+       rts
+       
+;;; find the minimum of two values
+;;;   takes: two 16 bit values on U stack
+;;;   returns: least of the two popped values on U stack
+;;;   modifies: D
+min:
+       pulu    d
+       cmpd    ,u
+       blo     a@
+       rts
+a@     std     ,u
+       rts
+
+;;;  A faster user-kernel space copy
+;;;    takes: usr = usr space ptr
+;;;    takes: krn = kernel space ptr
+;;;    takes: way = copy direction
+;;;    takes: count = number of bytes to xfer
+;;;
+;;; While the simple uput and uget remap mmu each byte of transfer,
+;;; this routine pre-calculates how many bytes we can copy without
+;;; remapping the mmu, copies those bytes, *then* re-computes the
+;;; mmu banking and repeats until all bytes are transfered.
+;;;
+;;; assume: map0 (kernel map) is in use.
+;;; "borrow" the low 4 kernel MMU mappings; treating them as 2, 16K
+;;; windows, map kernel space into the lower and user space into the
+;;; upper, then copy from one to the other. At the end of the routine,
+;;; restore the kernel MMU mappings from the values stored in
+;;; _krn_mmu_map.
+uxfer:
+       ;; make a data stack
+       leau    -8,s            ; allow 4 levels in S
+       ;; calc max src
+b@     ldd     krn             ; calc max byte copyable from source
+       bsr     max             ; and push it onto data stack
+       ;; calc max dest
+       ldd     usr             ; calc max byte copyable from destination
+       bsr     max             ; and push it onto data stack
+       ;; compare count to all
+       ldd     count           ; push total bytes to copy onto data stack
+       pshu    d               ;
+       bsr     min             ; find the minimum of all three
+       bsr     min             ;
+       pulu    d               ; get the result from data stack
+       std     icount          ;
+       ;; set kernel bank window (at CPU addr 0x0000)
+       ldy     #MMUADR         ; Y = beginning of mmu.. for storing
+       lda     krn             ; get kernel ptr
+       rola                    ; rotate top two bits
+       rola                    ; down to the bottom
+       rola                    ;
+       lsla                    ; multiply by two
+       anda    #7              ; clean off extra bits
+       ldx     #_krn_mmu_map   ; kernel's mmu ptr.. for reading
+       ;;
+       ;;
+       ;;
+       ldb     #(MMU_MAP1|8)
+       stb     0,y             ; MMUADR set mapsel=8
+       ;;
+       ldb     a,x             ; get mmu entry
+       stb     1,y             ; MMUDAT store in mmu
+        ;;
+       ;;
+       ldb     #(MMU_MAP1|9)
+       stb     0,y             ; MMUADR set mapsel=9
+       ;;
+        inca                    ; increment index [NAC HACK 2016May07] or a,x+ above and omit this??
+       ldb     a,x             ; get next mmu entry
+       stb     1,y             ; MMUDAT store in mmu
+       ldd     krn             ; get kernel ptr
+       anda    #$3f            ; get 16k offset
+       pshs    u               ; save data stack
+       tfr     d,u             ; U = ptr to window to kernel
+       ;; set user bank (at CPU addr 0x4000)
+       lda     usr             ; get userspace ptr
+       rola                    ; rotate top three bits
+       rola                    ; down to the bottom of A
+       rola                    ;
+       anda    #3              ; mask off junk [NAC HACK 2016May03] why not 7??
+       ldx     #U_DATA__U_PAGE ; X = ptr to user page table
+       ;;
+       ldb     #(MMU_MAP1|10)
+       stb     0,y             ; MMUADR set mapsel=10
+       ;;
+       ldb     a,x             ; B = page table entry
+       stb     1,y             ; MMUDAT store in MMU
+       incb                    ; increment for next page no [NAC HACK 2016May03] coz we use page pairs?
+       ;;
+       lda     #(MMU_MAP1|11)  ; use A because B is busy this time
+       sta     0,y             ; MMUADR set mapsel=11
+       ;;
+       stb     1,y             ; MMUDAT store in mmu
+       ldd     usr             ; get destination
+       anda    #$3f            ; get 16k offset
+       addd    #0x4000         ; add window base
+       tfr     d,x             ; X = ptr to window to user
+       ;; inner loop
+       ldy     icount          ; Y = count
+       tst     way             ; which way are we copying?
+       beq     a@              ; to userspace no dest/src swapping
+       exg     u,x             ; flip to copy the other way....
+a@     lda     ,u+             ; get a byte
+       sta     ,x+             ; store it
+       leay    -1,y            ; bump counter
+       bne     a@              ; loop if not done
+       ;; end inner loop
+       puls    u               ; restore data stack
+       ;; clean up kernel mmu's for next mapping or returning
+       ldx     #MMUADR
+       ldy     #_krn_mmu_map
+       lda     #(MMU_MAP1|8)
+        ldb     ,y+             ; page from _krn_mmu_map
+        std     ,x              ; Write A to MMUADR to set MAPSEL=8, then write B to MMUDAT
+        inca                    ; next mapsel
+        ldb     ,y+             ; next page from _krn_mmu_map
+        std     ,x              ; Write A to MMUADR to set MAPSEL=9, then write B to MMUDAT
+        inca                    ; next mapsel
+        ldb     ,y+             ; next page from _krn_mmu_map
+        std     ,x              ; Write A to MMUADR to set MAPSEL=a, then write B to MMUDAT
+        inca                    ; next mapsel
+        ldb     ,y+             ; next page from _krn_mmu_map
+        std     ,x              ; Write A to MMUADR to set MAPSEL=b, then write B to MMUDAT
+       ;;
+       ;; increment out loop variables
+       ldd     krn             ; add this iteration's byte count
+       addd    icount          ; from source address
+       std     krn             ;
+       ldd     usr             ; add this iteration's byte count
+       addd    icount          ; from destination address
+       std     usr             ;
+       ldd     count           ; subtract this iteration's byte count
+       subd    icount          ; from total byte to copy
+       std     count
+       lbne    b@              ; if bytes left to copy then repeat
+       ;; return
+       ldx     #0              ; return #0 - success
+       puls    u,y,cc,pc       ; return
+
diff --git a/Kernel/platform-multicomp09/videoll.s b/Kernel/platform-multicomp09/videoll.s
new file mode 100644 (file)
index 0000000..036e1f7
--- /dev/null
@@ -0,0 +1,40 @@
+;;;
+;;;  Low-Level Video Routines
+;;;
+;;;
+;;; [NAC HACK 2016Apr23] this also has memset and memcpy .. which should really be elsewhere
+;;; because I don't need the other stuff
+       .module videoll
+
+
+       ;; exported
+       .globl  _memset
+       .globl  _memcpy
+
+       include "kernel.def"
+       include "../kernel09.def"
+
+       .area .video
+
+;;;   void *memset(void *d, int c, size_t sz)
+_memset:
+       pshs    x,y
+       ldb     7,s
+       ldy     8,s
+a@     stb     ,x+
+       leay    -1,y
+       bne     a@
+       puls    x,y,pc
+
+
+
+;;;   void *memcpy(void *d, const void *s, size_t sz)
+_memcpy:
+       pshs    x,y,u
+       ldu     8,s
+       ldy     10,s
+a@     ldb     ,u+
+       stb     ,x+
+       leay    -1,y
+       bne     a@
+       puls    x,y,u,pc