--- /dev/null
+
+CSRCS = devtty.c devhd.c
+CSRCS += devices.c main.c
+
+ASRCS = appleii.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 \
+ appleii.o ../start.o ../version.o ../lowlevel-6502.o ../simple.o \
+ ../swap.o \
+ tricks.o main.o ../timer.o ../kdata.o devhd.o devices.o ../vt.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
--- /dev/null
+Early Draft For Apple IIe
+
+Assumptions:
+- Single process in memory at a time
+- 128K machine
+- Some kind of timer/clock present that can provide an interrupt
+
+System Problems To Solve:
+- Do we call disk devices via the PASCAL or ProDOS firmware gloop
+- Can some or any of the devices handle being asked to do I/O to alt
+ bank and/or language card spaces
+- How to drive the 140K floppies
+- C000-CFFF are unavailable putting a big hole mid memory, how do we handle
+ that sanely on the user side.
+- Can we do more banking tricks to get more of Fuzix in language card space
+- How do we cleanly deal with Z and S when we can't load directly into them
+- How do you tell a ram drive from a hard disk
+- How do we pick the right swap device
+- Can we put discard over other bits of low memory so we at least can blow
+ some of it away into the second text screen and maybe merge the rest with
+ buffers
+- Glue for talking to the mouse, appletalk, clock etc
+- Can we bank the syscall blocks to make best use of the wacky 4K window
+ in order to get networking in ?
+- Can we stuff fonts into an alt bank if we do graphics (eg on ramworks)
+- Can we hide the tty buffers in the 4K funny altbank bit ?
+- Non sucky console I/O
+
+Architectural Problems To Solve:
+- Support interrupt enabled simple swap mode
+- The swapper has no understanding of the fact we have not just udata/C stack
+ to swap along with user space but also a kernel S and Z
+- There is no way to move the S stack around. The tricks.s proposed code has
+ a suggested trick but that implies we'll need to play interesting games
+ at swap time
+- Pre-emption in IRQ logic for 6502 Apple style
+- 6502 dynamic loader and ZP assignment is needed for AppleIIe. Also maybe
+ an opportunity to tweak the binary format to create two banks normally
+ relocated together as one, but can be split (eg stuff libc/runtime high).
+ - and perhaps factors in to the 'shared library' idea where we have the
+ shared library in high memory with a jump table, and a small block of
+ app space for writable library data. Runtime however would be too expensive
+ to jump table so would need to build a fixed size version that matched up
+ on 6502 and 65C02 - doable but tedious.
+- HTF do we load it all into memory ? Do we need a ProDOS loader where we
+ just blow up /RAM, load all the extras into alt bank from files and then
+ do a giant shuffle when we kill off ProDOS ?
+
+Later
+
+- Add a multiple binary kernel to use RAMworks banking
+- Can we do double hires then ?
+
+
+Use a modern cc65 from https://github.com/cc65/cc65
+
+Our planned memory mapping looks like this
+
+ 0000-00FF Zero Page
+ 0100-01FF Stack (Kernel/Interrupt/Firmware usually)
+ 0200-03FF Firmware reserved for now (to be cautious)
+ 0400-07FF Half of 80 column display / All of 40
+ 0800-1FFF Discard area
+ 2000-BEFF Kernel load and execution area (covers double
+ hi-res space)
+ BF00-BFFF ProDOS leaves us info here before we kill it off
+ C000-CFFF I/O
+ D000-FFFF Kernel (with a 4K extra overlay if we need it)
+
+And in alt banks
+
+ 0000-00FF Alt ZP (used for user space and a few bytes for
+ our IRQ helpers etc)
+ 0100-01FF User stack
+ 0200-03FF Reserve for now
+ 0400-07FF Half of 80 column display / Free in 40
+ 0800-BFFF Application 47.5 K
+ C000-CFFF I/O
+ D000-FEFF ?? (cc65 runtime 'shared' libc ?)
+ FF00-FFFF Stubs and extra IRQ paging logic
+
+For multiple bank (Ramworks) we could instead use bank 2+ for most apps
+and bank 0 would then be kernel with a hole at 2000-3FFF for video and the
+rest for the OS (with the video output/scroll etc hiding in the 4K hole in
+the language ram space along with the font)
+
+
+TODO
+----
+- Most of the work! This is just a sketch but in particular I've yet to
+ * Write the hack code that preserves a bit of S for swapping
+ * Write any of the glue to the protocol convertor methods
+ * Figure out how to load it off disk (some kind of prodos boot
+ strap system file or fuzix itself loading the rest of the code ?
+ (could we do some kind of unpack or is there too much non zero ?)
+ Trouble is we only get 2000-BEFF which is 40K, or 0800-BEFF with
+ the cc65 loader (46848 bytes)
+
+ For unpacking we might need to build an image with the data, udata
+ and bss all low (just over 8K) and stuff the loader into it at
+ $2000 so that we do
+
+ Load at $2000
+ Move blocks about
+ Jump out of BSS
+ wipe BSS
+
+ By my maths it still wouldn't fit.
+
+ Maybe for that matter boot in DOS3 as it won't muck about with
+ banks on us ?
+
+- Look at banking system calls. The core kernel is designed to allow the
+ syscalls to be banked (SYS1-SYS5), each of which is about $A00 bytes. That
+ would let us hide the syscalls in the language ROM space along with common
+ and use the awkward other 4K. We might need to do this to get network in.
+
+
+- Test signal handling paths
+- Fix brk() checking
+- Fix execl() execle() in userspace (so init can be fully tested)
+- Add pre-emption logic to the interrupt return path (and a clock based
+ check for non-timer machines on syscall paths is needed in the core code
+ as it's not yet a supported configuration)
+
+
+
--- /dev/null
+;
+; Apple IIe 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
+
+ .export _hd_map
+
+ ; 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] (for now anyway)
+;
+syscall = $FE
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0x0200 upwards after the common data blocks)
+; -----------------------------------------------------------------------------
+ .segment "COMMONMEM"
+
+;
+; Fixme - can we get back to the AppleII monitor ?
+;
+_trap_monitor:
+ jmp _trap_monitor
+
+_trap_reboot:
+ 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
+ bne irq_on
+ cli
+ rts
+irq_on:
+ sei
+ rts
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+ .code
+
+init_early:
+ rts
+
+init_hardware:
+ ; set system RAM size for test purposes
+ lda #128
+ sta _ramsize
+ lda #0
+ sta _ramsize+1
+ lda #64
+ sta _procmem
+ lda #0
+ sta _procmem+1
+ rts
+;
+; We will set the vectors and stubs up early in boot for both banks so
+; need do nothing here.
+;
+_program_vectors:
+ rts
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+ .segment "COMMONMEM"
+;
+; For swap only this is trivial. The later iie + ramworks kernel
+; will have far more to do as we'll need to load a page register
+; from the process page. We need these routines in common so always
+; available. (Probably crt0.s will need to coppy commonmem to both
+; language banks not just the vectors)
+;
+; Select the auxiliary memory between $200 and $BFFF. If we copy
+; common high into each bank then we'll want to switch the language
+; bank around too
+;
+map_process_always:
+ sta $C003
+ sta $C005
+ sta $C055
+ rts
+;
+; X,A points to the map table of this process but it doesn't
+; matter as we have one process
+;
+map_process:
+ cmp #0
+ bne map_process_always
+ cpx #0
+ bne map_process_always
+;
+; Map in the kernel
+; $200-$BFFF from main bank (we don't flip ALTZP in core kenrel that
+; would cause a train wreck). Note that once we do syscall banking
+; we have to think *which* language bank mapping to set.
+;
+map_kernel:
+ sta $C002
+ sta $C004
+ sta $C054
+ rts
+
+;
+; Restore mapping. This may not be sufficient. We may need to do a
+; complex save/restore if we do clever stuff in future so that an
+; interrupt can occur while we are in other mappings or when we have
+; varied language mappings. Think about 80 col video as a starter
+;
+map_restore:
+ lda saved_map
+ beq map_kernel
+ bne map_process_always
+;
+; Save the current mapping. For the moment we just track if we are
+; user or kernel. We may eventually need to track language bank
+; switches, video and more.
+;
+map_save:
+ pha
+ lda $C013
+ and #$80
+ sta saved_map
+ pla
+ rts
+
+saved_map: .byte 0
+
+; outchar: Wait for UART TX idle, then print the char in a without
+; corrupting other registers
+
+outchar:
+; We don't have an easy way to do this - could vtoutput ??
+ rts
+
+;
+; Code that will live in each bank at the same address and is copied
+; there at boot.
+;
+ .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
+
+; Remember where we are coming from. Also remember we may not be
+; going back quite the same way. For single process in memory however
+; it's not too hard - if we are in kernel mapping an IRQ will always
+; exit in kernel mapping.
+
+ jsr map_save
+ sta $C008 ; Kernel ZP and stack
+
+;
+; 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
+
+ lda saved_map ; we entered kernel map
+ beq exit_as_kernel ; so we exit that way
+ ; no premption or signals
+
+;
+; 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.
+;
+
+ lda U_DATA__U_CURSIG
+ beq irqout
+
+
+ sta $C009 ; Back to the users S and ZP
+ 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:
+ jsr map_restore
+exit_as_kernel:
+ 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
+;
+; Now switch to the kernel ZP/S
+;
+ sta $C008
+ ldx #$FF
+ txs
+;
+;
+; 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
+
+ sta $C009 ; Switch to user stsck and Zp
+
+ 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
+
+;
+; IRQ must be off on entry
+;
+platform_doexec:
+;
+; Start address of executable
+;
+ stx ptr2+1 ; Point ptr2 at base + 0x20
+ sta ptr2
+ ldy #$20
+ lda (ptr2),y ; Get the signal vector pointer
+ sta PROGLOAD+$20 ; if we loaded high put the vector in
+ iny
+ lda (ptr2),y
+ sta PROGLOAD+$21 ; the low space where it is expected
+
+ lda ptr2
+
+;
+; Switch zero page and CPU stack to user
+;
+ sta $C009 ; Auxiliary stack and SP selected
+
+ stx ptr1+1
+ sta ptr1
+
+
+;
+; Set up the C stack. FIXME: assumes for now our sp in ZP matches it
+;
+ 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
+ ldy #0
+ jmp (ptr1) ; Enter user application
+
+;
+; Straight jumps no funny banking issues yet
+; If we start using the alternate language page we'll need to save
+; and restre that state plus have stubs in both copies.
+;
+_unix_syscall_i:
+ jmp _unix_syscall
+_platform_interrupt_i:
+ jmp _platform_interrupt
+
+ .segment "COMMONDATA"
+
+_hd_map:
+ .res 1
+
+ .code
+ ; Dummy stubs for now
+ .export _block_rw
+
+_block_rw:
+ rts
--- /dev/null
+;
+; User data, C kernel stacks, C interrupt stacks and ZP switch for
+; Fuzix. These need to always be visible when running C code.
+;
+ ; 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
--- /dev/null
+//#define CONFIG_RAMWORKS
+
+#ifdef CONFIG_RAMWORKS
+/* 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_BANK_FIXED
+#define MAX_MAPS 15
+#define MAP_SIZE 0xB800
+#else
+/* One process in memory for base 128K system - 6502 just isnt compact enough
+ for a two process setup as Z80 would do for 128K */
+#define CONFIG_BANKS 1 /* 1 bank per process */
+#define CONFIG_MULTI /* Multi-tasking */
+#define CONFIG_SWAP_ONLY /* One process in memory rest by swap */
+#define CONFIG_SPLIT_UDATA /* We'll need to do some work on this.. */
+#define UDATA_SIZE 512
+#define UDATA_BLKS 1
+
+/* HACK for now */
+#define SWAPDEV (1 << 5)
+#endif
+
+
+/* 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
+
+#define CONFIG_CALL_R2L /* Runtime stacks arguments backwards */
+
+#define TICKSPERSEC 50 /* Ticks per second. Needs work as will vary */
+#define MAPBASE 0x0800 /* We map from 0x0800 */
+#define PROGBASE 0x0800 /* also data base */
+#define PROGLOAD 0x0800
+#define PROGTOP 0xC000 /* When we hit the data space */
+
+#define SWAP_SIZE 0x60 /* 48K - allow for udata and our magic */
+#define SWAPBASE 0x0800
+#define SWAPTOP 0xC000
+#define MAX_SWAPS PTABSIZE
+#define swap_map(x) ((uint8_t *)(x))
+
+/* Support a 40 column console for now */
+#define CONFIG_VT
+#define VT_RIGHT 39
+#define VT_BOTTOM 24
+
+#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 /* For now until we add console switches */
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+#define NBUFS 6 /* Number of block buffers */
+#define NMOUNTS 3 /* Number of mounts at a time */
+
+#define platform_discard()
--- /dev/null
+;
+;
+; When we knock this into proper shape it'll need to begin by using
+; ProDOS to load the chunks of image and stuff them into bank 1, then
+; kill off ProDOS and move them into the language space.
+;
+; For now we put discard low, so we can in theory switch video mode
+; and blow it away (at least for lower resolution modes)
+;
+;
+ ; 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"
+
+entry:
+;
+; We are entered at $2000 from ProDOS
+;
+ lda #'1'
+ sta $0400 ; signal our arrival
+
+ sei ; interrupts off
+ cld ; decimal off
+ ldx #$FF
+ txs ; Stack (6502 not C)
+
+ sta $C000 ; 40 column
+ sta $C050 ; text
+ sta $C054 ; page 1
+ sta $C00F ; alt char set on
+ sta $C000 ; 80 store off
+
+ lda #'2'
+ sta $0400
+
+ 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 #'3'
+ sta $0400
+
+ 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 #'4'
+ sta $0400
+
+ jsr init_early
+ lda #'5'
+ sta $0400
+ jsr init_hardware
+ lda #'6'
+ sta $0400
+ jsr _fuzix_main ; Should never return
+ sei ; Spin
+stop: jmp stop
+
+ .segment "VECTORS"
+ .addr vector
+ .addr $2000 ; does it matter ???
+ .addr nmi_handler
--- /dev/null
+/*
+ * There are essentially two kinds of disks on the Apple IIe/c
+ * - The ancient 140K floppies which need an OS level driver
+ * - SmartPort type devices with a common (and quite nice) firmware
+ * interface.
+ *
+ * This driver handles SmartPort devices. It assumes no more than 32
+ * devices per slot and no more than 8 slots. Device partitioning
+ * on the Apple IIe can be a strange mix of hardware and software.
+ * Usually the firmware presents the real device as multiple actual
+ * smartport devices.
+ *
+ * TODO:
+ * PASCAL or ProDOS entry points ?
+ * if it reports removable ensure we flush buffers on close
+ * if it reports formatting add an ioctl
+ * if it doesn't report interruptible ensure the asm cli's it
+ *
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devhd.h>
+
+extern uint8_t hd_map;
+uint8_t rw_cmd[7];
+uint8_t block_units[8];
+uint8_t readonly;
+
+static int hd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ uint16_t dptr, nb;
+ uint8_t err;
+
+ /* FIXME: swap support */
+ 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--) {
+ /* Fill in the protocol convertor information */
+ /* Note hd_map will be fun to handle */
+#ifdef CONFIG_PASCAL
+ rw_cmd[0] = is_read ? 0x03 : 0x04;
+ rw_cmd[1] = minor & 0x1F;
+ rw_cmd[2] = dptr;
+ rw_cmd[3] = dptr >> 8;
+ rw_cmd[4] = udata.u_block;
+ rw_cmd[5] = udata.u_block >> 8;
+ rw_cmd[6] = 0; /* If we do big disks this will be 16-23 */
+#else
+ /* DOS mode only allows 2 devices/slot */
+ rw_cmd[0] = is_read ? 1 : 2;
+ /* FIXME: check these are correct shifts */
+ rw_cmd[1] = ((minor & 0xE0) >> 2);
+ rw_cmd[1] |= (minor & 3) << 6;
+ rw_cmd[2] = dptr;
+ rw_cmd[3] = dptr >> 8;
+ rw_cmd[4] = udata.u_block;
+ rw_cmd[5] = udata.u_block >> 8;
+#endif
+
+ err = block_rw();
+
+ if (err) {
+ kprintf("hd%d: disk error %x\n", err);
+ udata.u_error = EIO;
+ return -1;
+ }
+
+ udata.u_block++;
+ dptr += 512;
+
+ }
+ return nb;
+}
+
+int hd_open(uint8_t minor, uint16_t flag)
+{
+ uint8_t slot = minor >> 5;
+ used(flag);
+
+#ifndef CONFIG_PASCAL
+ /* FIXME: may need a core tweak to match */
+ if ((readonly & (1 << slot)) && O_ACCMODE(flag)) {
+ udata.u_error = EROFS;
+ return -1;
+ }
+#endif
+ /* Non block device slots have 0 here */
+ if ((minor & 0x1F) >= block_units[slot]) {
+ udata.u_error = ENXIO;
+ 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);
+}
+
+/* This assumes a ProDOS type structure is present and we are doing
+ ProDOS things */
+void hd_install(uint8_t slot)
+{
+ uint8_t *p = (uint8_t *)0xC000 + (((uint16_t)slot) << 8);
+
+ if (!(p[254] & 4))
+ readonly |= (1 << slot);
+ block_units[slot] = (p[254] >> 4) & 3 + 1;
+ /* And at this point we ought to scan it for a swap signature */
+}
--- /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);
+void hd_install(uint8_t slot);
+
+extern uint8_t rw_cmd[7];
+extern uint8_t block_rw(void);
+extern uint8_t block_units[8];
+
+#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>
+
+/* Ignore super serial and friends for the moment */
+
+static volatile uint8_t *kbd_read = (volatile uint8_t *)0xC000;
+static volatile uint8_t *kbd_strobe = (volatile uint8_t *)0xC010;
+/* Assume a //c for the moment */
+static volatile uint8_t *irq_check = (volatile uint8_t *)0xC041;
+static volatile uint8_t *irq_reset = (volatile uint8_t *)0xC019;
+
+static char tbuf1[TTYSIZ];
+PTY_BUFFERS;
+
+uint8_t vtattr_cap = 0;
+
+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+ are the serial ports */
+
+/* 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)
+{
+ vtoutput(&c,1);
+}
+
+void tty_setup(uint8_t minor)
+{
+ minor;
+}
+
+void tty_sleeping(uint8_t minor)
+{
+ minor;
+}
+
+int tty_carrier(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+/* Beware - this kbd access also disables 80store */
+void tty_poll(void)
+{
+ uint8_t x;
+
+ x = *kbd_read;
+ if (x & 0x80) {
+ tty_inproc(1, x & 0x7F);
+ x = *kbd_strobe;
+ }
+}
+
+uint8_t check_timer(void)
+{
+ /* For now asume mouse card IIc - hack. Once we have proper IRQ
+ handling in place we can key this appropriately */
+ if (*irq_check & 0x80) {
+ *irq_reset;
+ return 1;
+ }
+ return 0;
+}
+
+void platform_interrupt(void)
+{
+ tty_poll();
+ if (check_timer())
+ timer_interrupt();
+}
+
+/* Video driver: Some of this would be better in asm, especially the scrolling */
+
+/* Line start table for 40 or 80 column. The only difference is that for
+ 40 column you add X, for 80 column you add X/2 and bit 0 of X tells you
+ if its alternate (0) or main (1) memory. */
+static volatile uint8_t *vtmap[24] = {
+ (volatile uint8_t *)0x400,
+ (volatile uint8_t *)0x480,
+ (volatile uint8_t *)0x500,
+ (volatile uint8_t *)0x580,
+ (volatile uint8_t *)0x600,
+ (volatile uint8_t *)0x680,
+ (volatile uint8_t *)0x700,
+ (volatile uint8_t *)0x780,
+
+ (volatile uint8_t *)0x428,
+ (volatile uint8_t *)0x4A8,
+ (volatile uint8_t *)0x528,
+ (volatile uint8_t *)0x5A8,
+ (volatile uint8_t *)0x628,
+ (volatile uint8_t *)0x6A8,
+ (volatile uint8_t *)0x728,
+ (volatile uint8_t *)0x7A8,
+
+ (volatile uint8_t *)0x450,
+ (volatile uint8_t *)0x4D0,
+ (volatile uint8_t *)0x550,
+ (volatile uint8_t *)0x5D0,
+ (volatile uint8_t *)0x650,
+ (volatile uint8_t *)0x6D0,
+ (volatile uint8_t *)0x750,
+ (volatile uint8_t *)0x7D0
+};
+
+/* Simple driver for 40 column text */
+
+void plot_char(int8_t y, int8_t x, uint16_t c)
+{
+ *(vtmap[y] + x) = ((uint8_t)c) | 128;
+}
+
+/* Point at ourselves so the first dummy cursor_off is harmless */
+static volatile uint8_t *cursorptr = (uint8_t *)&cursorptr;
+
+void cursor_off(void)
+{
+ *cursorptr |= 128;
+}
+
+void cursor_on(int8_t y, int8_t x)
+{
+ cursorptr = vtmap[y] + x;
+ *cursorptr &= 127;
+}
+
+void clear_across(int8_t y, int8_t x, int16_t l)
+{
+ volatile uint8_t *addr = vtmap[y] + x;
+ memset(addr, ' '|0x80, l);
+}
+
+void clear_lines(int8_t y, int8_t n)
+{
+ volatile uint8_t *addr;
+ while(n--) {
+ addr = vtmap[y++];
+ memset(addr, ' '|0x80, 40);
+ }
+}
+
+void vtattr_notify(void)
+{
+}
+
+void scroll_up(void)
+{
+ uint8_t y;
+ for (y = 1; y <= 23; y++) {
+ volatile uint8_t *src = vtmap[y];
+ volatile uint8_t *dst = vtmap[y-1];
+ memcpy(dst, src, 40);
+ }
+}
+
+void scroll_down(void)
+{
+ uint8_t y;
+ for (y = 23; y > 1; y--) {
+ volatile uint8_t *src = vtmap[y-1];
+ volatile uint8_t *dst = vtmap[y];
+ memcpy(dst, src, 40);
+ }
+}
+
--- /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 $0200
+; 256+256+256 bytes.
+U_DATA__TOTALSIZE .set $300
+
+PROGLOAD .set $2000
+ZPBASE .set $0
--- /dev/null
+MEMORY {
+ RAMZ: start = $0000, size = $0100, type = rw, fill = yes;
+ STACK: start = $0100, size = $0100, type = rw, fill = yes;
+ RAM0: start = $0800, size = $17FF, type = rw, fill = yes;
+ RAM1: start = $2000, size = $9EFF, type = rw, fill = yes;
+ RAM2: start = $D000, size = $2FFA, type = rw, fill = yes;
+ RAM3: start = $FFFA, size = $0010, type = rw, fill = yes;
+}
+
+SEGMENTS {
+ ZEROPAGE: load = RAMZ, type = zp, define = yes;
+ COMMONDATA: load = RAM2, type = bss;
+ COMMONMEM: load = RAM2, type = rw;
+
+ START: load = RAM1, type = ro;
+ CODE: load = RAM1, type = ro, define = yes;
+ RODATA: load = RAM1, type = ro;
+ STUBS: load = RAM2, type = ro, define = yes;
+ DATA: load = RAM2, type = rw, define = yes;
+ BSS: load = RAM2, 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 = RAM2, type = ro;
+ SYS5: load = RAM0, type = ro;
+ DISCARD: load = RAM0, type = ro;
+ DISCARDDATA: load = RAM0, type = ro;
+
+ VECTORS: load = RAM3, type = ro;
+}
+
+FILES {
+ %O: format = bin;
+}
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <devhd.h>
+
+uint8_t kernel_flag = 1;
+uint8_t cols = 40;
+uint8_t card_present;
+uint8_t model;
+#define APPLE_UNKNOWN 0
+#define APPLE_IIE 1
+#define APPLE_IIC 2
+
+void platform_idle(void)
+{
+ irqflags_t flags = di();
+ tty_poll();
+ irqrestore(flags);
+}
+
+void do_beep(void)
+{
+ /* Strobe speaker bit - TODO */
+}
+
+
+/* Below wants to end up mostly in discard */
+
+#ifdef CONFIG_RAMWORKS
+/*
+ * 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);
+}
+
+#endif
+
+void map_init(void)
+{
+}
+
+extern int strcmp(const char *, const char *);
+
+uint8_t platform_param(char *p)
+{
+ if (strcmp(p,"40") == 0) {
+ cols = 40;
+ return 1;
+ }
+ if (strcmp(p,"80") == 0) {
+ cols = 80;
+ return 1;
+ }
+ return 0;
+}
+
+static void unsupported(void)
+{
+ kputs(" (unsupported) ");
+}
+
+/* Slot detection on Apple is a bit magic. It started out as sneaky ROM
+ peeking and evolved later into proper protocols */
+
+void scan_slots(void)
+{
+ uint8_t i = 1; /* slot 0 is the motherboard */
+ volatile uint8_t *card = (volatile uint8_t *)0xC100;
+
+ for (i = 1; i < 8; i++, card += 0x100) {
+ kprintf("\n%c - ", '0' + i);
+ if (!(card_present & (1 << i))) {
+ kputs("empty");
+ continue;
+ }
+ if (card[1] == 0x20 && card[3] == 0x00 && card[5] == 0x03) {
+ /* storage */
+
+ /* Old ROM floppy drive: needs custom driver */
+ if (card[255] == 0xFF) {
+ kputs("13 sector/track floppy");
+ unsupported();
+ }
+ /* New ROM floppy drive: needs custom driver */
+ else if (card[255] == 0x00) {
+ kputs("16 sector/track floppy");
+ unsupported();
+ } else { /* We use the firmware interface */
+ kputs("storage");
+ hd_install(i);
+ }
+ continue;
+ }
+ if (card[1] == 0xB0 && card[2] == 0x20) {
+ kputs("tablet");
+ unsupported();
+ continue;
+ }
+ /* This one is important, we must find it to get a period timer */
+ if (card[5] == 0x38 && card[7] == 0x18 && card[11] == 0x01 &&
+ card[12] == 0x20 && card[0xFB] == 0xD6) {
+ kputs("mouse");
+ continue;
+ }
+ /* Serial cards: from dumb to dumber */
+ if (card[5] == 0x38 && card[7] == 0x18) {
+ if (card[11] == 0x01 && card[12] == 0x31)
+ kputs("super serial");
+ else {
+ kputs("serial");
+ unsupported();
+ }
+ continue;
+ }
+ if (card[0] == 0x08 && card[2] == 0x28 && card[4] == 0x5B &&
+ card[6] == 0x70) {
+ kputs("clock");
+ continue;
+ }
+ /* TODO cards with pascal idents not in the above */
+ kputs("unknown");
+ }
+}
+
+/* Process the ProDOS bits */
+void breadcrumbs(void)
+{
+ uint8_t *dos = (uint8_t *)0xBF00;
+ uint8_t bits = dos[0x98];
+
+ card_present = dos[0x99];
+
+ if ((bits & 0x30) != 0x30)
+ panic("128K memory required");
+ if ((bits & 0x34) == 0x20)
+ model = APPLE_IIE;
+ if ((bits & 0x34) == 0x24)
+ model = APPLE_IIC;
+ /* IIgs is rather different */
+ if (model == APPLE_UNKNOWN)
+ panic("Apple IIe or IIc required");
+}
--- /dev/null
+export CPU = 6502
--- /dev/null
+;
+; 6502 version
+;
+ .export _switchout
+ .export _switchin
+ .export _dofork
+ .export _ramtop
+
+ .import _chksigs
+ .import _trap_monitor
+
+ .import map_kernel
+ .import _swapper
+ .import _swapout
+
+ .import _newproc
+ .import _getproc
+ .import _runticks
+ .import _inint
+ .import outstring
+ .import outxa
+ .import outcharhex
+
+ .include "kernel.def"
+ .include "../kernel02.def"
+ .include "zeropage.inc"
+
+; FIXME: review but all but some of the fork logic looks safe to go in code
+ .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 $C000
+
+; 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
+ ; 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
+ ldy #P_TAB__P_PAGE_OFFSET
+ lda (ptr1),y
+
+ bne not_swapped
+
+ lda U_DATA__U_PTAB
+ ldx U_DATA__U_PTAB+1
+ ;
+ ; FIXME - need the extra logic to swap out the kernel Z/S bits of
+ ; interest.
+ ;
+;FIXME jsr _swapoutudz
+ ;
+ jsr _swapout
+
+ ; We have to worry about stacks here. We need to load in the
+ ; kernel C stack and CPU stack for the new process over the one
+ ; we are running upon. On most processors we just select another
+ ; stack but for the CPU stack on 6502 we can't do this.
+ ;
+ ; We do have the alternate ZP and stack but remember those hold
+ ; the user space ZP and stack. What we actually do is gross
+ ;
+
+ lda switch_proc_ptr
+ ldx switch_proc_ptr + 1
+
+ ; After swapout the ZP and S in memory are the old user ones, that
+ ; means they are effectively free memory
+
+ sta $C009 ; Switch to Alt ZP
+
+ ;
+ ; Set up a plausible ZP and S. S points somewhere in the
+ ; right area. We don't care exactly so don't reload.
+ ;
+ lda #<swapstack_top
+ sta sp
+ ldx #>swapstack_top
+ sta sp+1
+ ;
+ ; We now have a constructed environment to run the
+ ; swap helper on the alt stack to load the real ZP and S
+ ;
+;FIXME jsr _swapudz ; Swap in real Udata/Stack via
+ ; special platform routine
+
+ ;
+ ; We then run the swapper in the normal fashion and it will not
+ ; touch our kernel ZP and S so all is good. Note that we are still
+ ; on the swapstack for C. We'll only fix that up at the end.
+ ;
+ sta $C008 ; Back to real C stack
+
+ ldx U_DATA__U_SP ; Use the memory below the
+ ; CPU stack we swapped in
+ txs
+
+ lda switch_proc_ptr
+ ldx switch_proc_ptr + 1
+
+ ; Swap in the user process and FIXME teach swapper about 6502 S/Z
+ ; user pages.
+
+ jsr _swapper
+
+
+
+not_swapped:
+ ; Make sure we swapped in the right process
+ lda U_DATA__U_PTAB
+ cmp switch_proc_ptr
+ bne switchinfail
+ ldx U_DATA__U_PTAB+1
+ cpx switch_proc_ptr+1
+ bne switchinfail
+ ; XA holds the process ptr as a sidde effect, now construct a
+ ; pointer
+ sta ptr1
+ stx ptr1+1
+ ; Set it to running
+ ldy #P_TAB__P_STATUS_OFFSET
+ lda #P_RUNNING
+ sta (ptr1),y
+ ; And copy the page offset into the udata
+ ldy #P_TAB__P_PAGE_OFFSET
+ lda #0
+ lda (ptr1),y
+ sta U_DATA__U_PAGE
+ ; Fix up the stack pointer from the one we are hiding in
+ ldx U_DATA__U_SP
+ txs
+
+ lda #0
+ sta _runticks
+ sta _runticks+1
+
+ pla
+ sta sp+1
+ pla
+ sta sp
+ lda _inint
+ beq swtchdone ; in ISR, leave interrupts off
+ cli
+swtchdone:
+ pla ; Return code
+ tax
+ pla
+ rts
+
+switchinfail:
+ 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
+
+
+;
+; Do the hard work for a fork. Actually in a swap only environment
+; there isn't that much to do. We swap out the existing image as is
+; and it's the parent, and we keep the memory copy as child.
+;
+_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 so write it out to disk.
+
+ lda fork_proc_ptr
+ ldx fork_proc_ptr+1
+
+ jsr _swapout
+
+ ; now the save 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
+
+ ; Perform the necessary magic to turn into the child
+ 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
+
+ .data
+
+switch_proc_ptr: .word 0
+fork_proc_ptr: .word 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
+swapstack:
+ .res 128,0
+swapstack_top:
--- /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
+