--- /dev/null
+
+CSRCS = devlpr.c devtty.c devfd.c devdw.c
+CSRCS += devices.c main.c libc.c
+
+ASRCS = crt0.s dragon.s mem-nx32.s
+ASRCS += tricks.s commonmem.s usermem_sam.s floppy.s drivewire.s
+
+COBJS = $(CSRCS:.c=$(BINEXT))
+AOBJS = $(ASRCS:.s=$(BINEXT))
+OBJS = $(COBJS) $(AOBJS)
+
+JUNK = $(CSRCS:.c=.o) $(ASRCS:.s=.o)
+
+all: $(OBJS)
+
+$(COBJS): %$(BINEXT): %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG2) -c $<
+
+$(AOBJS): %$(BINEXT): %.s
+ $(CROSS_AS) $(ASOPTS) $< -o $*.o
+
+clean:
+ rm -f $(OBJS) $(JUNK)
+
+image:
+ $(CROSS_LD) -o ../fuzix.bin --map=../fuzix.map --script=fuzix.link \
+ crt0.o commonmem.o usermem_sam.o \
+ dragon.o mem-nx32.o ../bankfixed.o \
+ ../start.o ../version.o ../lowlevel-6809.o \
+ tricks.o main.o ../timer.o ../kdata.o devfd.o floppy.o devices.o \
+ drivewire.o devdw.o \
+ ../devio.o ../filesys.o ../process.o ../inode.o ../syscall_fs.o \
+ ../syscall_proc.o ../syscall_other.o ../mm.o \
+ ../tty.o ../devsys.o ../usermem.o ../syscall_fs2.o ../syscall_exec16.o \
+ devlpr.o devtty.o libc.o ../vt.o
--- /dev/null
+Dragon (32 or 64) + external memory cartridge
+
+This port is for running FUZIX on real Dragons using an external
+memory cartridge which is being prototyped.
+
+The cartridge can have from 2 to 256 banks of 32KB each. It will
+map the active bank at 0x8000-0xFEFF and use the EXMEM (SLENB) signal to
+hide the internal ROMs. Writing the bank number to FFBF selects the
+bank and activates the cartridge. Writing to FFBE deactivates the
+cartridge. It is only active if the SAM is in map type 0.
+
+The port reserves the internal RAM at 0-0x7FFF plus bank 0 for the kernel.
+
+The port can use DriveWire to mount a root filesystem, and is
+able to execute init. syscalls and fork have not been tested.
+Swapping out to disk does not work yet.
+
+There is not much 6809 userspace work done.
+
--- /dev/null
+;
+ .module commonmem
+
+ ; exported symbols
+ .globl _ub
+ .globl _udata
+ .globl kstack_top
+ .globl istack_top
+ .globl istack_switched_sp
+
+ .area .udata
+
+_ub: ; first 512 bytes: starts with struct u_block, with the kernel stack working down from above
+_udata:
+kstack_base:
+ zmb 512
+kstack_top:
+
+ ; next 256 bytes: 254 byte interrupt stack, then 2 byte saved stack pointer
+istack_base:
+ zmb 254
+istack_top:
+istack_switched_sp: .dw 0
--- /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
+/* Pure swap */
+#undef CONFIG_SWAP_ONLY
+/* Banked Kernel: need to fix GCC first */
+#define CONFIG_BANK_FIXED
+#define MAX_MAPS 4
+#define MAP_SIZE 0x7C00U
+#define CONFIG_BANKS 1
+/* And swapping */
+#undef SWAPDEV
+
+/* Video terminal, not a serial tty */
+#define CONFIG_VT
+/* Simple text mode */
+#define CONFIG_VT_SIMPLE
+/* Vt definitions */
+#define VT_BASE (uint8_t *)0x0400 /* Default video text mode base */
+#define VT_WIDTH 32
+#define VT_HEIGHT 16
+#define VT_RIGHT 31
+#define VT_BOTTOM 15
+#define VT_INITIAL_LINE 4
+
+extern unsigned char vt_mangle_6847(unsigned char c);
+#define VT_MAP_CHAR(x) vt_mangle_6847(x)
+
+#define TICKSPERSEC 50 /* Ticks per second */
+/* FIXME: This will move once we put the display in the kernel bank and
+ sort the banker out */
+#define PROGBASE 0x8000 /* also data base */
+#define PROGLOAD 0x8000 /* also data base */
+#define PROGTOP 0xFC00 /* Top of program */
+
+#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 */
+
+/* We need a tidier way to do this from the loader */
+#define CMDLINE NULL /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 2
+#define NDEVS 2 /* Devices 0..NDEVS-1 are capable of being mounted */
+ /* (add new mountable devices to beginning area.) */
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+#define NBUFS 6 /* Number of block buffers */
+#define NMOUNTS 2 /* Number of mounts at a time */
--- /dev/null
+ ; exported
+ .globl start
+
+ ; imported symbols
+ .globl _fuzix_main
+ .globl init_early
+ .globl init_hardware
+ .globl kstack_top
+
+ ; startup code
+ .area .start
+
+start:
+ ; text may be in memory bank 0
+ jsr map_kernel
+ jmp main
+
+ .area .text
+
+main: orcc #0x10 ; interrupts definitely off
+ lds #kstack_top
+
+ ldx #__sectionbase_.bss__
+ ldy #__sectionlen_.bss__
+ clra
+bss_wipe: sta ,x+
+ leay -1,y
+ bne bss_wipe
+
+ jsr init_early
+ jsr init_hardware
+ jsr _fuzix_main
+ orcc #0x10
+stop: bra stop
+
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devdw.h>
+
+#define MAX_DW 4 /* can be 255 */
+
+#define DW_READ 0
+#define DW_WRITE 1
+
+static uint8_t dw_tab[MAX_DW];
+
+/*
+ * Block device glue for DriveWire
+ *
+ * DriveWire uses 256 byte sector transfers
+ */
+
+static int dw_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ blkno_t block;
+ uint16_t dptr;
+ int ct = 0;
+ int tries;
+ uint8_t err;
+ uint8_t *driveptr = dw_tab + minor;
+ uint8_t cmd[5];
+
+ if(rawflag)
+ goto bad2;
+
+ dptr = (uint16_t)udata.u_buf->bf_data;
+ block = udata.u_buf->bf_blk;
+
+// kprintf("Issue command: drive %d\n", minor);
+ /* maybe mimicking floppy driver more than needed? */
+ cmd[0] = is_read ? DW_READ : DW_WRITE;
+ cmd[1] = block >> 7; /* 2 sectors per block */
+ cmd[2] = (block << 1) & 0xFF;
+ cmd[3] = dptr >> 8;
+ cmd[4] = dptr & 0xFF;
+ *driveptr = minor; /* pass minor (drive number) through here for now */
+
+ while (ct < 2) {
+ for (tries = 0; tries < 4 ; tries++) {
+ // kprintf("dw_operation on block %d ct %d\n", block, ct);
+ err = dw_operation(cmd, driveptr);
+ if (err == 0)
+ break;
+ if (tries > 1)
+ dw_reset(driveptr);
+ }
+ /* FIXME: should we try the other half and then bail out ? */
+ if (tries == 3)
+ goto bad;
+ cmd[3]++; /* Move on 256 bytes in the buffer */
+ cmd[2]++; /* Next sector for 2nd block */
+ ct++;
+ }
+ return 1;
+bad:
+ kprintf("dw%d: error %x\n", minor, err);
+bad2:
+ udata.u_error = EIO;
+ return -1;
+}
+
+/* FIXME: for bit-banger transport (not Becker) we should set up
+ the PIA at some point too */
+
+int dw_open(uint8_t minor, uint16_t flag)
+{
+ if(minor >= MAX_DW) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return 0;
+}
+
+int dw_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ return dw_transfer(minor, true, rawflag);
+}
+
+int dw_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ return dw_transfer(minor, false, rawflag);
+}
+
--- /dev/null
+#ifndef __DEVDW_DOT_H__
+#define __DEVDW_DOT_H__
+
+/* public interface */
+int dw_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int dw_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int dw_open(uint8_t minor, uint16_t flag);
+
+/* low level interface */
+uint8_t dw_reset(uint8_t *drive);
+uint8_t dw_operation(uint8_t *cmd, uint8_t *drive);
+
+#endif /* __DEVDW_DOT_H__ */
+
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+
+#define MAX_FD 4
+
+#define OPDIR_NONE 0
+#define OPDIR_READ 1
+#define OPDIR_WRITE 2
+
+#define FD_READ 0x88 /* 2797 needs 0x88, 1797 needs 0x80 */
+#define FD_WRITE 0xA8 /* Likewise A8 v A0 */
+
+static uint8_t motorct;
+static uint8_t fd_selected = 0xFF;
+static uint8_t fd_tab[MAX_FD];
+
+static void fd_motor_busy(void)
+{
+ motorct++;
+}
+
+static void fd_motor_idle(void)
+{
+ motorct--;
+ // if (motorct == 0) ... start timer */
+}
+
+static void fd_motor_timeout(void)
+{
+ fd_selected = 0xff;
+ fd_motor_off();
+}
+
+/*
+ * We only support normal block I/O because otherwise we'd need
+ * bounce buffers - which would make it just as pointless!
+ *
+ * The Dragon and COCO have 18 x 256 byte sectors per track. We
+ * use them in pairs. We assume an even sectors per track. This is fine
+ * for our usage but would break for single density media.
+ */
+
+/* static uint8_t selmap[4] = { 0x01, 0x02, 0x04, 0x40 }; - COCO */
+static uint8_t selmap[4] = {0x00, 0x01, 0x02, 0x03 };
+
+static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ blkno_t block;
+ uint16_t dptr;
+ int ct = 0;
+ int tries;
+ uint8_t err;
+ uint8_t *driveptr = fd_tab + minor;
+ uint8_t cmd[6];
+
+ if(rawflag)
+ goto bad2;
+
+ fd_motor_busy(); /* Touch the motor timer first so we don't
+ go and turn it off as we are doing this */
+ if (fd_selected != minor) {
+ uint8_t err = fd_motor_on(selmap[minor]);
+ if (err)
+ goto bad;
+ }
+
+ dptr = (uint16_t)udata.u_buf->bf_data;
+ block = udata.u_buf->bf_blk;
+
+// kprintf("Issue command: drive %d\n", minor);
+ cmd[0] = is_read ? FD_READ : FD_WRITE;
+ cmd[1] = block / 9; /* 2 sectors per block */
+ cmd[2] = ((block % 9) << 1) + 1; /*eww.. */
+ cmd[3] = is_read ? OPDIR_READ: OPDIR_WRITE;
+ cmd[4] = dptr >> 8;
+ cmd[5] = dptr & 0xFF;
+
+ while (ct < 2) {
+ for (tries = 0; tries < 4 ; tries++) {
+ err = fd_operation(cmd, driveptr);
+ if (err == 0)
+ break;
+ if (tries > 1)
+ fd_reset(driveptr);
+ }
+ /* FIXME: should we try the other half and then bale out ? */
+ if (tries == 3)
+ goto bad;
+ cmd[4]++; /* Move on 256 bytes in the buffer */
+ cmd[2]++; /* Next sector for 2nd block */
+ ct++;
+ }
+ fd_motor_idle();
+ return 1;
+bad:
+ kprintf("fd%d: error %x\n", minor, err);
+bad2:
+ fd_motor_idle();
+ udata.u_error = EIO;
+ return -1;
+
+}
+
+int fd_open(uint8_t minor, uint16_t flag)
+{
+ if(minor >= MAX_FD) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return 0;
+}
+
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ return fd_transfer(minor, true, rawflag);
+}
+
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ return fd_transfer(minor, false, rawflag);
+}
+
--- /dev/null
+#ifndef __DEVFD_DOT_H__
+#define __DEVFD_DOT_H__
+
+/* public interface */
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int fd_open(uint8_t minor, uint16_t flag);
+
+/* low level interface */
+uint8_t fd_reset(uint8_t *drive);
+uint8_t fd_operation(uint8_t *cmd, uint8_t *drive);
+uint8_t fd_motor_on(uint8_t drive);
+uint8_t fd_motor_off(void);
+
+#endif /* __DEVFD_DOT_H__ */
+
--- /dev/null
+#ifndef __DEVICE_DOT_H__
+#define __DEVICE_DOT_H__
+
+extern void mod_control(uint8_t set, uint8_t clr);
+
+#endif /* __DEVICE_DOT_H__ */
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devfd.h>
+#include <devdw.h>
+#include <devsys.h>
+#include <devlpr.h>
+#include <tty.h>
+#include <vt.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+// minor open close read write ioctl
+// -----------------------------------------------------------------
+ /* 0: /dev/fd Floppy disc block devices */
+ { fd_open, no_close, fd_read, fd_write, no_ioctl },
+ /* 1: /dev/hd Hard disc block devices (absent) */
+ { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 2: /dev/tty TTY devices */
+ { tty_open, tty_close, tty_read, tty_write, vt_ioctl },
+ /* 3: /dev/lpr Printer devices */
+ { lpr_open, lpr_close, no_rdwr, lpr_write, no_ioctl },
+ /* 4: /dev/mem etc System devices (one offs) */
+ { no_open, no_close, sys_read, sys_write, sys_ioctl },
+ /* Pack to 7 with nxio if adding private devices and start at 8 */
+ { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl },
+ /* 8: /dev/dw DriveWire remote disk images */
+ { dw_open, no_close, dw_read, dw_write, no_ioctl },
+};
+
+bool validdev(uint16_t dev)
+{
+ /* This is a bit uglier than needed but the right hand side is
+ a constant this way */
+ if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255)
+ return false;
+ else
+ return true;
+}
+void device_init(void)
+{
+}
+
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <device.h>
+#include <devlpr.h>
+
+/* random test places */
+uint8_t *lpstat = (uint8_t *)0xFF00;
+uint8_t *lpdata = (uint8_t *)0xFF01;
+
+int lpr_open(uint8_t minor, uint16_t flag)
+{
+ minor;
+ flag; // shut up compiler
+ return 0;
+}
+
+int lpr_close(uint8_t minor)
+{
+ minor; // shut up compiler
+ return 0;
+}
+
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ int c = udata.u_count;
+ char *p = udata.u_base;
+ uint16_t ct;
+
+ minor;
+ rawflag;
+ flag; // shut up compiler
+
+ while (c-- > 0) {
+ ct = 0;
+
+ /* Try and balance polling and sleeping */
+ while (*lpstat & 2) {
+ ct++;
+ if (ct == 10000) {
+ udata.u_ptab->p_timeout = 3;
+ if (psleep_flags(NULL, flag)) {
+ if (udata.u_count)
+ udata.u_error = 0;
+ return udata.u_count;
+ }
+ ct = 0;
+ }
+ }
+ /* Data */
+ *lpdata = ugetc(p++);
+ }
+ return udata.u_count;
+}
--- /dev/null
+#ifndef __DEVLPR_DOT_H__
+#define __DEVLPR_DOT_H__
+
+int lpr_open(uint8_t minor, uint16_t flag);
+int lpr_close(uint8_t minor);
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+
+#endif
--- /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>
+
+#undef DEBUG /* UNdefine to delete debug code sequences */
+
+uint8_t *uart_data = (uint8_t *)0xFF04; /* ACIA data */
+uint8_t *uart_status = (uint8_t *)0xFF05; /* ACIA status */
+uint8_t *uart_command = (uint8_t *)0xFF06; /* ACIA command */
+uint8_t *uart_control = (uint8_t *)0xFF07; /* ACIA control */
+
+char tbuf1[TTYSIZ];
+char tbuf2[TTYSIZ];
+
+struct s_queue ttyinq[NUM_DEV_TTY + 1] = { /* ttyinq[0] is never used */
+ {NULL, NULL, NULL, 0, 0, 0},
+ {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2},
+ {tbuf2, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ / 2}
+};
+
+/* tty1 is the screen tty2 is the serial port */
+
+/* Output for the system console (kprintf etc) */
+void kputchar(char c)
+{
+ if (c == '\n')
+ tty_putc(1, '\r');
+ tty_putc(1, c);
+}
+
+ttyready_t tty_writeready(uint8_t minor)
+{
+ uint8_t c;
+ if (minor == 1)
+ return TTY_READY_NOW;
+ c = *uart_status;
+ return (c & 16) ? TTY_READY_NOW : TTY_READY_SOON; /* TX DATA empty */
+}
+
+/* For DragonPlus we should perhaps support both monitors 8) */
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ if (minor == 1) {
+ vtoutput(&c, 1);
+ } else
+ *uart_data = c; /* Data */
+}
+
+void tty_sleeping(uint8_t minor)
+{
+ used(minor);
+}
+
+void tty_setup(uint8_t minor)
+{
+ if (minor == 2) {
+ /* FIXME: do proper mode setting */
+ *uart_command = 0x01; /* DTR high, IRQ enabled, TX irq disabled 8N1 */
+ *uart_control = 0x1E; /* 9600 baud */
+ }
+}
+
+int tty_carrier(uint8_t minor)
+{
+ /* The serial DCD is status bit 5 but not wired */
+ return 1;
+}
+
+void tty_interrupt(void)
+{
+ uint8_t r = *uart_status;
+ if (r & 0x8) {
+ r = *uart_data;
+ tty_inproc(2,r);
+ }
+}
+
+uint8_t keymap[8];
+static uint8_t keyin[8];
+static uint8_t keybyte, keybit;
+static uint8_t newkey;
+static int keysdown = 0;
+static uint8_t shiftmask[8] = {
+ 0, 0x40, 0, 0, 0, 0, 0, 0x40
+};
+
+static uint8_t rbit[8] = {
+ 0xFE,
+ 0xFD,
+ 0xFB,
+ 0xF7,
+ 0xEF,
+ 0xDF,
+ 0xBF,
+ 0x7F,
+};
+
+/* Row inputs: multiplexed with the joystick */
+static volatile uint8_t *pia_row = (uint8_t *)0xFF00;
+/* Columns for scanning: multiplexed with the printer port */
+static volatile uint8_t *pia_col = (uint8_t *)0xFF02;
+/* Control */
+static volatile uint8_t *pia_ctrl = (uint8_t *)0xFF03;
+
+static void keyproc(void)
+{
+ int i;
+ uint8_t key;
+
+ for (i = 0; i < 8; i++) {
+ /* We do the scan in software on the Dragon */
+ *pia_col = rbit[i];
+ keyin[i] = ~*pia_row;
+ key = keyin[i] ^ keymap[i];
+ if (key) {
+ int n;
+ int m = 1;
+ for (n = 0; n < 7; n++) {
+ if ((key & m) && (keymap[i] & m)) {
+ if (!(shiftmask[i] & m))
+ keysdown--;
+ }
+ if ((key & m) && !(keymap[i] & m)) {
+ if (!(shiftmask[i] & m))
+ keysdown++;
+ keybyte = i;
+ keybit = n;
+ newkey = 1;
+ }
+ m += m;
+ }
+ }
+ keymap[i] = keyin[i];
+ }
+}
+
+uint8_t keyboard[8][7] = {
+ { '0', '8', '@', 'h', 'p', 'x', 10 },
+ { '1', '9', 'a', 'i', 'q', 'y', 0 /* clear - used as ctrl*/ },
+ { '2', ':', 'b', 'j', 'r', 'z', 27 /* break (used for esc) */ },
+ { '3', ';', 'c', 'k', 's', '^' /* up */, 0 /* NC */ },
+ { '4', ',', 'd', 'l', 't', '|' /* down */, 0 /* NC */ },
+ { '5', '-', 'e', 'm', 'u', 8 /* left */, 0 /* NC */ },
+ { '6', '.', 'f', 'n', 'v', '\t' /* right */, 0 /* NC */ },
+ { '7', '/', 'g', 'o', 'w', ' ', 0 /* shift */ },
+};
+
+uint8_t shiftkeyboard[8][7] = {
+ { '_', '(', '\\', 'H', 'P', 'X', 10 },
+ { '!', ')', 'A', 'I', 'Q', 'Y', 0 /* clear */ },
+ { '"', '*', 'B', 'J', 'R', 'Z', 3 /* break */ },
+ { '#', '+', 'C', 'K', 'S', '[' /* up */, 0 /* NC */ },
+ { '$', '<', 'D', 'L', 'T', ']' /* down */, 0 /* NC */ },
+ { '%', '=', 'E', 'M', 'U', '{' /* left */, 0 /* NC */ },
+ { '&', '>', 'F', 'N', 'V', '}' /* right */, 0 /* NC */ },
+ { '\'', '?', 'G', 'O', 'W', ' ', 0 /* shift */ },
+};
+
+static void keydecode(void)
+{
+ uint8_t c;
+
+ if (keymap[7] & 64) /* shift */
+ c = shiftkeyboard[keybyte][keybit];
+ else
+ c = keyboard[keybyte][keybit];
+ if (keymap[1] & 64) { /* control */
+ if (c > 31 && c < 96)
+ c &= 31;
+ }
+ tty_inproc(1, c);
+}
+
+void platform_interrupt(void)
+{
+ uint8_t i = *pia_ctrl;
+ if (i & 0x80) {
+ *pia_col;
+ newkey = 0;
+ keyproc();
+ if (keysdown < 3 && newkey)
+ keydecode();
+ timer_interrupt();
+ }
+}
+
+/* This is used by the vt asm code, but needs to live at the top of the kernel */
+uint16_t cursorpos;
--- /dev/null
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+
+#define KEY_ROWS 8
+#define KEY_COLS 7
+extern uint8_t keymap[8];
+extern uint8_t keyboard[8][7];
+extern uint8_t shiftkeyboard[8][7];
+
+#endif
--- /dev/null
+ ;
+ ; common Dragon platform
+ ;
+
+ .module dragon
+
+ ; exported
+
+ ; imported
+ .globl unix_syscall_entry
+ .globl fd_nmi_handler
+ .globl size_ram
+
+ ; exported debugging tools
+ .globl _trap_monitor
+ .globl outchar
+ .globl _di
+ .globl _ei
+ .globl _irqrestore
+
+ include "kernel.def"
+ include "../kernel09.def"
+
+
+ .area .vectors
+ ;
+ ; At 0x100 as required by the Dragon ROM
+ ;
+ jmp badswi_handler ; 0x100
+ jmp badswi_handler ; 0x103
+ jmp unix_syscall_entry ; 0x106
+ jmp fd_nmi_handler ; 0x109
+ jmp interrupt_handler ; 0x10C
+ jmp firq_handler ; 0x10F
+
+
+ .area .text
+init_early:
+ rts
+init_hardware:
+ jsr size_ram
+ ; Turn on PIA CB1 (50Hz interrupt)
+ lda 0xFF03
+ ora #1
+ sta 0xFF03
+ rts
+
+
+; old p6809.s stuff below
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl _program_vectors
+ .globl _kernel_flag
+
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+ .globl unix_syscall_entry
+ .globl fd_nmi_handler
+
+ .area .common
+
+trapmsg: .ascii "Trapdoor: SP="
+ .db 0
+trapmsg2: .ascii ", PC="
+ .db 0
+tm_user_sp: .dw 0
+
+_trap_reboot:
+_trap_monitor:
+ cwai #0
+ bra _trap_monitor
+
+
+_di:
+ tfr cc,b ; return the old irq state
+ orcc #0x10
+ rts
+_ei:
+ andcc #0xef
+ rts
+
+_irqrestore: ; B holds the data
+ tfr b,cc
+ rts
+
+ .area .text
+
+
+;
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+
+ .area .common
+
+;
+; In the Dragon nx32 case our vectors live in a fixed block
+; and is not banked out.
+;
+_program_vectors:
+ rts
+
+;
+; FIXME:
+;
+firq_handler:
+badswi_handler:
+ rti
+
+; outchar: Simple writing to video memory
+
+outchar:
+ pshs x
+ ldx traceptr
+ cmpa #0x60
+ blo lc
+ suba #0x20
+lc anda #0x3F
+ sta ,x+
+ stx traceptr
+ puls x,pc
+
+ .area .data
+
+_kernel_flag: .db 1
+traceptr:
+ .dw 0x0400
--- /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
+
+ .area .text
+
+_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
+;
+; Core floppy routines for the Dragon
+;
+
+ .globl fd_nmi_handler
+ .globl nmi_handler
+
+ .globl _fd_reset
+ .globl _fd_operation
+ .globl _fd_motor_on
+ .globl _fd_motor_off
+;
+; MMIO for the floppy controller
+;
+; For a Dragon cartridge
+;
+FDCCTRL EQU 0xFF48
+;
+; 0-1: drive select
+; 2: motor on
+; 3: density
+; 4: precomp
+; 5: nmi mode
+;
+FDCREG EQU 0xFF40
+FDCTRK EQU 0xFF41
+FDCSEC EQU 0xFF42
+FDCDATA EQU 0xFF43
+
+;
+; Structures we use
+;
+;
+; Per disk structure to hold device state
+;
+TRKCOPY EQU 0
+
+;
+; Command issue
+;
+CMD EQU 0
+TRACK EQU 1
+SECTOR EQU 2
+DIRECT EQU 3 ; 0 = read 2 = write 1 = status
+DATA EQU 4
+
+ .area .text
+;
+; NMI handling for the floppy drive
+;
+fd_nmi_handler:
+ lda FDCREG
+ ldy nmivector
+ sty 10,s ; overwrite the return PC
+ ldy #nmi_handler
+ sty nmivector ; unexpected NMI trap
+ rti
+
+;
+; Snooze to give the drive controller time to think
+;
+disknap:
+ ldx #8750 ; assuming 0.9MHz
+disknapw:
+ leax -1,x
+ bne disknapw
+ rts
+
+;
+; Wait for the drive controller to become ready
+;
+waitdisk:
+ ldx #0 ; wait timer
+waitdisk_l:
+ leax -1,x
+ beq forceint ; try forcing an interrupt
+ lda <FDCREG
+ bita #0x01
+ bne waitdisk_l
+ rts ; done, idle EQ true
+forceint: ; no response, bigger stick
+ lda #0xD0 ; reset
+ sta <FDCREG
+ nop
+ exg a,a
+ exg a,a
+ lda <FDCREG ; read to reset int status
+ ; ?? what to do next ??
+ lda #0xff ; force NEQ
+ rts
+
+; Set up the disk. On entry y points to our per drive data and
+; x points to the command block
+;
+fdsetup:
+ lda TRKCOPY,y
+ sta <FDCTRK ; reset track register
+ pshs x,y
+ cmpa TRACK,x ; target track
+;
+; FIXME: what have we screwed up here so this always branches ???
+;
+; beq fdiosetup
+
+ sta <FDCTRK ; target
+ ;
+ ; So we can verify
+ ;
+ lda SECTOR,x
+ sta <FDCSEC
+ ;
+ ; Need to seek the disk
+ ;
+ lda #0x14
+ sta <FDCREG ; seek
+ nop
+ exg a,a
+ exg a,a
+ jsr waitdisk
+ bne setuptimeout
+ jsr disknap
+ anda #0x18 ; error bits
+ beq fdiosetup
+ ; seek failed, not good
+setuptimeout: ; NE = bad
+ puls x,y
+ ldb <FDCTRK ; we have no idea where we are
+ stb TRKCOPY,y ; so remember what the drive reported
+ rts
+;
+; Head in the right place
+;
+fdiosetup:
+ puls x,y
+ lda TRACK,x
+ sta TRKCOPY,y ; remember we arrived
+ ldb fdcctrl
+ andb #0xEF ; precomp
+ cmpa #22
+ blo noprecomp
+ orb #0x10
+noprecomp:
+ orb #0x20 ; NMI/halt on
+ stb <FDCCTRL ; precomp configured
+ lda SECTOR,x
+ sta <FDCSEC
+ lda <FDCREG ; clear any pending int
+ lda CMD,x ; command to issue
+ ldy #fdxferdone
+ sty nmivector ; so our NMI handler will clean up
+ ldy #0 ; timeout handling
+ orcc #0x50 ; irqs off or we'll miss bytes
+ sta <FDCREG ; issue the command
+ nop ; give the FDC a moment to think
+ exg a,a
+ exg a,a
+ ldb DIRECT,x
+ cmpb #0x01 ; read ?
+ beq fdio_in
+ cmpb #0x02
+ beq wait_drq ; write
+;
+; Status registers
+;
+fdxferdone:
+ ldb fdcctrl
+ stb <FDCCTRL
+ lda <FDCREG
+ anda #0x7C ; Returns with A holding the status bits
+ rts
+;
+; Relies on B being 2...
+;
+wait_drq:
+ bitb <FDCREG
+ bne drq_on
+ leay -1,y
+ bne wait_drq
+ ;
+ ; Timed out - reset etc to clean up ??
+ ;
+ lda #0xff ; our error code
+ rts
+
+;
+; Once the controller decides it is finished it will flag an NMI
+; and the NMI will switch the PC on the return to fdxferdone.
+;
+; Begin the actual copy to disk
+;
+drq_on:
+ ldx DATA,x
+ lda ,x+
+ bra drq_go
+drq_loop:
+ sync
+drq_go:
+ sta <FDCDATA
+ lda ,x+
+ bra drq_loop
+
+;
+; Read from the disk
+;
+fdio_in:
+ ldx DATA,x
+fdio_dwait:
+ ldb <FDCREG
+ bitb #0x02
+ bne fdio_go
+ leay -1,y
+ bne fdio_dwait
+ ldb fdcctrl
+ stb <FDCCTRL
+ lda #0xff
+ rts
+;
+; Now do the data
+;
+fdio_loop:
+ sync
+fdio_go:
+ ldb <0xFF22 ; clear the FIR (PIA1DB)
+ lda <FDCDATA
+ sta ,x+
+ bra fdio_loop
+
+;
+; PIA management
+;
+piasave:
+ pshs x,y
+ ldx #0xFF01 ; PIA0CRA
+ ldy #pia_stash
+ lda ,x
+ sta ,y+
+ anda #0xFC
+ sta ,x++ ; move on to 0CRB
+ lda ,x
+ sta ,y+
+ anda #0xFC
+ sta ,x
+ leax 0x1e,x ; PIA1CRA
+ lda ,x
+ sta ,y+
+ anda #0xFC
+ sta ,x++ ; on to PIA1CRB
+ lda ,x
+ sta ,y
+ ora #0x37
+ sta ,x ; floppy FIR enabled
+ puls x,y,pc
+
+piaload:
+ ; Must leave B untouched
+ pshs x,y
+ ldx #0xFF01
+ ldy #pia_stash
+ lda ,y+
+ sta ,x++
+ lda ,y+
+ sta ,x
+ leax 0x1e,x
+ lda ,y+
+ sta ,x++
+ lda ,y+
+ sta ,x
+ puls x,y,pc
+;
+; C glue interface.
+;
+; Because of the brain dead memory paging we dump the bits into
+; kernel space always. The thought of taking an NMI while in the
+; user memory and bank flipping to recover is just too odious !
+;
+
+;
+; Reset to track 0, wait for the command then idle
+;
+; fd_reset(uint16_t *drive)
+;
+_fd_reset:
+ pshs x,y,dp
+ lda #0xFF
+ tfr a,dp
+ ldb fdcctrl
+ stb <FDCCTRL
+ lda #0x01
+ sta <FDCSEC
+ lda #0x00 ; seek
+ sta <FDCTRK
+ sta <FDCREG
+ nop
+ exg a,a
+ exg a,a
+ jsr waitdisk
+ jsr disknap
+ ; FIXME: longer delay goes here ???
+ cmpa #0xff
+ beq rstff ; Total fail
+ anda #0x10 ; Error bit from the reset
+rstff:
+ tfr a,b
+ puls x,y,dp,pc
+;
+; fd_operation(uint16_t *cmd, uint16_t *drive)
+;
+; The caller must ensure the drive has been selected and the motor is
+; running.
+;
+_fd_operation:
+ pshs y,cc,dp
+ lda #0xFF
+ tfr a,dp
+ orcc #0x40 ; Make sure FIR is off
+ jsr piasave
+ ldy 6,s ; Drive struct
+ jsr fdsetup ; Set up for a command
+ tfr a,b ; Status code or 0xFF for total failure
+ bsr piaload
+ puls y,cc,dp,pc ; Restore IRQ state etc
+;
+; C interface fd_motor_on(uint8 drivesel)
+;
+; Selects this drive and turns on the motors
+;
+_fd_motor_on:
+ pshs y,dp
+ lda #0xFF
+ tfr a,dp
+
+ ;
+ ; Select drive B, turn on motor if needed
+ ;
+ lda motor_running ; nothing selected
+ beq notsel
+ cmpb curdrive ; check if we are good
+ bne notsel
+;
+; All is actually good
+;
+motor_was_on:
+ ldb #0
+ puls y,dp,pc
+;
+; Select our drive
+;
+notsel:
+ orb #0x04 ; motor on, single density + our drive id
+ stb <FDCCTRL
+ stb fdcctrl
+ bita #0x4
+ bne motor_was_on
+ jsr disknap
+ ; FIXME: longer motor spin up delay goes here
+ jsr waitdisk
+ tfr a,b ; return in the right place
+ puls y,dp,pc
+
+;
+; C interface fd_motor_off(void)
+;
+; Turns off the drive motors, deselects all drives
+;
+_fd_motor_off:
+ pshs y,dp
+ lda #0xFF
+ tfr a,dp
+
+;
+; Deselect drives and turn off motor
+;
+ ldb motor_running
+ beq no_work_motor
+ ; Should we seek to track 0 ?
+ ldb <FDCCTRL
+ andb #0xF0
+ stb <FDCCTRL
+ clr motor_running
+no_work_motor:
+ puls y,dp,pc
+
+ .area .data
+
+nmivector:
+ .word nmi_handler
+curdrive:
+ .byte 0xff
+
+ .area .bss
+motor_running:
+ .byte 0
+fdcctrl:
+ .byte 0
+pia_stash:
+ .byte 0
+ .byte 0
+ .byte 0
+ .byte 0
\ No newline at end of file
--- /dev/null
+define basesympat __sectionbase_%s__
+define lensympat __sectionlen_%s__
+section .vectors load 0x0100
+section .start load 0x0800
+section .common
+section .text2
+section .discard
+section .udata load 0x7D00
+section .text load 0x8010
+section .bss high 0xFC00
+section .data
+section .videodata
+section .video
+entry start
--- /dev/null
+; UZI mnemonics for memory addresses etc
+
+U_DATA equ 0x7D00 ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE equ 0x300 ; 256+256+256 bytes.
+
+U_DATA_STASH equ 0xFC00 ; FC00-FEFF
+
+
+; Have these defined so that lowlevel-6809.s can be used as-is
+SAM_USER macro
+ endm
+
+SAM_KERNEL macro
+ endm
+
+SAM_SAVE macro
+ endm
+
+SAM_RESTORE macro
+ endm
+
--- /dev/null
+#include "cpu.h"
+
+void *memcpy(void *d, const void *s, size_t sz)
+{
+ unsigned char *dp = d;
+ const unsigned char *sp = s;
+ while(sz--)
+ *dp++=*sp++;
+ return d;
+}
+
+void *memset(void *d, int c, size_t sz)
+{
+ unsigned char *p = d;
+ while(sz--)
+ *p++ = c;
+ return d;
+}
+
+size_t strlen(const char *p)
+{
+ const char *e = p;
+ while(*e++);
+ return e-p-1;
+}
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+uint8_t membanks;
+
+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;
+ /* map bank 1 last for init, leave 0 for kernel */
+ for (i = membanks - 1; i > 0; i--)
+ pagemap_add(i);
+}
+
+void map_init(void)
+{
+}
+
+unsigned char vt_mangle_6847(unsigned char c)
+{
+ if (c >= 96)
+ c -= 32;
+ c &= 0x3F;
+ return c;
+}
--- /dev/null
+;
+; memory banking for external memory cartridge
+;
+; Copyright 2015 Tormod Volden
+;
+
+ .module mem_nx32
+
+ ; exported
+ .globl size_ram
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_a
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+
+ ; imported
+ .globl _ramsize
+ .globl _procmem
+ .globl _membanks
+
+banksel equ 0xFFBF
+bankoff equ 0xFFBE
+
+ include "kernel.def"
+ include "../kernel09.def"
+
+ .area .discard
+
+; Sets ramsize, procmem, membanks
+size_ram
+ ldx #0x8000 ; test location, no code can be preloaded here!
+ clra
+zbank sta banksel ; clear test byte on all possible banks
+ clr ,x
+ inca
+ cmpa #16
+ blo zbank
+ clra ; now test writing to banks
+ ldb #0xA5
+size_next
+ sta banksel
+ cmpb ,x ; if value was there already we have probably wrapped around
+ beq size_nonram
+ stb ,x
+ cmpb ,x
+ bne size_nonram
+ inca
+ cmpa #16
+ bne size_next
+size_nonram
+ clr banksel ; leave bank 0 selected and kdata mapped in
+ sta _membanks
+ inca ; add the internal 32K to the count
+ ldb #32
+ mul
+ std _ramsize
+ subd #48 ; whatever the kernel occupies
+ std _procmem
+ rts
+
+ .area .common
+
+map_kernel
+ pshs a
+ lda #0
+ sta map_copy
+ sta banksel
+ puls a,pc
+
+map_process
+ tsta
+ beq map_kernel
+map_process_a
+ sta map_copy
+ sta banksel
+ rts
+
+map_process_always
+ pshs a
+ lda U_DATA__U_PAGE+1 ; LSB of 16-bit page
+map_set_a
+ sta map_copy
+ sta banksel
+ puls a,pc
+
+map_save
+ pshs a
+ lda map_copy
+ sta map_store
+ puls a,pc
+
+map_restore
+ pshs a
+ lda map_store
+ bra map_set_a
+
+map_store .db 0
+map_copy .db 0
+
--- /dev/null
+export CPU = 6809
--- /dev/null
+;
+; 6809 version
+;
+ .module tricks
+
+ #imported
+ .globl _swapout
+ .globl _newproc
+ .globl _chksigs
+ .globl _getproc
+ .globl _trap_monitor
+ .globl _inint
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_a
+ .globl map_process_always
+
+ # exported
+ .globl _switchout
+ .globl _switchin
+ .globl _dofork
+ .globl _ramtop
+
+
+ include "kernel.def"
+ include "../kernel09.def"
+
+ .area .common
+
+_ramtop:
+ .dw 0
+
+; Switchout switches out the current process, finds another that is READY,
+; possibly the same process, and switches it in. When a process is
+; restarted after calling switchout, it thinks it has just returned
+; from switchout().
+;
+; 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
+
+ ; save machine state
+ ldd #0 ; return code set here is ignored, but _switchin can
+ ; return from either _switchout OR _dofork, so they must both write
+ ; U_DATA__U_SP with the following on the stack:
+ pshs d
+ sts U_DATA__U_SP ; this is where the SP is restored in _switchin
+
+ ; set inint to false
+ lda #0
+ sta _inint
+
+ ; Stash the uarea into process memory bank
+ jsr map_process_always
+ sty _swapstack+2
+
+ ldx #U_DATA
+ ldy #U_DATA_STASH
+stash ldd ,x++
+ std ,y++
+ cmpx #U_DATA+U_DATA__TOTALSIZE
+ bne stash
+ ldy _swapstack+2
+
+ ; get process table in
+ jsr map_kernel
+
+ ; find another process to run (may select this one again) returns it
+ ; in X
+ jsr _getproc
+ jsr _switchin
+ ; we should never get here
+ jsr _trap_monitor
+
+_swapstack .dw 0
+ .dw 0
+
+badswitchmsg: .ascii "_switchin: FAIL"
+ .db 13
+ .db 10
+ .db 0
+
+; new process pointer is in X
+_switchin:
+ orcc #0x10 ; irq off
+
+ ;pshs x
+ stx _swapstack
+ ; get process table - must be in already from switchout
+ ; jsr map_kernel
+ lda P_TAB__P_PAGE_OFFSET+1,x ; LSB of 16-bit page no
+ jsr map_process_a
+
+ ; fetch uarea from process memory
+ sty _swapstack+2
+ ldx #U_DATA_STASH
+ ldy #U_DATA
+stashb ldd ,x++
+ std ,y++
+ cmpx #U_DATA_STASH+U_DATA__TOTALSIZE
+ bne stashb
+ ldy _swapstack+2
+
+ ; get back kernel page so that we see process table
+ jsr map_kernel
+
+ ;puls x
+ ldx _swapstack
+ ; check u_data->u_ptab matches what we wanted
+ cmpx U_DATA__U_PTAB
+ bne switchinfail
+
+ lda #P_RUNNING
+ sta P_TAB__P_STATUS_OFFSET,x
+
+ lda #0
+ sta _runticks
+
+ ; restore machine state -- note we may be returning from either
+ ; _switchout or _dofork
+ lds U_DATA__U_SP
+ puls x ; return code
+
+ ; enable interrupts, if the ISR isn't already running
+ lda _inint
+ beq swtchdone ; in ISR, leave interrupts off
+ andcc #0xef
+swtchdone:
+ rts
+
+switchinfail:
+ jsr outx
+ ldx #badswitchmsg
+ jsr outstring
+ ; something went wrong and we didn't switch in what we asked for
+ jmp _trap_monitor
+
+ .area .data
+fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
+
+ .area .common
+;
+; Called from _fork. We are in a syscall, the uarea is live as the
+; parent uarea. The kernel is the mapped object.
+;
+; FIXME: preserve y
+;
+_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.
+ pshs x ; x has p->p_pid from above, the return value in the parent
+
+ ; save kernel stack pointer -- when it comes back in the parent we'll be in
+ ; _switchin which will immediately return (appearing to be _dofork()
+ ; returning) and with 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.
+
+ ldx U_DATA__U_PTAB ; parent
+; FIXME
+ ; jsr _swapout ; swap the parent out, leaving
+ ; it in memory as the child
+
+ ; We are now in the kernel child context
+
+ ; now the copy operation is complete we can get rid of the stuff
+ ; _switchin will be expecting from our copy of the stack.
+ puls x
+
+ ldx fork_proc_ptr
+ jsr _newproc
+
+ ; any calls to map process will now map the childs memory
+
+ ; runticks = 0;
+ clr _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().
+ rts
+
--- /dev/null
+ .module usermem
+
+;
+; 6809 copy to and from userspace
+;
+; This is linked by the 6881 using platforms only. MMU based
+; machines simply don't need it.
+
+
+ include "kernel.def"
+ include "../kernel09.def"
+
+ ; 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
+ .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
+ ldu 7,s ; user address
+ ldy 9,s ; count
+ orcc #0x10
+ugetl:
+ lda ,x+
+ jsr map_process_always
+ sta ,u+
+ jsr map_kernel
+ leay -1,y
+ bne ugetl
+ 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
+ puls cc,pc
+
+__uputw:
+ pshs cc
+ orcc #0x10
+ ldd 3,s
+ jsr map_process_always
+ exg d,x
+ std ,x
+ jsr map_kernel
+ puls cc,pc
+
+; X = source, user, size on stack
+__uput:
+ pshs u,y,cc
+ orcc #0x10
+ 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
+ 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
+ puls y,cc,pc