v65: initial platform code
authorAlan Cox <alan@linux.intel.com>
Sun, 13 Nov 2016 00:04:33 +0000 (00:04 +0000)
committerAlan Cox <alan@linux.intel.com>
Sun, 13 Nov 2016 00:04:33 +0000 (00:04 +0000)
This is sufficient to enter user space on the emulator. We don't yet
implement pre-emption on the timer interrupt exit path but the rest is
basically there.

18 files changed:
Kernel/platform-v65/Makefile [new file with mode: 0644]
Kernel/platform-v65/README [new file with mode: 0644]
Kernel/platform-v65/commonmem.s [new file with mode: 0644]
Kernel/platform-v65/config.h [new file with mode: 0644]
Kernel/platform-v65/crt0.s [new file with mode: 0644]
Kernel/platform-v65/devhd.c [new file with mode: 0644]
Kernel/platform-v65/devhd.h [new file with mode: 0644]
Kernel/platform-v65/device.h [new file with mode: 0644]
Kernel/platform-v65/devices.c [new file with mode: 0644]
Kernel/platform-v65/devtty.c [new file with mode: 0644]
Kernel/platform-v65/devtty.h [new file with mode: 0644]
Kernel/platform-v65/kernel.def [new file with mode: 0644]
Kernel/platform-v65/ld65.cfg [new file with mode: 0644]
Kernel/platform-v65/main.c [new file with mode: 0644]
Kernel/platform-v65/target.mk [new file with mode: 0644]
Kernel/platform-v65/tricks.s [new file with mode: 0644]
Kernel/platform-v65/v65.s [new file with mode: 0644]
Kernel/platform-v65/zeropage.inc [new file with mode: 0644]

diff --git a/Kernel/platform-v65/Makefile b/Kernel/platform-v65/Makefile
new file mode 100644 (file)
index 0000000..54b9fab
--- /dev/null
@@ -0,0 +1,33 @@
+
+CSRCS = devtty.c devhd.c
+CSRCS += devices.c main.c
+
+ASRCS = v65.s crt0.s
+ASRCS += tricks.s commonmem.s
+
+COBJS = $(CSRCS:.c=$(BINEXT))
+AOBJS = $(ASRCS:.s=$(BINEXT))
+OBJS  = $(COBJS) $(AOBJS)
+
+JUNK = $(CSRCS:.c=.o) $(CSRCS:.c=.s) $(ASRCS:.s=.o)
+
+all:   $(OBJS)
+
+$(COBJS): %$(BINEXT): %.c
+       $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG1) $<
+
+$(AOBJS): %$(BINEXT): %.s
+       $(CROSS_AS) $(ASOPTS) $< -o $*$(BINEXT)
+
+clean:
+       rm -f $(OBJS) $(JUNK)  core *~ 
+
+image:
+       $(CROSS_LD) -o ../fuzix.bin --mapfile ../fuzix.map -C ld65.cfg crt0.o commonmem.o \
+       v65.o ../start.o ../version.o ../lowlevel-6502.o \
+       tricks.o main.o ../timer.o ../kdata.o devhd.o devices.o \
+       ../devio.o ../filesys.o ../process.o ../inode.o ../syscall_fs.o \
+       ../syscall_proc.o ../syscall_other.o ../mm.o ../bankfixed.o \
+       ../tty.o ../devsys.o ../syscall_fs2.o ../syscall_fs3.o ../syscall_exec16.o \
+       ../usermem.o ../usermem_std-6502.o devtty.o
+       dd if=../fuzix.bin of=fuzix.img bs=512 skip=1
diff --git a/Kernel/platform-v65/README b/Kernel/platform-v65/README
new file mode 100644 (file)
index 0000000..fd901f8
--- /dev/null
@@ -0,0 +1,60 @@
+You need my cc65 git tree, released cc65 can't compile Fuzix as it lacks a
+compiler bug fix I contributed.
+
+Our memory mapping looks like this
+
+       0x0000          ZP
+       0x0100          6502 Stack (per proc) 
+       0x0200          C stack (per proc)
+       0x0400          I stack (per proc)
+       0x0500          Udata actual data per proc
+       0x0600+         Common copy and compiler runtime
+
+       0x2000          Kernel data     (8K)
+       0x4000          Kernel code     (48K)
+                       (currently the top the image holds discard, data
+                       and common copies for initialisation. That needs
+                       tidying up)
+
+And in user space
+
+       0x2000+         User process (with vectors at top)
+
+This ensures we can do all our stack flips in one operation when we switch
+process in switchin.
+
+Things To Do
+
+Debug signals
+
+Checking on the 6502 stack. Probably we should just check for overflows and
+kill, or perhaps copy stacks in/out IFF it would otherwise run out (as 
+Apple ProDOS seems to)
+
+Lots of memory to save in kernel space by making the common and data copies
+come from a bank we then switch out, along perhaps with the const data from
+what would be discard areas on the Z80.
+
+
+To build:
+Set the platform/target
+make clean
+make
+
+and you'll get an image file to dd onto the last 64K of your disk image for
+the emulator.
+
+TODO
+----
+- Signal handling paths
+- Fix brk() checking
+- Interrupts
+
+Optimisations We Need To Do
+--------------------------------------------------------------
+- Only use low bank numbers for low 8K until we have a cache
+- Only copy the needed memory when forking, not 48K (in theory we are copying
+  low->brk, sp->top, S->top of page, and Z)
+- usermem functions that use banking tricks
+- map_save/restore copy/restore entries for kernel mode so we can take an
+  interrupt when we are pulling banking tricks
diff --git a/Kernel/platform-v65/commonmem.s b/Kernel/platform-v65/commonmem.s
new file mode 100644 (file)
index 0000000..2a64624
--- /dev/null
@@ -0,0 +1,41 @@
+;
+;      We keep our common area right down low, with the ZP and stack
+;
+;
+        ; exported symbols
+        .export _ub
+        .export _udata
+        .export kstack_top
+        .export istack_top
+        .export istack_switched_sp
+       .export CTemp
+
+        .segment "COMMONDATA"
+       .include "zeropage.inc"
+
+;
+;      In 6502 land these are the C stacks, we will need to handle the
+;      hardware stack separately, and also to save sp,sp+1 etc on irqs
+;
+;      Declared as BSS so no non zero bytes here please
+;
+_ub:    ; first 512 bytes: starts with struct u_block, with the kernel stack working down from above
+_udata:
+kstack_base:
+       .res 512,0
+kstack_top:
+
+        ; next 256 bytes: 253 byte interrupt stack, then 3 byte saved stack pointer
+istack_base:
+       .res 254,0
+istack_top:
+istack_switched_sp: .word 0
+;
+;      Finally we tack the ZP save area for interrupts on the end
+;
+; Swap space for the the C temporaries (FIXME - we stash sp twice right now)
+;
+CTemp:
+       .res    2               ; sp
+       .res    2               ; sreg
+        .res    (zpsavespace-4) ; Other stuff
diff --git a/Kernel/platform-v65/config.h b/Kernel/platform-v65/config.h
new file mode 100644 (file)
index 0000000..13319b4
--- /dev/null
@@ -0,0 +1,45 @@
+/* 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
+/* Acct syscall support */
+#undef CONFIG_ACCT
+/* Multiple processes in memory at once */
+#define CONFIG_MULTI
+/* Use fixed banks for now. It's simplest and we've got so much memory ! */
+#define CONFIG_BANKS   1
+
+#define CONFIG_CALL_R2L                /* Runtime stacks arguments backwards */
+
+/*
+ *     As we have 1MB of RAM we simply allocate it into 64K per process
+ *     for 15 processes plus kernel. A big server would no doubt want to
+ *     use 16K banks and swapping 8)
+ */
+#define CONFIG_BANK_FIXED
+#define MAX_MAPS       15
+#define MAP_SIZE    0xE000  /* The low 8K is taken up with common space ZP and
+                               S, while we don't currently use the top 8K
+                               (see tricks.s and fix up the fork copy code) */
+
+#define TICKSPERSEC 10     /* Ticks per second */
+#define MAPBASE            0x0000  /* We map from 0 */
+#define PROGBASE    0x2000  /* also data base */
+#define PROGLOAD    0x2000
+#define PROGTOP     0xE000  /* Top of program. If we fixed a few things we
+                               could go to FE00 */
+
+#define BOOT_TTY 513        /* Set this to default device for stdio, stderr */
+
+/* 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 1
+#define TTYDEV   BOOT_TTY /* Device used by kernel for messages, panics */
+#define NBUFS    8        /* Number of block buffers */
+#define NMOUNTS         2        /* Number of mounts at a time */
+
+#define platform_discard()
diff --git a/Kernel/platform-v65/crt0.s b/Kernel/platform-v65/crt0.s
new file mode 100644 (file)
index 0000000..314eeca
--- /dev/null
@@ -0,0 +1,90 @@
+               ; imported symbols
+               .import init_early
+               .import init_hardware
+               .import _fuzix_main
+               .import kstack_top
+               .import vector
+               .import nmi_handler
+
+               .import  __BSS_RUN__, __BSS_SIZE__
+               .importzp       ptr1, ptr2, tmp1
+
+               ; startup code @0
+               .include "zeropage.inc"
+
+;
+;      So we end up first in the image
+;
+               .segment "START"
+               .byte 65
+               .byte 02
+
+entry:
+;
+;      We are entered at $2002 just after the required magic number
+;
+               lda #'F'
+               sta $FE20               ; signal our arrival
+
+               sei                     ; interrupts off
+               cld                     ; decimal off
+               ldx #$FF
+               txs                     ; Stack (6502 not C)
+
+               lda #'u'
+               sta $FE20
+
+               lda #<kstack_top        ; C stack
+               sta sp
+               lda #>kstack_top
+               sta sp+1 
+
+               lda #<__BSS_RUN__
+               sta ptr1
+               lda #>__BSS_RUN__
+               sta ptr1+1
+
+               lda #'z'
+               sta $FE20
+
+               lda #0
+               tay
+               ldx #>__BSS_SIZE__
+               beq bss_wipe_tail
+bss_wiper_1:   sta (ptr1),y
+               iny
+               bne bss_wiper_1
+               inc ptr1+1
+               dex
+               bne bss_wiper_1
+
+bss_wipe_tail:
+               cpy #<__BSS_SIZE__
+               beq gogogo
+               sta (ptr1),y
+               iny
+               bne bss_wipe_tail
+
+gogogo:
+               lda #'i'
+               sta $FE20
+
+               lda #'x'
+               sta $FE20
+
+               jsr init_early
+               lda #'.'
+               sta $FE20
+               jsr init_hardware
+               lda #13
+               sta $FE20
+               lda #10
+               sta $FE20
+               jsr _fuzix_main         ; Should never return
+               sei                     ; Spin
+stop:          jmp stop
+
+               .segment "VECTORS"
+               .addr   vector
+               .addr   $2002           ; does it matter ???
+               .addr   nmi_handler
diff --git a/Kernel/platform-v65/devhd.c b/Kernel/platform-v65/devhd.c
new file mode 100644 (file)
index 0000000..8897942
--- /dev/null
@@ -0,0 +1,79 @@
+/* 
+ * ROMdisc hack for testing
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devhd.h>
+
+extern uint8_t hd_map;
+
+extern void hd_read_data(uint16_t addr);
+extern void hd_write_data(uint16_t addr);
+
+volatile uint8_t *disknum  = (volatile uint8_t *)0xFE30;
+volatile uint8_t *diskcylh = (volatile uint8_t *)0xFE31;
+volatile uint8_t *diskcyll = (volatile uint8_t *)0xFE32;
+volatile uint8_t *diskcmd  = (volatile uint8_t *)0xFE33;
+volatile uint8_t *diskstat = (volatile uint8_t *)0xFE35;
+
+static int hd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+    uint16_t dptr, nb;
+    irqflags_t irq;
+    uint8_t err;
+
+    if(rawflag == 1 && d_blkoff(9))
+        return -1;
+
+    hd_map = rawflag;
+
+    dptr = (uint16_t)udata.u_dptr;
+    nb = udata.u_nblock;
+        
+    while (udata.u_nblock--) {
+        *disknum = minor;
+        *diskcylh = udata.u_block >> 8;
+        *diskcyll = udata.u_block;
+        *diskcmd = 1;
+        if ((err = *diskstat) != 0) {
+            kprintf("hd%d: disk error %x\n", err);
+            udata.u_error = EIO;
+            return -1;
+        }
+        
+        irq = di();
+        if (is_read)
+            hd_read_data(dptr);
+        else
+            hd_write_data(dptr);
+        irqrestore(irq);
+        udata.u_block++;
+        dptr += 512;
+    }
+    return nb;
+}
+
+int hd_open(uint8_t minor, uint16_t flag)
+{
+    used(flag);
+    if(minor != 0) {
+        udata.u_error = ENODEV;
+        return -1;
+    }
+    return 0;
+}
+
+int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    used(flag);
+    return hd_transfer(minor, true, rawflag);
+}
+
+int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    used(flag);
+    return hd_transfer(minor, false, rawflag);
+}
+
diff --git a/Kernel/platform-v65/devhd.h b/Kernel/platform-v65/devhd.h
new file mode 100644 (file)
index 0000000..5b99f00
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __DEVHD_DOT_H__
+#define __DEVHD_DOT_H__
+
+/* public interface */
+int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int hd_open(uint8_t minor, uint16_t flag);
+
+#endif /* __DEVHD_DOT_H__ */
diff --git a/Kernel/platform-v65/device.h b/Kernel/platform-v65/device.h
new file mode 100644 (file)
index 0000000..6f4c1e2
--- /dev/null
@@ -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-v65/devices.c b/Kernel/platform-v65/devices.c
new file mode 100644 (file)
index 0000000..fe94a0b
--- /dev/null
@@ -0,0 +1,39 @@
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devhd.h>
+#include <devsys.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  */
+  {  hd_open,     no_close,    hd_read,   hd_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 */
+  {  no_open,     no_close,   no_rdwr,   no_rdwr,  no_ioctl  },
+  /* 4: /dev/mem etc   System devices (one offs) */
+  {  no_open,      no_close,    sys_read, sys_write, sys_ioctl  },
+  /* Pack to 7 with nxio if adding private devices and start at 8 */
+};
+
+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-v65/devtty.c b/Kernel/platform-v65/devtty.c
new file mode 100644 (file)
index 0000000..9895ea3
--- /dev/null
@@ -0,0 +1,77 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <devtty.h>
+#include <device.h>
+#include <vt.h>
+#include <tty.h>
+
+static volatile uint8_t *uart = (volatile uint8_t *)0xFE20;
+static volatile uint8_t *timer = (volatile uint8_t *)0xFE10;
+
+static char tbuf1[TTYSIZ];
+PTY_BUFFERS;
+
+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},
+       PTY_QUEUES
+};
+
+/* tty1 is the screen tty2 is the serial port */
+
+/* Output for the system console (kprintf etc) */
+void kputchar(uint8_t c)
+{
+       if (c == '\n')
+               tty_putc(1, '\r');
+       tty_putc(1, c);
+}
+
+ttyready_t tty_writeready(uint8_t minor)
+{
+        return TTY_READY_NOW;
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+       minor;
+       uart[0] = c;
+}
+
+void tty_setup(uint8_t minor)
+{
+       minor;
+}
+
+void tty_sleeping(uint8_t minor)
+{
+       minor;
+}
+
+/* For the moment */
+int tty_carrier(uint8_t minor)
+{
+       minor;
+       return 1;
+}
+
+void tty_poll(void)
+{
+        uint8_t x;
+        
+        x = uart[1] & 1;
+        if (x) {
+               x = uart[0];
+               tty_inproc(1, x);
+       }
+}
+                
+void platform_interrupt(void)
+{
+       uint8_t t = *timer;
+       tty_poll();
+       while(t--)
+               timer_interrupt();
+}
diff --git a/Kernel/platform-v65/devtty.h b/Kernel/platform-v65/devtty.h
new file mode 100644 (file)
index 0000000..da0afba
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+
+extern void tty_poll(void);
+
+#endif
diff --git a/Kernel/platform-v65/kernel.def b/Kernel/platform-v65/kernel.def
new file mode 100644 (file)
index 0000000..61bfff4
--- /dev/null
@@ -0,0 +1,9 @@
+; UZI mnemonics for memory addresses etc
+
+; (this is struct u_data from kernel.h)
+U_DATA                      .set $0200
+; 256+256+256 bytes.
+U_DATA__TOTALSIZE           .set $300        
+
+PROGLOAD                   .set $2000
+ZPBASE                     .set $0
diff --git a/Kernel/platform-v65/ld65.cfg b/Kernel/platform-v65/ld65.cfg
new file mode 100644 (file)
index 0000000..eb22106
--- /dev/null
@@ -0,0 +1,38 @@
+MEMORY {
+       RAMZ:   start = $0000, size = $0100, type = rw, fill = yes;
+       STACK:  start = $0100, size = $0100, type = rw, fill = yes;
+       RAM0:   start = $0200, size = $1E00, type = rw, fill = yes;
+       RAM1:   start = $2000, size = $DE00, type = rw, fill = yes;
+       RAM2:   start = $FFFA, size = $0006, type = rw, fill = yes;
+}
+
+SEGMENTS {
+       ZEROPAGE:       load = RAMZ, type = zp, define = yes;
+       COMMONDATA:     load = RAM0, type = bss;
+       COMMONMEM:      load = RAM0, type = rw;
+       CODE:           load = RAM0, type = ro, define = yes;
+       RODATA:         load = RAM0, type = ro;
+       STUBS:          load = RAM0, type = ro, define = yes;
+
+       START:          load = RAM1, type = ro;
+
+       DATA:           load = RAM1, type = rw, define = yes;
+       BSS:            load = RAM1, type = bss, define = yes;
+
+       SEG1:           load = RAM1, type = ro;
+       SEG2:           load = RAM1, type = ro;
+       SEG3:           load = RAM1, type = ro;
+       SYS1:           load = RAM1, type = ro;
+       SYS2:           load = RAM1, type = ro;
+       SYS3:           load = RAM1, type = ro;
+       SYS4:           load = RAM1, type = ro;
+       SYS5:           load = RAM1, type = ro;
+       DISCARD:        load = RAM1, type = ro;
+       DISCARDDATA:    load = RAM1, type = ro;
+
+       VECTORS:        load = RAM2, type = ro;
+}
+
+FILES {
+       %O: format = bin;
+}
diff --git a/Kernel/platform-v65/main.c b/Kernel/platform-v65/main.c
new file mode 100644 (file)
index 0000000..eff6a4e
--- /dev/null
@@ -0,0 +1,40 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+uint8_t kernel_flag = 1;
+
+void platform_idle(void)
+{
+    irqflags_t flags = di();
+    tty_poll();
+    irqrestore(flags);
+}
+
+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;
+    /* Bank 0 is the kernel */
+    for (i = 15 ; i > 0; i--)
+        pagemap_add(i * 8);
+}
+
+void map_init(void)
+{
+}
+
+uint8_t platform_param(unsigned char *p)
+{
+    return 0;
+}
diff --git a/Kernel/platform-v65/target.mk b/Kernel/platform-v65/target.mk
new file mode 100644 (file)
index 0000000..eb53ebc
--- /dev/null
@@ -0,0 +1 @@
+export CPU = 6502
diff --git a/Kernel/platform-v65/tricks.s b/Kernel/platform-v65/tricks.s
new file mode 100644 (file)
index 0000000..d55361c
--- /dev/null
@@ -0,0 +1,312 @@
+;
+;      6502 version
+;
+        .export _switchout
+        .export _switchin
+        .export _dofork
+       .export _ramtop
+       .export _create_init_common
+
+       .import _chksigs
+       .import _trap_monitor
+
+       .import map_kernel
+
+       .import _newproc
+       .import _getproc
+       .import _runticks
+       .import _inint
+       .import outstring
+       .import outxa
+       .import outcharhex
+
+        .include "kernel.def"
+        .include "../kernel02.def"
+       .include "zeropage.inc"
+
+        .segment "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:
+       .word $E000
+
+; 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:
+       sei
+
+        jsr _chksigs
+;
+;      Put the C stack on the CPU stack, and store that in U_SP
+;
+       lda #0          ; return code
+       pha
+       pha
+       lda sp          ; C stack
+       pha
+       lda sp+1
+       pha
+       tsx
+       stx U_DATA__U_SP        ; Save it
+
+        ; set inint to false
+       lda #0
+       sta _inint
+
+        ; find another process to run (may select this one again) returns it
+        ; in x,a
+        jsr _getproc
+        jsr _switchin
+        ; we should never get here
+        jsr _trap_monitor
+
+badswitchmsg: .byte "_switchin: FAIL"
+       .byte 13, 10, 0
+
+;
+;      On entry x,a holds the process to switch in
+;
+_switchin:
+       sei
+       sta     ptr1
+       stx     ptr1+1
+       ; Take a second saved set as we are going to swap stacks and ZP
+       ; with a CPU that hasn't got sufficient registers to keep it on
+       ; CPU
+       sta     switch_proc_ptr
+       stx     switch_proc_ptr+1
+;      jsr     outxa
+       ldy     #P_TAB__P_PAGE_OFFSET
+       lda     (ptr1),y
+;      pha
+;      jsr     outcharhex
+;      pla
+       sta     $FE00           ; switches zero page, stack memory area
+       ; ------- New stack and ZP -------
+
+       ; Set ptr1 back up (the old ptr1 was on the other ZP)
+       lda     switch_proc_ptr
+       sta     ptr1
+       lda     switch_proc_ptr+1
+       sta     ptr1+1
+
+        ; check u_data->u_ptab matches what we wanted
+       lda     U_DATA__U_PTAB
+       cmp     ptr1
+       bne     switchinfail
+       lda     U_DATA__U_PTAB+1
+       cmp     ptr1+1
+       bne     switchinfail
+
+       lda     #P_RUNNING
+       ldy     #P_TAB__P_STATUS_OFFSET
+       sta     (ptr1),y
+
+       lda #0
+       sta _runticks
+       sta _runticks+1
+
+        ; restore machine state -- note we may be returning from either
+        ; _switchout or _dofork
+        ldx U_DATA__U_SP
+       txs
+       pla
+       sta sp+1
+       pla
+       sta sp
+       lda     #'*'
+       sta     $FE20
+       lda _inint
+        beq swtchdone          ; in ISR, leave interrupts off
+       cli
+swtchdone:
+       pla             ; Return code
+       tax
+       pla
+        rts
+
+switchinfail:
+       lda     #'!'
+       sta     $FE20
+       lda     ptr1+1
+       jsr     outcharhex
+       lda     ptr1
+       jsr     outcharhex
+        lda    #<badswitchmsg
+       ldx     #>badswitchmsg
+        jsr outstring
+       ; something went wrong and we didn't switch in what we asked for
+        jmp _trap_monitor
+
+; Must not put this in ZP ?
+;
+; Move to commondata ??
+;
+fork_proc_ptr: .word 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
+        sei     ; should already be the case ... belt and braces.
+
+       ; new process in X, get parent pid into y
+
+       sta fork_proc_ptr
+       stx fork_proc_ptr+1
+
+       ldy #P_TAB__P_PID_OFFSET
+       sta ptr1
+       stx ptr1+1
+
+        ; Save the stack pointer and critical registers.
+       ; 6502 at least doesn't have too many of those 8)
+
+        ; When this process (the parent) is switched back in, it will be as if
+        ; it returns with the value of the child's pid.
+       lda (ptr1),y
+       pha
+       iny
+       lda (ptr1),y
+       pha
+
+        ; 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) the child PID.
+       lda sp
+       pha
+       lda sp+1
+       pha
+       tsx
+       stx U_DATA__U_SP
+
+        ; now we're in a safe state for _switchin to return in the parent
+       ; process.
+
+       ;
+       ;       Assumes ptr1 still holds the new process ptr
+       ;
+
+       jsr fork_copy
+
+       ; --------- we switch stack copies here -----------
+        lda fork_proc_ptr
+       ldx fork_proc_ptr+1
+       sta ptr1
+       stx ptr1+1
+       ldy #P_TAB__P_PAGE_OFFSET
+       lda (ptr1),y
+       sta $FE00                       ; switch to child and child stack
+                                       ; and zero page etc
+       ; 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.
+       pla
+       pla
+       pla
+       pla
+
+        lda fork_proc_ptr
+       ldx fork_proc_ptr+1
+
+        jsr _newproc
+
+       ; any calls to map process will now map the childs memory
+
+        ; runticks = 0;
+       lda #0
+       sta _runticks
+       sta _runticks+1
+
+        ; in the child process, fork() returns zero.
+       tax
+
+       ; 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
+
+;
+;      On entry ptr1 points to the process table of the child, and
+;      the U_DATA is still not fully modified so holds the parents bank
+;      number. This wants optimising to avoid copying all the unused
+;      space!
+;
+fork_copy:
+       ldy #P_TAB__P_PAGE_OFFSET
+       lda (ptr1),y            ; child->p_pag[0]
+       sta $FE03               ; 6000-7FFF
+       lda U_DATA__U_PAGE
+       sta $FE02               ; 4000-5FFF
+
+       ; Now use that window to copy 56K from 0000-DFFF
+
+       jsr bank2bank           ; copies 8K
+       ; This assumes our MMU is r/w. We'd need shadows otherwise
+       inc $FE02               ; next 8K
+       inc $FE03
+       jsr bank2bank           ; copies 8K
+       inc $FE02               ; next 8K
+       inc $FE03
+       jsr bank2bank           ; copies 8K
+       inc $FE02               ; next 8K
+       inc $FE03
+       jsr bank2bank           ; copies 8K
+       inc $FE02               ; next 8K
+       inc $FE03
+       jsr bank2bank           ; copies 8K
+       inc $FE02               ; next 8K
+       inc $FE03
+       jsr bank2bank           ; copies 8K
+       inc $FE02               ; next 8K
+       inc $FE03
+       jsr bank2bank           ; copies 8K
+       jmp map_kernel          ; put the kernel mapping back as it should be
+       
+bank2bank:                     ;       copy 4K between the blocks mapped
+                               ;       at 0x4000 and 0x6000
+       lda #$40
+       sta ptr3+1
+       lda #$60
+       sta ptr4+1
+       lda #0
+       sta ptr3
+       sta ptr4
+       tay
+       ldx #$20                ;       32 x 256 bytes = 8K
+copy1:
+       lda (ptr3),y
+       sta (ptr4),y
+       iny
+       bne copy1
+       inc ptr3+1
+       inc ptr4+1
+       dex
+       bne copy1
+       rts
+
+_create_init_common:
+       lda #$00
+       sta $FE02
+       lda #$08
+       sta $FE03
+       jsr bank2bank
+       jmp map_kernel
+;
+;      The switch proc pointer cannot live anywhere in common as we switch
+;      common on process switch
+;
+       .data
+
+switch_proc_ptr: .word 0
diff --git a/Kernel/platform-v65/v65.s b/Kernel/platform-v65/v65.s
new file mode 100644 (file)
index 0000000..13853a4
--- /dev/null
@@ -0,0 +1,650 @@
+;
+;          v65 platform functions
+;
+
+            .export init_early
+            .export init_hardware
+            .export _program_vectors
+           .export map_kernel
+           .export map_process
+           .export map_process_always
+           .export map_save
+           .export map_restore
+
+           .export _unix_syscall_i
+           .export _platform_interrupt_i
+           .export platform_doexec
+
+            ; exported debugging tools
+            .export _trap_monitor
+           .export _trap_reboot
+            .export outchar
+           .export ___hard_di
+           .export ___hard_ei
+           .export ___hard_irqrestore
+           .export vector
+
+           .import interrupt_handler
+           .import _ramsize
+           .import _procmem
+           .import nmi_handler
+           .import unix_syscall_entry
+           .import kstack_top
+           .import istack_switched_sp
+           .import istack_top
+           .import _unix_syscall
+           .import _platform_interrupt
+           .import _kernel_flag
+           .import stash_zp
+           .import pushax
+
+           .import outcharhex
+           .import outxa
+           .import incaxy
+
+           .import _create_init_common
+
+            .include "kernel.def"
+            .include "../kernel02.def"
+           .include "zeropage.inc"
+
+;
+;      syscall is jsr [$00fe]
+;
+syscall             =  $FE
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0x0200 upwards after the common data blocks)
+; -----------------------------------------------------------------------------
+            .segment "COMMONMEM"
+
+_trap_monitor:
+           jmp _trap_monitor
+
+_trap_reboot:
+           lda #$A5
+           sta $FE40           ; Off
+           jmp _trap_reboot    ; FIXME: original ROM map and jmp
+
+___hard_di:
+           php
+           sei                 ; Save old state in return to C
+           pla                 ; Old status
+           rts
+___hard_ei:
+           cli                 ; on 6502 cli enables IRQs!!!
+           rts
+
+___hard_irqrestore:
+           and #4              ; IRQ flag
+           beq irq_on
+           cli
+           rts
+irq_on:
+           sei
+           rts
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+            .code
+
+init_early:
+           ; copy the low 8K from bank 0 to bank 8 so we can keep the
+           ; simple model of 8-15 process 16-24 process ....
+           ; bank 0 is effectively spare at this point and can be
+           ; reused.
+           jsr _create_init_common
+           lda #$08
+           sta $FE00
+            rts
+
+init_hardware:
+            ; set system RAM size for test purposes
+           lda #0
+           sta _ramsize
+           lda #4
+           sta _ramsize+1
+           lda #192
+           sta _procmem
+           lda #3
+           sta _procmem+1
+            jmp program_vectors_k
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+            .segment "COMMONMEM"
+
+_program_vectors:
+            ; we are called, with interrupts disabled, by both newproc() and crt0
+           ; will exit with interrupts off
+           sei
+           ;
+           ; our C caller will invoke us with the pointer in x,a
+           ; just pass it on
+           jsr map_process
+program_vectors_k:
+           lda #<vector
+           sta $FFFE
+           lda #>vector
+           sta $FFFF
+           lda #<nmi_handler
+           sta $FFFA
+           lda #>nmi_handler
+           sta $FFFB
+           ; However tempting it may be to use BRK for system calls we
+           ; can't do this on an NMOS 6502 because the chip has brain
+           ; dead IRQ handling bits that could simply "lose" the syscall!
+           lda #<syscall_entry
+           sta syscall
+           lda #>syscall_entry
+           sta syscall+1
+           jmp map_kernel
+
+;
+;      On a fully switching system with far copies like the 6509 this is
+;      all basically a no-op
+;
+;      On a banked setup the semantics are:
+;
+;      map_process_always()
+;      Map the current process (ie the one with the live uarea)
+;
+;      map_kernel()
+;      Map the kernel
+;
+;      map_process(pageptr [in X,A])
+;      if pageptr = 0 then map_kernel
+;      else map_process using the pageptr
+;
+;      map_save
+;      save the current mapping
+;
+;      map_restore
+;      restore the saved mapping
+;
+;      save/restore are used so that the kernel can play with its internal
+;      banking/mappings without having to leave interrupts off all the time
+;
+;      ptr1 and tmp1 may be destroyed by these methods, but no other
+;      temporaries.
+;
+map_process_always:
+           pha
+           txa
+           pha
+           ldx U_DATA__U_PAGE
+           jsr map_bank_i
+           pla
+           tax
+           pla
+           rts
+;
+;      X,A points to the map table of this process
+;
+map_process:
+           cmp #0
+           bne map_process_2
+           cpx #0
+           bne map_process_2
+;
+;      Map in the kernel below the current common, all registers preserved
+;      the kernel lives in 40/42/43/.... (41 has the reset vectors in so
+;      we avoid it. Later we'll be clever and stuff _DISCARD and the copy
+;      blocks there or something (that would also let us put RODATA in
+;      common area just to balance out memory usages).
+;
+map_kernel:
+           pha
+           txa
+           pha
+                               ; Common is left untouched as is ZP and S
+           ldx #$01            ; Kernel RAM at 0x2000-0x3FFF
+           jsr map_bank
+           pla
+           tax
+           pla
+           rts
+
+;
+;      Entry point to map a linear bank range
+;
+map_bank_i:                    ; We are not mapping the first user page yet
+           inx
+map_bank:
+           stx $FE01
+           inx
+           stx $FE02
+           inx
+           stx $FE03
+           inx
+           stx $FE04
+           inx
+           stx $FE05
+           inx
+           stx $FE06
+;          inx                 ; We don't use bank 7 copies yet
+;          stx $FE07
+           rts
+
+; X,A holds the map table of this process
+map_process_2:
+           sta ptr1
+           stx ptr1+1
+           tya
+           pha
+           ldy #0
+           lda (ptr1),y        ; 4 bytes if needed
+           tax
+           pla
+           tay
+           jmp map_bank_i
+
+
+;
+;      Restore mapping. This may not be sufficient. We may need to do a
+;      careful 6 byte save/restore if we do clever stuff in future
+;
+map_restore:
+           pha
+           txa
+           pha
+           ldx saved_map       ; First bank we skip half of
+           jsr map_bank
+           pla
+           tax
+           pla
+           rts
+
+;
+;      Save the current mapping.
+;      May not be sufficient if we want IRQs on while doing page tricks
+;
+map_save:
+           pha
+           lda $FE01
+           sta saved_map
+           pla
+           rts
+
+saved_map:  .byte 0, 0, 0, 0
+
+; outchar: Wait for UART TX idle, then print the char in a without
+; corrupting other registers
+
+outchar:
+           sta $FE20
+           rts
+
+;
+;      Code that will live in each bank at the same address and is copied
+;      there on 6509 type setups. On 6502 it may well be linked once in
+;      common space
+;
+       .segment "STUBS"
+;
+;      Interrupt vector logic. Keep this in platform for the 6502 so that
+;      we can use the shorter one for the CMOS chip
+;
+vector:
+           pha
+           txa
+           pha
+           tya
+           pha
+           cld
+;
+;      Q: do we want to spot brk() instructions and signal them ?
+;
+;
+;      Save the old stack ptr
+;
+           jsr stash_zp                        ; Save zero page bits
+           tsx                                 ; and save the 6502 stack ptr
+           stx istack_switched_sp              ; in uarea/stacks
+;
+;      Hope the user hasn't used all the CPU stack
+;
+;      FIXME: we should check here if S is too low and if so set it high
+;      and deliver SIGKILL
+;
+;      Configure the C stack to the i stack
+;
+           lda #<istack_top
+           sta sp
+           lda #>istack_top
+           sta sp+1
+           jsr interrupt_handler
+;
+;      Reload the previous value into the stack ptr
+;
+           ldx istack_switched_sp
+           txs                                 ; recover 6502 stack
+           jsr stash_zp                        ; restore zero page bits
+
+;
+;      TODO: pre-emption
+;
+
+
+
+;
+;      Signal handling on 6502 is foul as the C stack may be inconsistent
+;      during an IRQ. We push a new complete rti frame below the official
+;      one, along with a vector and the signal number. The glue in the
+;      app is expected to switch to a signal stack or similar, pop the
+;      values, invoke the signal handler and then return.
+;
+;      FIXME: at the moment the irqout path will not check for multiple
+;      signals so the next one gets delivered next irq.
+;
+;
+           lda U_DATA__U_CURSIG
+           beq irqout
+           tay
+           tsx
+           txa
+           sec
+           sbc #6                      ; move down past the existing rti
+           tax
+           txs
+           lda #>irqout
+           pha
+           lda #<irqout
+           pha                         ; stack a return vector
+           tya
+           pha                         ; stack signal number
+           ldx #0
+           stx U_DATA__U_CURSIG
+           asl a
+           tay
+           lda U_DATA__U_SIGVEC,y      ; Our vector (low)
+           pha                         ; stack half of vector
+           lda U_DATA__U_SIGVEC+1,y    ; High half
+           pha                         ; stack rest of vector
+           txa
+           sta U_DATA__U_SIGVEC,y      ; Wipe the vector
+           sta U_DATA__U_SIGVEC+1,y
+           lda #<PROGLOAD + 20
+           pha
+           lda #>PROGLOAD + 20
+           lda #0
+           pha                         ; dummy flags, with irq enable
+           rti                         ; return on the fake frame
+                                       ; if the handler returns
+                                       ; rather than doing a longjmp
+                                       ; we'll end up at irqout and pop the
+                                       ; real frame
+irqout:
+           pla
+           tay
+           pla
+           tax
+           pla
+           rti
+;
+;          sp/sp+1 are the C stack of the userspace
+;          with the syscall number in X
+;          Y indicates the number of bytes of argument
+;
+syscall_entry:
+           php
+           sei
+           cld
+
+           stx U_DATA__U_CALLNO
+
+           ; No arguments - skip all the copying and stack bits
+           cpy #0
+           beq noargs
+
+           ; Remove the arguments. This is fine as by the time we go back
+           ; to the user stack we'll have finished with them
+           lda sp
+           sta ptr1
+           ldx sp+1
+           stx ptr1+1
+           jsr incaxy
+           sta sp
+           stx sp+1
+
+           ;
+           ;   We copy the arguments but need to deal with the compiler
+           ;   stacking in the reverse order. At this point ptr1 points
+           ;   to the last byte of the arguments (first argument). We go
+           ;   down the stack copying words up the argument list.
+           ;
+           ldx #0
+copy_args:
+           dey
+           lda (ptr1),y                ; copy the arguments over
+           sta U_DATA__U_ARGN+1,x
+           dey
+           lda (ptr1),y
+           sta U_DATA__U_ARGN,x
+            inx
+           inx
+           cpy #0
+           bne copy_args
+noargs:
+           ;
+           ; Now we need to stack switch. Save the adjusted stack we want
+           ; for return
+           ;
+           lda sp
+           pha
+           lda sp+1
+           pha
+           tsx
+           stx U_DATA__U_SYSCALL_SP
+;
+;      We save a copy of the high byte of sp here as we may need it to get
+;      the brk() syscall right.
+;
+           sta U_DATA__U_SYSCALL_SP + 1
+;
+;
+;      FIXME: we should check here if there is enough 6502 stack left
+;      and if so either copy and switch stacks or kill the process
+;
+;      Set up the C stack
+;
+           lda #<kstack_top
+           sta sp
+           lda #>kstack_top
+           sta sp+1
+
+           cli
+;
+;      Caution: We may enter here and context switch and another task
+;      exit via its own syscall returning in its own memory context.
+;
+;      Don't assume anything we stored statically *except* the uarea
+;      will be different. The uarea is banked in and out (or copied in
+;      more awkward systems).
+;
+           jsr unix_syscall_entry
+
+           sei
+;
+;      Correct the system stack
+;
+           ldx U_DATA__U_SYSCALL_SP
+           txs
+;
+;      From that recover the C stack and the syscall buf ptr
+;
+           pla
+           sta sp+1
+           pla
+           sta sp
+           lda U_DATA__U_CURSIG
+           beq syscout
+           tay
+
+           tsx                         ; Move past existing return stack
+           dex
+           dex
+           dex
+           txs
+
+           ;
+           ;   The signal handler might make syscalls so we need to get
+           ;   our return saved and return the right value!
+           ;
+           lda U_DATA__U_ERROR
+           pha
+           lda U_DATA__U_RETVAL
+           pha
+           lda U_DATA__U_RETVAL+1
+           pha
+           lda #>sigret                ; Return address
+           pha
+           lda #<sigret
+           pha
+
+           tya
+           pha                         ; signal
+           ldx #0
+           stx U_DATA__U_CURSIG
+           asl a
+           tay
+           lda U_DATA__U_SIGVEC,y      ; Our vector
+           pha
+           lda U_DATA__U_SIGVEC+1,y
+           pha
+           txa
+           sta U_DATA__U_SIGVEC,y      ; Wipe the vector
+           sta U_DATA__U_SIGVEC+1,y
+
+           ; Invoke the helper with signal and vector stacked
+           ; it will then return to syscout and recover the original
+           ; frame. If the handler made syscalls then
+           jmp (PROGLOAD + 20)
+
+           ;
+           ; FIXME: should loop for more signals if appropriate
+           ;
+sigret:
+           pla         ; Unstack the syscall return pieces
+           tax
+           pla
+           tay
+           pla
+           plp         ; from original stack frame
+           rts
+
+syscout:
+;      We may be in decimal mode beyond this line.. take care
+;
+           plp
+
+;      Copy the return data over
+;
+           ldy U_DATA__U_RETVAL
+           ldx U_DATA__U_RETVAL+1
+;      Also sets Z for us
+           lda U_DATA__U_ERROR
+
+           rts
+
+platform_doexec:
+;
+;      Start address of executable
+;
+           stx ptr1+1
+           sta ptr1
+
+;
+;      Set up the C stack
+;
+           lda U_DATA__U_ISP
+           sta sp
+           ldx U_DATA__U_ISP+1
+            stx sp+1
+
+;
+;      Set up the 6502 stack
+;
+           ldx #$ff
+           txs
+           ldx #>PROGLOAD      ; For the relocation engine
+           lda #ZPBASE
+           jmp (ptr1)          ; Enter user application
+
+;
+;      Straight jumps no funny banking issues
+;
+_unix_syscall_i:
+           jmp _unix_syscall
+_platform_interrupt_i:
+           jmp _platform_interrupt
+
+
+;
+;      Disk copier (needs to be in common), call with ints off
+;      for now
+;
+;      AX = ptr, length always 512, src and page in globals
+;
+;      Uses ptr3/4 as 1/2 are reserved for the mappers
+;
+
+       .export _hd_read_data,_hd_write_data,_hd_map
+
+_hd_read_data:
+       sta ptr3
+       stx ptr3+1              ; Save the target
+
+       ;
+       ;       We must flip banks before we play mmu pokery, or it will
+       ; undo all our work. This means our variables must be commondata
+       ;
+       lda _hd_map
+       beq hd_kmap
+       jsr map_process_always
+hd_kmap:
+       ldy #0
+       jsr hd_read256
+       inc ptr3+1
+       jsr hd_read256
+       jsr map_kernel
+       rts
+
+hd_read256:
+       lda $FE34
+       sta (ptr3),y
+       iny
+       bne hd_read256
+       rts
+
+_hd_write_data:
+       sta ptr3
+       stx ptr3+1              ; Save the target
+
+       ;
+       ;       We must flip banks before we play mmu pokery, or it will
+       ; undo all our work. This means our variables must be commondata
+       ;
+       lda _hd_map
+       beq hd_kmapw
+       jsr map_process_always
+hd_kmapw:
+       ldy #0
+       jsr hd_write256
+       inc ptr3+1
+       jsr hd_write256
+       jsr map_kernel
+       rts
+
+hd_write256:
+       lda (ptr3),y
+       sta $FE34
+       iny
+       bne hd_write256
+       rts
+
+       .segment "COMMONDATA"
+
+_hd_map:
+       .res 1
diff --git a/Kernel/platform-v65/zeropage.inc b/Kernel/platform-v65/zeropage.inc
new file mode 100644 (file)
index 0000000..1ba0358
--- /dev/null
@@ -0,0 +1,26 @@
+;
+; zeropage.inc
+;
+; (C) Copyright 2002-2012, Ullrich von Bassewitz (uz@cc65.org)
+;
+
+; Assembler include file that imports the runtime zero page locations used
+; by the compiler, ready for usage in asm code.
+
+
+        .globalzp       sp, sreg, regsave
+        .globalzp       ptr1, ptr2, ptr3, ptr4
+        .globalzp       tmp1, tmp2, tmp3, tmp4
+        .globalzp       regbank
+  
+; The size of the register bank
+regbanksize     = 6
+
+; The total amount of zero page space used
+zpspace         = 26
+
+; The amount of space that needs to be saved by an interrupt handler that
+; calls C code (does not include the register bank, which is saved by the
+; generated C code if required).
+zpsavespace     = zpspace - regbanksize
+