From: Tormod Volden Date: Mon, 6 Apr 2015 09:32:57 +0000 (+0200) Subject: Add new dragon-nx32 port X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=cda17b281cb7124370622cea6e67b099f56d30c8;p=FUZIX.git Add new dragon-nx32 port For Dragon 32 or 64 with an external banked memory cartridge. Signed-off-by: Tormod Volden --- diff --git a/Kernel/platform-dragon-nx32/Makefile b/Kernel/platform-dragon-nx32/Makefile new file mode 100644 index 00000000..422c63bd --- /dev/null +++ b/Kernel/platform-dragon-nx32/Makefile @@ -0,0 +1,35 @@ + +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 diff --git a/Kernel/platform-dragon-nx32/README b/Kernel/platform-dragon-nx32/README new file mode 100644 index 00000000..2a7d258e --- /dev/null +++ b/Kernel/platform-dragon-nx32/README @@ -0,0 +1,19 @@ +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. + diff --git a/Kernel/platform-dragon-nx32/commonmem.s b/Kernel/platform-dragon-nx32/commonmem.s new file mode 100644 index 00000000..335764dd --- /dev/null +++ b/Kernel/platform-dragon-nx32/commonmem.s @@ -0,0 +1,23 @@ +; + .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 diff --git a/Kernel/platform-dragon-nx32/config.h b/Kernel/platform-dragon-nx32/config.h new file mode 100644 index 00000000..77233a48 --- /dev/null +++ b/Kernel/platform-dragon-nx32/config.h @@ -0,0 +1,56 @@ +/* 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 */ diff --git a/Kernel/platform-dragon-nx32/crt0.s b/Kernel/platform-dragon-nx32/crt0.s new file mode 100644 index 00000000..01979032 --- /dev/null +++ b/Kernel/platform-dragon-nx32/crt0.s @@ -0,0 +1,35 @@ + ; 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 + diff --git a/Kernel/platform-dragon-nx32/devdw.c b/Kernel/platform-dragon-nx32/devdw.c new file mode 100644 index 00000000..ae3fc914 --- /dev/null +++ b/Kernel/platform-dragon-nx32/devdw.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include + +#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); +} + diff --git a/Kernel/platform-dragon-nx32/devdw.h b/Kernel/platform-dragon-nx32/devdw.h new file mode 100644 index 00000000..d7b93583 --- /dev/null +++ b/Kernel/platform-dragon-nx32/devdw.h @@ -0,0 +1,14 @@ +#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__ */ + diff --git a/Kernel/platform-dragon-nx32/devfd.c b/Kernel/platform-dragon-nx32/devfd.c new file mode 100644 index 00000000..c4754a56 --- /dev/null +++ b/Kernel/platform-dragon-nx32/devfd.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include + +#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); +} + diff --git a/Kernel/platform-dragon-nx32/devfd.h b/Kernel/platform-dragon-nx32/devfd.h new file mode 100644 index 00000000..e5224ac9 --- /dev/null +++ b/Kernel/platform-dragon-nx32/devfd.h @@ -0,0 +1,16 @@ +#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__ */ + diff --git a/Kernel/platform-dragon-nx32/device.h b/Kernel/platform-dragon-nx32/device.h new file mode 100644 index 00000000..6f4c1e26 --- /dev/null +++ b/Kernel/platform-dragon-nx32/device.h @@ -0,0 +1,6 @@ +#ifndef __DEVICE_DOT_H__ +#define __DEVICE_DOT_H__ + +extern void mod_control(uint8_t set, uint8_t clr); + +#endif /* __DEVICE_DOT_H__ */ diff --git a/Kernel/platform-dragon-nx32/devices.c b/Kernel/platform-dragon-nx32/devices.c new file mode 100644 index 00000000..a188243c --- /dev/null +++ b/Kernel/platform-dragon-nx32/devices.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct devsw dev_tab[] = /* The device driver switch table */ +{ +// minor open close read write ioctl +// ----------------------------------------------------------------- + /* 0: /dev/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) +{ +} + diff --git a/Kernel/platform-dragon-nx32/devlpr.c b/Kernel/platform-dragon-nx32/devlpr.c new file mode 100644 index 00000000..a0110858 --- /dev/null +++ b/Kernel/platform-dragon-nx32/devlpr.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/Kernel/platform-dragon-nx32/devlpr.h b/Kernel/platform-dragon-nx32/devlpr.h new file mode 100644 index 00000000..7765c187 --- /dev/null +++ b/Kernel/platform-dragon-nx32/devlpr.h @@ -0,0 +1,8 @@ +#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 diff --git a/Kernel/platform-dragon-nx32/devtty.c b/Kernel/platform-dragon-nx32/devtty.c new file mode 100644 index 00000000..9419151b --- /dev/null +++ b/Kernel/platform-dragon-nx32/devtty.c @@ -0,0 +1,194 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG /* UNdefine to delete debug code sequences */ + +uint8_t *uart_data = (uint8_t *)0xFF04; /* ACIA data */ +uint8_t *uart_status = (uint8_t *)0xFF05; /* ACIA status */ +uint8_t *uart_command = (uint8_t *)0xFF06; /* ACIA command */ +uint8_t *uart_control = (uint8_t *)0xFF07; /* ACIA control */ + +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; diff --git a/Kernel/platform-dragon-nx32/devtty.h b/Kernel/platform-dragon-nx32/devtty.h new file mode 100644 index 00000000..14c28c31 --- /dev/null +++ b/Kernel/platform-dragon-nx32/devtty.h @@ -0,0 +1,10 @@ +#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 diff --git a/Kernel/platform-dragon-nx32/dragon.s b/Kernel/platform-dragon-nx32/dragon.s new file mode 100644 index 00000000..4f1462ee --- /dev/null +++ b/Kernel/platform-dragon-nx32/dragon.s @@ -0,0 +1,131 @@ + ; + ; 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 diff --git a/Kernel/platform-dragon-nx32/drivewire.s b/Kernel/platform-dragon-nx32/drivewire.s new file mode 100644 index 00000000..babebeb9 --- /dev/null +++ b/Kernel/platform-dragon-nx32/drivewire.s @@ -0,0 +1,139 @@ +; +; 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" + diff --git a/Kernel/platform-dragon-nx32/dw.def b/Kernel/platform-dragon-nx32/dw.def new file mode 100644 index 00000000..677f4c71 --- /dev/null +++ b/Kernel/platform-dragon-nx32/dw.def @@ -0,0 +1,70 @@ +******************************************************************** +* +* Copied from HDB-DOS from toolshed.sf.net +* +* dwdefs - DriveWire Definitions File +* +* $Id: dwdefs.d,v 1.10 2010/02/21 06:24:47 aaronwolfe Exp $ +* +* Ed. Comments Who YY/MM/DD +* ------------------------------------------------------------------ +* 1 Started BGP 03/04/03 +* 2 Added DWGLOBS area BGP 09/12/27 + + nam dwdefs + ttl DriveWire Definitions File + +* Addresses +BBOUT equ $FF20 +BBIN equ $FF22 + +* Opcodes +OP_NOP equ $00 No-Op +OP_RESET1 equ $FE Server Reset +OP_RESET2 equ $FF Server Reset +OP_RESET3 equ $F8 Server Reset +OP_DWINIT equ 'Z DriveWire dw3 init/OS9 boot +OP_TIME equ '# Current time requested +OP_INIT equ 'I Init routine called +OP_READ equ 'R Read one sector +OP_REREAD equ 'r Re-read one sector +OP_READEX equ 'R+128 Read one sector +OP_REREADEX equ 'r+128 Re-read one sector +OP_WRITE equ 'W Write one sector +OP_REWRIT equ 'w Re-write one sector +OP_GETSTA equ 'G GetStat routine called +OP_SETSTA equ 'S SetStat routine called +OP_TERM equ 'T Term routine called +OP_SERINIT equ 'E +OP_SERTERM equ 'E+128 + +* Printer opcodes +OP_PRINT equ 'P Print byte to the print buffer +OP_PRINTFLUSH equ 'F Flush the server print buffer + +* Serial opcodes +OP_SERREAD equ 'C +OP_SERREADM equ 'c +OP_SERWRITE equ 'C+128 +OP_SERGETSTAT equ 'D +OP_SERSETSTAT equ 'D+128 + +* for dw vfm +OP_VFM equ 'V+128 + +* WireBug opcodes (Server-initiated) +OP_WIREBUG_MODE equ 'B +* WireBug opcodes (Server-initiated) +OP_WIREBUG_READREGS equ 'R Read the CoCo's registers +OP_WIREBUG_WRITEREGS equ 'r Write the CoCo's registers +OP_WIREBUG_READMEM equ 'M Read the CoCo's memory +OP_WIREBUG_WRITEMEM equ 'm Write the CoCo's memory +OP_WIREBUG_GO equ 'G Tell CoCo to get out of WireBug mode and continue execution + +* VPort opcodes (CoCo-initiated) +OP_VPORT_READ equ 'V +OP_VPORT_WRITE equ 'v + +* Error definitions +E_CRC equ $F3 Same as NitrOS-9 E$CRC + diff --git a/Kernel/platform-dragon-nx32/dwread.s b/Kernel/platform-dragon-nx32/dwread.s new file mode 100644 index 00000000..1b4c587b --- /dev/null +++ b/Kernel/platform-dragon-nx32/dwread.s @@ -0,0 +1,341 @@ +******************************************************* +* +* Copied from HDB-DOS from toolshed.sf.net +* The original code is public domain +* +* DWRead +* Receive a response from the DriveWire server. +* Times out if serial port goes idle for more than 1.4 (0.7) seconds. +* Serial data format: 1-8-N-1 +* 4/12/2009 by Darren Atkinson +* +* Entry: +* X = starting address where data is to be stored +* Y = number of bytes expected +* +* Exit: +* CC = carry set on framing error, Z set if all bytes received +* X = starting address of data received +* Y = checksum +* U is preserved. All accumulators are clobbered +* + + IFNE ARDUINO +* Note: this is an optimistic routine. It presumes that the server will always be there, and +* has NO timeout fallback. It is also very short and quick. +DWRead clra ; clear Carry (no framing error) + pshs u,x,cc ; preserve registers + leau ,x + ldx #$0000 +loop@ tst $FF51 ; check for CA1 bit (1=Arduino has byte ready) + bpl loop@ ; loop if not set + ldb $FF50 ; clear CA1 bit in status register + stb ,u+ ; save off acquired byte + abx ; update checksum + leay ,-y + bne loop@ + + leay ,x ; return checksum in Y + puls cc,x,u,pc ; restore registers and return + + ELSE + + IFNE JMCPBCK +* NOTE: There is no timeout currently on here... +DWRead clra ; clear Carry (no framing error) + deca ; clear Z flag, A = timeout msb ($ff) + tfr cc,b + pshs u,x,dp,b,a ; preserve registers, push timeout msb + leau ,x + ldx #$0000 + IFEQ NOINTMASK + orcc #IntMasks + ENDC +loop@ ldb $FF4C + bitb #$02 + beq loop@ + ldb $FF44 + stb ,u+ + abx + leay ,-y + bne loop@ + + tfr x,y + ldb #0 + lda #3 + leas 1,s ; remove timeout msb from stack + inca ; A = status to be returned in C and Z + ora ,s ; place status information into the.. + sta ,s ; ..C and Z bits of the preserved CC + leay ,x ; return checksum in Y + puls cc,dp,x,u,pc ; restore registers and return + ELSE + IFNE BECKER + IFNDEF BCKSTAT +BCKSTAT equ $FF41 + ENDC + IFNDEF BCKPORT +BCKPORT equ $FF42 + ENDC +* NOTE: There is no timeout currently on here... +DWRead clra ; clear Carry (no framing error) + deca ; clear Z flag, A = timeout msb ($ff) + tfr cc,b + pshs u,x,dp,b,a ; preserve registers, push timeout msb + leau ,x + ldx #$0000 + IFEQ NOINTMASK + orcc #IntMasks + ENDC +loop@ ldb BCKSTAT + bitb #$02 + beq loop@ + ldb BCKPORT + stb ,u+ + abx + leay ,-y + bne loop@ + tfr x,y + ldb #0 + lda #3 +timeout leas 1,s ; remove timeout msb from stack + inca ; A = status to be returned in C and Z + ora ,s ; place status information into the.. + sta ,s ; ..C and Z bits of the preserved CC + leay ,x ; return checksum in Y + puls cc,dp,x,u,pc ; restore registers and return + ENDC + ENDC + ENDC + + IFEQ BECKER+JMCPBCK+ARDUINO + IFNE BAUD38400 +******************************************************* +* 38400 bps using 6809 code and timimg +******************************************************* + +DWRead clra ; clear Carry (no framing error) + deca ; clear Z flag, A = timeout msb ($ff) + tfr cc,b + pshs u,x,dp,b,a ; preserve registers, push timeout msb + IFEQ NOINTMASK + orcc #IntMasks ; mask interrupts + ENDC + tfr a,dp ; set direct page to $FFxx + setdp $ff + leau ,x ; U = storage ptr + ldx #0 ; initialize checksum + adda #2 ; A = $01 (serial in mask), set Carry + +* Wait for a start bit or timeout +rx0010 bcc rxExit ; exit if timeout expired + ldb #$ff ; init timeout lsb +rx0020 bita +#include +#include +#include +#include + +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; +} diff --git a/Kernel/platform-dragon-nx32/mem-nx32.s b/Kernel/platform-dragon-nx32/mem-nx32.s new file mode 100644 index 00000000..39d79552 --- /dev/null +++ b/Kernel/platform-dragon-nx32/mem-nx32.s @@ -0,0 +1,101 @@ +; +; 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 + diff --git a/Kernel/platform-dragon-nx32/target.mk b/Kernel/platform-dragon-nx32/target.mk new file mode 100644 index 00000000..e9abbba1 --- /dev/null +++ b/Kernel/platform-dragon-nx32/target.mk @@ -0,0 +1 @@ +export CPU = 6809 diff --git a/Kernel/platform-dragon-nx32/tricks.s b/Kernel/platform-dragon-nx32/tricks.s new file mode 100644 index 00000000..7af4997a --- /dev/null +++ b/Kernel/platform-dragon-nx32/tricks.s @@ -0,0 +1,198 @@ +; +; 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 + diff --git a/Kernel/platform-dragon-nx32/usermem_sam.s b/Kernel/platform-dragon-nx32/usermem_sam.s new file mode 100644 index 00000000..1be408af --- /dev/null +++ b/Kernel/platform-dragon-nx32/usermem_sam.s @@ -0,0 +1,131 @@ + .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