--- /dev/null
+Use a modern cc65 from https://github.com/cc65/cc65
+
+
+Our memory mapping looks like this
+
+Bank 0:
+ 0x0000 Kernel DP
+ 0x0100 UData + C stack
+ 0x0200 Bootcode / Replaced with C stack
+ 0x0300 CPU stack (kernel mode)
+ 0x0400 Kernel image
+ 0xF700 7 x 256 byte CPU stacks for processes
+ 0xFE00 I/O page
+ 0xFF00 Vectors
+
+And in user space
+
+ 0x0000 User DP
+ 0x0100 Program (CPU stack is in bank 0)
+ 0xFC00 Udata copy (768 bytes)
+ 0xFF00 Stubs [signal return etc]
+
+We don't yet allow for a split I/D program using two banks and having 64K
+code and 64K data.
+
+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
+----
+- Debug initial sketches
+- Add stubs and correct stub copier (+ syscall vector)
+- Add vectors correctly for the 816
+- Figure out how to set stacks up nicely
+- Test signal handling paths
+- Fix brk() checking [right now its busted entirely]
+- Fix execl() execle() in userspace (so init can be fully tested)
+- Add pre-emption logic to the interrupt return path
+- Fix fork() to copy the user C stacks in dofork()
+- Add swap logic
+
+Optimisations We Need To Do
+--------------------------------------------------------------
+- Only copy the needed memory when forking, not 64K ?
--- /dev/null
+;
+; 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:
+FIXME: C stack of 512 - udata, 65C816 stack follows
+
+;
+; We have a single istack so we can stuff that anywhere we like
+;
+ .bss
+
+istack_base:
+ .res 254,0
+istack_top:
+FIXME: interrupt CPU stack (64 ?)
+istack_switched_sp: .word 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
+/* 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 */
+
+/*
+ * We have 512K of RAM and have to allocate it in banks due to the CPU
+ * bank granularity. That gives us 7 processes plus kernel and more
+ * if we add swap.
+ */
+#define CONFIG_BANK_65C816
+#define MAX_MAPS 7
+#define MAP_SIZE 0xFC00 /* 0-FBFF */
+
+#define TICKSPERSEC 10 /* Ticks per second */
+#define MAPBASE 0x0000 /* We map from 0 */
+#define PROGBASE 0x0100 /* also data base */
+#define PROGLOAD 0x0100
+#define PROGTOP 0xFC00 /* 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() /* for now - wants fixing */
--- /dev/null
+ ; 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 81
+
+ .a8
+ .i8
+ .p816
+
+entry:
+;
+; We are entered at $0202 just after the required magic number
+;
+; We get run from bank 0, our I/O writes would otherwise need to be
+; 24bit
+;
+ sep #$30 ; ensure we are in 8bit mode
+ lda #'F'
+ sta $FE20 ; signal our arrival
+
+ sei ; interrupts off
+ cld ; decimal off
+
+ rep #$10
+ .i16
+ ldx #kstack
+ txs ; Stack (6502 not C)
+
+ lda #'u'
+ sta $FE20
+
+ ldx #kstack_top ; C stack
+ sta sp
+
+ ldx #__BSS_RUN__
+
+ lda #'z'
+ sta $FE20
+
+ txy
+ iny
+
+ ; Wipe the BSS
+
+ rep #$20
+ .a16
+ lda #__BSS_SIZE-2 ; must be >=2 bytes or else
+ clz 0,x
+ mvn 0,0
+
+
+ sep #$30
+ .a8
+ .i8
+
+ 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
+ jmp code
+
+; The above gets blasted into udata space
+ .code
+
+code:
+ rep #$30
+ .a8
+ .i8
+ ldx #$U_DATA
+ ldy #$U_DATA+1
+ lda #$UDATA_TOTALSIZE-2
+ clz 0,x
+ mvn 0,0
+
+ sep #$30
+ .a8
+ .i8
+
+ jsr _fuzix_main ; Should never return
+ sei ; Spin
+stop: jmp stop
+
+ .segment "VECTORS"
+ .addr vector
+ .addr $0202 ; does it matter ???
+ .addr nmi_handler
--- /dev/null
+/*
+ * 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;
+
+ /* FIXME: add swap */
+ if(rawflag == 1 && d_blkoff(9))
+ return -1;
+
+ /* For swap it'll be the swap bank passed */
+ hd_map = rawflag ? udata.u_page : KERNEL_BANK;
+
+ 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;
+ }
+ /* We shouldn't need the di any more */
+ 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);
+}
+
--- /dev/null
+#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__ */
--- /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 <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) - 1)
+ return false;
+ else
+ return true;
+}
+
+void device_init(void)
+{
+}
+
--- /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>
+
+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();
+}
--- /dev/null
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+
+extern void tty_poll(void);
+
+#endif
--- /dev/null
+; UZI mnemonics for memory addresses etc
+
+; (this is struct u_data from kernel.h)
+U_DATA .set $0100 ; stomps over bootstrap
+; 256+256+256 bytes. (U, kstack copy, k C stack copy)
+U_DATA__TOTALSIZE .set $300
+U_DATA_STASH .set $FC00 ; leaves FFxx for vectors and stubs
+
+PROGLOAD .set $0200
+ZPBASE .set $0
--- /dev/null
+MEMORY {
+ RAMZ: start = $0000, size = $0100, type = rw, fill = yes;
+ STACK: start = $0100, size = $0100, type = rw, fill = yes;
+ BOOT: start = $0200, size = $0200, type = rw, fill = yes;
+ MAIN: start = $0400, size = $F300, type = rw, fill = yes;
+ USTACKS:start = $F700, size = $0700, type = rw, fill = yes;
+ IO: start = $FE00, size = $0100, type = rw, fill = yes;
+ VECTOR: start = $FFF0, size = $0010, type = rw, fill = yes;
+}
+
+SEGMENTS {
+ ZEROPAGE: load = RAMZ, type = zp, define = yes;
+ COMMONDATA: load = MAIN, type = bss;
+ CODE: load = MAIN, type = ro, define = yes;
+ RODATA: load = MAIN, type = ro;
+
+ START: load = BOOT, type = ro;
+
+ DATA: load = MAIN, type = rw, define = yes;
+ BSS: load = MAIN, type = bss, define = yes;
+
+ DISCARD: load = MAIN, type = ro;
+ DISCARDDATA: load = MAIN, type = ro;
+
+ VECTORS: load = VECTOR, type = ro;
+}
+
+FILES {
+ %O: format = bin;
+}
--- /dev/null
+#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(char *p)
+{
+ return 0;
+}
--- /dev/null
+export CPU = 658C16
--- /dev/null
+;
+; v65 platform functions
+;
+
+ .export init_early
+ .export init_hardware
+ .export _program_vectors
+
+ ; exported debugging tools
+ .export _trap_monitor
+ .export _trap_reboot
+ .export outchar
+ .export ___hard_di
+ .export ___hard_ei
+ .export ___hard_irqrestore
+ .export vector
+
+ .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 pushax
+
+ .import outcharhex
+ .import outxa
+ .import incaxy
+
+ .import _create_init_common
+
+ .include "kernel.def"
+ .include "../kernel816.def"
+ .include "zeropage.inc"
+
+;
+; syscall is jsr [$00fe]
+;
+syscall = $FE
+
+ .code
+
+_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
+
+init_early:
+ ; copy the stubs from bank 0 to all banks so we can keep the
+ jsr _create_init_common
+ rts
+
+init_hardware:
+ ; set system RAM size for test purposes
+ rep #$10
+ .i16
+ ldx #8
+ stx _ramsize
+ ldx #512-64
+ stx _procmem
+ ; TODO - correct vectors for the 816
+ ldx #vector
+ stx $FFFE
+ ldx #<nmi_handler
+ stx $FFFA
+ ldx #syscall_entry
+ stx #syscall
+
+ rep #$10
+ .i8
+
+ rts
+;
+; We did this at early boot when we set up the vectors and copied
+; stubs everywhere. Only thing we needed in each bank vector wise
+; was syscall (if we keep to 6502 style syscall)
+;
+_program_vectors:
+ rts
+
+; outchar: Wait for UART TX idle, then print the char in a without
+; corrupting other registers
+outchar:
+ sta $0000FE20
+ rts
+
+;
+; Code that will live in each bank
+;
+ .segment "STUBS"
+sigret:
+ pla ; Unstack the syscall return pieces
+ tax
+ pla
+ tay
+ pla
+ plp ; from original stack frame
+ rts
+
+; FIXME: add sig ret interrupt path
+
+
+;
+; I/O logic
+;
+
+
+;
+; Disk copier (needs to be in common), call with ints off
+; for now
+;
+; AX = ptr, length always 512, page in globals
+;
+
+_hd_read_data:
+ sta ptr3
+ stx ptr3+1
+ phd
+ phb
+ rep #$10
+ .i16
+ ldx ptr3 ; buffer address
+ lda _hd_kmap ; page number
+ pha
+ plb ; data now points into user app
+ ldy #00FE
+ phy
+ pld ; DP is now the I/O space
+
+ ldy #512
+ lda $34 ; I/O data via DP
+ sta 0,x ; stores into data (user) bank
+ inx
+ dey
+ bne hd_read
+ plb ; restore bank registers
+ pld
+ sep #$10
+ .i8 ; restore expected CPU state
+ rts
+
+_hd_write_data:
+ sta ptr3
+ stx ptr3+1
+ phd
+ phb
+ rep #$10
+ .i16
+ ldx ptr3 ; buffer address
+ lda _hd_kmap ; page number
+ pha
+ plb ; data now points into user app
+ ldy #00FE
+ phy
+ pld ; DP is now the I/O space
+
+ ldy #512
+ lda 0,x ; load from data (user) bank
+ sta $34 ; I/O data via DP
+ inx
+ dey
+ bne hd_read
+ plb ; restore bank registers
+ pld
+ sep #$10
+ .i8 ; restore expected CPU state
+ rts
+
+ .bss
+
+_hd_map:
+ .res 1
--- /dev/null
+;
+; 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
+