--- /dev/null
+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
-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
#export TARGET = coco2
#export TARGET = coco3
#export TARGET = dragon-nx32
+#export TARGET = multicomp09
#export TARGET = micropack
#export TARGET = msx1
#export TARGET = msx2
- 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
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
- 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
- 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.
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
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)
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.
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
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
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)
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)
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.
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)
#include <kdata.h>
#include <printf.h>
#include <blkdev.h>
+#include <config.h>
typedef struct {
uint8_t status;
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)
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;
}
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){
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 */
--- /dev/null
+
+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
--- /dev/null
+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.
+
+
--- /dev/null
+;;;
+;;; 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
--- /dev/null
+
+# 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
+
--- /dev/null
+;
+; 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
--- /dev/null
+/* 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 */
--- /dev/null
+;;;
+;;; 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
+
--- /dev/null
+#ifndef __DEVICE_DOT_H__
+#define __DEVICE_DOT_H__
+
+
+#endif /* __DEVICE_DOT_H__ */
--- /dev/null
+#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
+}
+
--- /dev/null
+/*
+
+ 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");
+}
+
--- /dev/null
+
+
+void devsdc_read( char *addr );
+void devsdc_write( char *addr );
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+;;
+;; 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"
+
--- /dev/null
+********************************************************************
+*
+* 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
+
--- /dev/null
+*******************************************************
+*
+* 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
+
--- /dev/null
+*******************************************************
+*
+* 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
+
--- /dev/null
+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
--- /dev/null
+;;; 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
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+;;;
+;;; 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
--- /dev/null
+;;;
+;;; 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
--- /dev/null
+export CPU = 6809
--- /dev/null
+;;;
+;;; 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
--- /dev/null
+/* 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 );
+}
--- /dev/null
+#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
--- /dev/null
+ .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
+
--- /dev/null
+;;;
+;;; 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