-TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite
+TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-dragon
#export TARGET= msx1
#export CPU = z80
--- /dev/null
+
+CSRCS = devlpr.c devtty.c devrd.c
+CSRCS += devices.c main.c libc.c
+
+ASRCS = p6809.s crt0.s
+ASRCS += tricks.s commonmem.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) -c $<
+
+$(AOBJS): %$(BINEXT): %.s
+ $(CROSS_AS) $(ASOPTS) $< -o $*.o
+
+clean:
+ rm -f $(OBJS) $(JUNK) core *~
+
+image:
+ $(CROSS_LD) -o ../fuzix.bin --map=../fuzix.map --script=fuzix.link \
+ crt0.o commonmem.o \
+ p6809.o ../start.o ../version.o ../lowlevel-6809.o \
+ tricks.o main.o ../timer.o ../kdata.o devrd.o devices.o \
+ ../devio.o ../filesys.o ../process.o ../inode.o ../syscall_fs.o \
+ ../syscall_proc.o ../syscall_other.o ../mm.o ../swap.o ../single.o \
+ ../tty.o ../devsys.o ../usermem.o ../syscall_fs2.o ../syscall_exec.o \
+ ../usermem_std-6809.o devlpr.o devtty.o libc.o ../vt.o
--- /dev/null
+Dragon Experimental Build
+
+Currently we build for a Dragon64 but are not doing anything but initial
+boot testing work
+
+Memory usage:
+
+The Dragon 64 has 2x32K banks of RAM which isn't sufficient but also 16K
+of cartridge space that can appear at 0xC000->0xF??? and two ROMs
+
+
+This is a very experimental build to see what might be possible if we either
+replaced the ROM images or used a cartridge or both.
--- /dev/null
+;
+; Put the udata at the start of common. We have four 16K banks so we
+; keep the non .common kernel elements below C000 and then keep bank 3 as a
+; true common bank
+;
+ .module commonmem
+
+ ; exported symbols
+ .globl _ub
+ .globl _udata
+ .globl kstack_top
+ .globl istack_top
+ .globl istack_switched_sp
+
+ .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 */
+#undef CONFIG_MULTI
+/* Single tasking - for now while we get it booting */
+#undef CONFIG_SINGLETASK
+/* Use C helpers for usermem */
+#define CONFIG_USERMEM_C
+/* Pure swap */
+#define CONFIG_SWAP_ONLY
+#define CONFIG_BANKS 1
+/* FIXME */
+#define BANK_PROCESS *((volatile uint8_t *)0xff91) &= ~1
+#define BANK_KERNEL *((volatile uint8_t *)0xff91) |= 1
+/* And swapping */
+#define SWAPDEV 6 /* FIXME */
+#define SWAP_SIZE 0x80 /* 64K blocks */
+/* FIXME */
+#define SWAPBASE 0x0000 /* We swap the lot in one, include the */
+#define SWAPTOP 0x8000 /* uarea so its a round number of sectors */
+#define UDATA_BLOCKS 0 /* We swap the uarea in the data */
+#define UDATA_SWAPSIZE 0
+#define MAX_SWAPS 32
+
+/* Video terminal, not a serial tty */
+#define CONFIG_VT
+/* Simple text mode */
+#define CONFIG_VT_SIMPLE
+/* Vt definitions */
+#define VT_BASE 0x0400 /* Default video text mode base */
+#define VT_WIDTH 32
+#define VT_HEIGHT 24
+#define VT_RIGHT 31
+#define VT_BOTTOM 23
+
+#define TICKSPERSEC 100 /* Ticks per second */
+#define PROGBASE ((uint8_t *)(0x8000)) /* also data base */
+#define PROGTOP ((uint8_t *)(0xff00)) /* 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 10 /* Number of block buffers */
+#define NMOUNTS 3 /* Number of mounts at a time */
--- /dev/null
+ ; Ordering of segments for the linker.
+ ; WRS: Note we list all our segments here, even though
+ ; we don't use them all, because their ordering is set
+ ; when they are first seen.
+
+ ; imported symbols
+ .globl _fuzix_main
+ .globl init_early
+ .globl init_hardware
+ .globl kstack_top
+
+ ; startup code @0
+ .area .start
+ jmp start
+
+ .area .text
+
+start: orcc #0x10 ; interrupts definitely off
+ lds #kstack_top
+ ; move the common memory where it belongs
+ ; we do this dowards, not out of any concern about
+ ; about overlap (although its correct for this) but because
+ ; it deals with linker reloc limits nicely
+; ldd #s__INITIALIZER
+; addd #l__COMMONMEM
+; tfr d,x
+; ldd #s__COMMONMEM
+; addd #l__COMMONMEM
+; tfr d,y
+
+;copier: lda ,-x
+; sta ,-y
+; cmpy #s__COMMONMEM
+; bgt copier
+
+;wiper: ldx #s__DATA
+; ldd #l__DATA
+; clr ,x+
+; subd #1
+; bne wiper
+
+ jsr init_early
+ jsr init_hardware
+ jsr _fuzix_main
+ orcc #0x10
+stop: bra stop
+
--- /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 <devrd.h>
+#include <devsys.h>
+#include <devlpr.h>
+#include <tty.h>
+#include <devtty.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+// minor open close read write ioctl
+// -----------------------------------------------------------------
+ /* 0: /dev/fd Floppy disc block devices */
+ { rd_open, no_close, rd_read, rd_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, tty_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 */
+};
+
+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
+/*
+ * NC100 RD PCMCIA driver
+ *
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devrd.h>
+
+static int rd_transfer(bool is_read, uint8_t rawflag)
+{
+ blkno_t block;
+ int block_xfer;
+ uint16_t dptr;
+ int dlen;
+ int ct = 0;
+ int map;
+
+ /* FIXME: raw is broken unless nicely aligned */
+ if(rawflag) {
+ dlen = udata.u_count;
+ dptr = (uint16_t)udata.u_base;
+ if (dptr & 0x1FF) {
+ udata.u_error = EIO;
+ return -1;
+ }
+ block = udata.u_offset >> 9;
+ block_xfer = dlen >> 9;
+ map = 1;
+ } else { /* rawflag == 0 */
+ dlen = 512;
+ dptr = (uint16_t)udata.u_buf->bf_data;
+ block = udata.u_buf->bf_blk;
+ block_xfer = 1;
+ map = 0;
+ }
+ block += 2*320; /* ramdisc starts at 320K in */
+
+ while (ct < block_xfer) {
+/* rd_memcpy(is_read, map, dptr, block); */
+ block++;
+ ct++;
+ }
+ return ct;
+}
+
+int rd_open(uint8_t minor, uint16_t flag)
+{
+ flag;
+ if(minor != 0) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return 0;
+}
+
+int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;minor;
+ return rd_transfer(true, rawflag);
+}
+
+int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;minor;
+ return rd_transfer(false, rawflag);
+}
+
--- /dev/null
+#ifndef __DEVRD_DOT_H__
+#define __DEVRD_DOT_H__
+
+/* public interface */
+int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int rd_open(uint8_t minor, uint16_t flag);
+
+/* asm banking helper */
+void rd_memcpy(uint8_t isread, uint8_t map, uint16_t dptr, uint16_t block);
+
+#endif /* __DEVRD_DOT_H__ */
+
--- /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);
+}
+
+bool tty_writeready(uint8_t minor)
+{
+ uint8_t c;
+ if (minor == 1)
+ return 1;
+ c = *uart_status;
+ return c & 16; /* TX DATA empty */
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+#if 0
+ if (minor == 1) {
+ vtoutput(&c, 1);
+ return;
+ }
+#endif
+ *uart_data = c; /* Data */
+}
+
+void tty_setup(uint8_t minor)
+{
+ /* 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);
+ }
+}
+
+void platform_interrupt(void)
+{
+ 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__
+
+extern int nc100_tty_open(uint8_t minor, uint16_t flag);
+extern int nc100_tty_close(uint8_t minor);
+extern void nc100_tty_init(void);
+#endif
--- /dev/null
+section .vectors load 0x0100
+section .discard load 0x2000
+section .data
+section .bss
+section _COMMONMEM load 0x8000
+section .text2
+section .video
+section .romvectors load 0xBFF0
+section .udata load 0x7D00
+section .start load 0xC000
+section .text
+
--- /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.
+
--- /dev/null
+#include "cpu.h"
+
+void *memcpy(void *d, void *s, size_t sz)
+{
+ unsigned char *dp, *sp;
+ 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;
+}
+
+/* Until we pull out the bits of libgcc that are useful instead */
+void abort(void)
+{
+}
+
+void *malloc(size_t size)
+{
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+/* The uarea is already synched to the stash which is written with the
+ process */
+uint8_t *swapout_prepare_uarea(ptptr p)
+{
+ p;
+ return NULL;
+}
+
+/* The switchin code will move the uarea into the process itself, we just
+ need to fix up the u_page pointer */
+uint8_t *swapin_prepare_uarea(ptptr p)
+{
+ p;
+ return NULL;
+}
+
+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)
+{
+}
+
+void map_init(void)
+{
+}
--- /dev/null
+;
+; 6809 Simulation Platform
+;
+
+ .module p6809
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl interrupt_handler
+ .globl _program_vectors
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+
+ ; exported debugging tools
+ .globl _trap_monitor
+ .globl outchar
+ .globl _di
+ .globl _ei
+ .globl _irqrestore
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+ .globl unix_syscall_entry
+ .globl nmi_handler
+
+ include "kernel.def"
+ include "../kernel09.def"
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0xF000 upwards)
+; -----------------------------------------------------------------------------
+ .area _COMMONMEM
+
+trapmsg: .ascii "Trapdoor: SP="
+ .db 0
+trapmsg2: .ascii ", PC="
+ .db 0
+tm_user_sp: .dw 0
+
+_trap_monitor:
+ orcc #0x10
+ bra _trap_monitor
+
+_trap_reboot:
+ lda 0xff90
+ anda #0xfc ; map in the ROM
+ jmp 0
+
+_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
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (below 0xF000, only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+ .area .text
+
+init_early:
+ rts
+
+init_hardware:
+ ; set system RAM size
+ ldd #96
+ std _ramsize
+ ldd #32
+ std _procmem
+ rts
+
+
+ .area .vectors
+;
+; At 0x100 as required by the Dragon ROM
+;
+ jmp badswi_handler ; 0x100
+ jmp badswi_handler ; 0x103
+ jmp unix_syscall_entry ; 0x106
+ jmp nmi_handler ; 0x109
+ jmp interrupt_handler ; 0x10C
+ jmp firq_handler ; 0x10F
+
+;
+; Included if we are replacing the basic ROM and magically mapped from
+; the ROM top (0xBFF0->FFF0)
+;
+ .area .romvectors
+
+ .dw 0x3634 ; Reserved
+ .dw 0x0100
+ .dw 0x0103
+ .dw 0x010f
+ .dw 0x010c
+ .dw 0x0106
+ .dw 0x0109
+ .dw 0xC000 ; Unused (reset)
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+
+ .area _COMMONMEM
+
+;
+; In the Dragon64 case our vectors live in a fixed block
+;
+_program_vectors:
+ rts
+
+;
+; FIXME:
+;
+firq_handler:
+badswi_handler:
+ rti
+
+;
+; The Dragon mapping is a bit tangled
+;
+; SAM ffd4/d5 reading turns off/on the upper 32K of RAM at 0x0-0x7FFF
+; SAM ffde/ffdf makes it appear at 0x8000-0xffef (ff00... is hardwired)
+; PIA 1 B side data reg (ff22) bit 2 switches between the two ROMs
+;
+;
+;
+; All registers preserved
+;
+map_process_always:
+ pshs a
+map_process_2:
+ lda 0xffdf ; RAM please
+ lda #0
+ sta cart_map
+ puls a, pc
+
+map_process:
+ cmpx #0
+ bne map_process_always
+;
+; Map in the kernel below the current common, all registers preserved
+;
+map_kernel:
+ pshs a
+map_kernel_2:
+ lda 0xffde ; RAM out (cart in hopefully)
+ lda #1
+ sta cart_map
+ puls a, pc
+
+map_restore:
+ pshs a
+ lda saved_map
+ cmpa #0
+ beq map_process_2
+ bra map_kernel_2
+;
+; Save the current mapping.
+;
+map_save:
+ pshs a
+ lda cart_map
+ sta saved_map
+ puls a,pc
+
+saved_map: .db 0
+cart_map: .db 0
+
+; outchar: Wait for UART TX idle, then print the char in a
+
+outchar:
+ pshs b
+outcharw:
+ ldb 0xff05
+ bitb #0x04
+ beq outcharw
+ sta 0xff04
+ puls b,pc
--- /dev/null
+;
+; Raw machine set up for running in 'ROM' mode
+;
+ .area .startup
+
+ ; Optimise all this to use the direct page register as 0xffxx
+ orcc #0x10
+ ; Memory 64K dynamic (I guess...)
+ lda 0xffdd
+ lda 0xffda
+ ; Memory low map (cartridge high, RAM low)
+ lda 0xffde ; Map type -> cartridge & rom
+ ; Serial
+ lda #0x01
+ sta 0xff06
+ lda #0x1e
+ sta 0xff07
+
+ ; 0.9Mhz
+ lda 0xffd6 ; R0
+ lda 0xffd8 ; R1
+ ; Put the video at 0x400
+ lda 0xffd2 ; F6 to F1 clear F0 set
+ lda 0xffd0
+ lda 0xffce
+ lda 0xffcc
+ lda 0xffca
+ lda 0xffc8
+ lda 0xffc7
+ ; SAM into ascii mode
+ lda 0xffc0
+ lda 0xffc2
+ lda 0xffc4
+
+ ; PIA0 A is all input
+ ; PIA0 B is all output
+ ; PIA1 A is mixed, both the same (input 0 only)
+ lda #0x4 ; enable access to DDR registers
+ sta 0xff01
+ sta 0xff03
+ sta 0xff21
+ sta 0xff23
+ lda #0x00
+ sta 0xff00
+ lda #0xff
+ sta 0xff02
+ lda #0xfe
+ sta 0xff21
+ sta 0xff23
+ lda #0x0 ; Disable access to DDR registers
+ sta 0xff01
+ sta 0xff03
+ sta 0xff21
+ sta 0xff23
+
+ ; VDG via PIA1
+ lda #0x00
+ ld 0xff22, a ; Graphics, internal text rom
+
+ ;
+ ; Say hello
+ ;
+ ldx 0x400
+ lda #'H'
+ sta ,x+
+
--- /dev/null
+;
+; 6809 version
+;
+ .module tricks
+
+ .globl _newproc
+ .globl _chksigs
+ .globl _getproc
+ .globl _trap_monitor
+ .globl _inint
+ .globl _switchout
+ .globl _switchin
+ .globl _dofork
+ .globl _ramtop
+
+
+ include "kernel.def"
+ include "../kernel09.def"
+
+ .area _COMMONMEM
+
+; ramtop must be in common for single process swapping cases
+; and its a constant for the others from before init forks so it'll be fine
+; here
+_ramtop:
+ .dw 0
+
+; Switchout switches out the current process, finds another that is READY,
+; possibly the same process, and switches it in. When a process is
+; restarted after calling switchout, it thinks it has just returned
+; from switchout().
+;
+; 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
+
+ ; set inint to false
+ lda #0
+ sta _inint
+
+ ; 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
+
+badswitchmsg: .ascii "_switchin: FAIL"
+ .db 13
+ .db 10
+ .db 0
+
+; new process pointer is in X
+_switchin:
+ orcc #0x10 ; irq off
+
+ ldy P_TAB__P_PAGE_OFFSET+3,x
+ ; FIXME: can we skip the usermaps here ?
+ stx 0xffa6 ; map the process uarea we want
+ adda #1
+ stx 0xffa7
+ stx 0xffaf ; and include the kernel mapping
+
+ ; ------- No stack -------
+ ; check u_data->u_ptab matches what we wanted
+ cmpx U_DATA__U_PTAB
+ bne switchinfail
+
+ ; wants optimising up a bit
+ 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
+
+fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
+
+;
+; Called from _fork. We are in a syscall, the uarea is live as the
+; parent uarea. The kernel is the mapped object.
+;
+_dofork:
+ ; always disconnect the vehicle battery before performing maintenance
+ orcc #0x10 ; should already be the case ... belt and braces.
+
+ ; new process in X, get parent pid into y
+
+ stx fork_proc_ptr
+ ldy 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 y ; y 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.
+
+ ; --------- we switch stack copies in this call -----------
+ jsr fork_copy ; copy 0x000 to udata.u_top and the
+ ; uarea and return on the childs
+ ; common
+ ; We are now in the kernel child context
+
+ ; now the copy operation is complete we can get rid of the stuff
+ ; _switchin will be expecting from our copy of the stack.
+ puls y
+
+ 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
+
+fork_copy:
+ ldd U_DATA__U_TOP
+ addd #0x0fff ; + 0x1000 (-1 for the rounding to follow)
+ lsra
+ lsra
+ lsra
+ lsra
+ lsra ; bits 2/1 for 8K pages
+ anda #6 ; lose bit 0
+ adda #2 ; and round up to the next bank (but in 8K terms)
+
+ ldx fork_proc_ptr
+ ldy P_TAB__P_PAGE_OFFSET,x
+ ; y now points to the child page pointers
+ ldx U_DATA__U_PAGE
+ ; and x to the parent
+fork_next:
+; ld a,(hl)
+; out (0x11), a ; 0x4000 map the child
+; ld c, a
+; inc hl
+; ld a, (de)
+; out (0x12), a ; 0x8000 maps the parent
+; inc de
+; exx
+; ld hl, #0x8000 ; copy the bank
+; ld de, #0x4000
+; ld bc, #0x4000 ; we copy the whole bank, we could optimise
+; ; further
+; ldir
+; exx
+; call map_kernel ; put the maps back so we can look in p_tab
+; FIXME: can't map_kernel here - we've been playing with the maps, fix
+; directly
+ suba #1
+ bne fork_next
+
+; ld a, c
+; out (0x13), a ; our last bank repeats up to common
+ ; --- we are now on the stack copy, parent stack is locked away ---
+ rts ; this stack is copied so safe to return on
+
+