platform-appleiie: Initial sketches on how a IIe port might look
authorAlan Cox <alan@linux.intel.com>
Mon, 22 Jan 2018 00:10:29 +0000 (00:10 +0000)
committerAlan Cox <alan@linux.intel.com>
Mon, 22 Jan 2018 00:10:29 +0000 (00:10 +0000)
18 files changed:
Kernel/platform-appleiie/Makefile [new file with mode: 0644]
Kernel/platform-appleiie/README [new file with mode: 0644]
Kernel/platform-appleiie/appleii.s [new file with mode: 0644]
Kernel/platform-appleiie/commonmem.s [new file with mode: 0644]
Kernel/platform-appleiie/config.h [new file with mode: 0644]
Kernel/platform-appleiie/crt0.s [new file with mode: 0644]
Kernel/platform-appleiie/devhd.c [new file with mode: 0644]
Kernel/platform-appleiie/devhd.h [new file with mode: 0644]
Kernel/platform-appleiie/device.h [new file with mode: 0644]
Kernel/platform-appleiie/devices.c [new file with mode: 0644]
Kernel/platform-appleiie/devtty.c [new file with mode: 0644]
Kernel/platform-appleiie/devtty.h [new file with mode: 0644]
Kernel/platform-appleiie/kernel.def [new file with mode: 0644]
Kernel/platform-appleiie/ld65.cfg [new file with mode: 0644]
Kernel/platform-appleiie/main.c [new file with mode: 0644]
Kernel/platform-appleiie/target.mk [new file with mode: 0644]
Kernel/platform-appleiie/tricks.s [new file with mode: 0644]
Kernel/platform-appleiie/zeropage.inc [new file with mode: 0644]

diff --git a/Kernel/platform-appleiie/Makefile b/Kernel/platform-appleiie/Makefile
new file mode 100644 (file)
index 0000000..b49ca19
--- /dev/null
@@ -0,0 +1,35 @@
+
+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
diff --git a/Kernel/platform-appleiie/README b/Kernel/platform-appleiie/README
new file mode 100644 (file)
index 0000000..022e2da
--- /dev/null
@@ -0,0 +1,127 @@
+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)
+
+
+
diff --git a/Kernel/platform-appleiie/appleii.s b/Kernel/platform-appleiie/appleii.s
new file mode 100644 (file)
index 0000000..b2a572c
--- /dev/null
@@ -0,0 +1,545 @@
+;
+;          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
diff --git a/Kernel/platform-appleiie/commonmem.s b/Kernel/platform-appleiie/commonmem.s
new file mode 100644 (file)
index 0000000..a7695d4
--- /dev/null
@@ -0,0 +1,41 @@
+;
+;      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
diff --git a/Kernel/platform-appleiie/config.h b/Kernel/platform-appleiie/config.h
new file mode 100644 (file)
index 0000000..5e80036
--- /dev/null
@@ -0,0 +1,64 @@
+//#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()
diff --git a/Kernel/platform-appleiie/crt0.s b/Kernel/platform-appleiie/crt0.s
new file mode 100644 (file)
index 0000000..c2b6ed5
--- /dev/null
@@ -0,0 +1,99 @@
+;
+;
+;      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
diff --git a/Kernel/platform-appleiie/devhd.c b/Kernel/platform-appleiie/devhd.c
new file mode 100644 (file)
index 0000000..4d0839d
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *     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 */
+}
diff --git a/Kernel/platform-appleiie/devhd.h b/Kernel/platform-appleiie/devhd.h
new file mode 100644 (file)
index 0000000..ed8a82e
--- /dev/null
@@ -0,0 +1,14 @@
+#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__ */
diff --git a/Kernel/platform-appleiie/device.h b/Kernel/platform-appleiie/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-appleiie/devices.c b/Kernel/platform-appleiie/devices.c
new file mode 100644 (file)
index 0000000..8f2563d
--- /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) - 1)
+       return false;
+    else
+        return true;
+}
+
+void device_init(void)
+{
+}
+
diff --git a/Kernel/platform-appleiie/devtty.c b/Kernel/platform-appleiie/devtty.c
new file mode 100644 (file)
index 0000000..12cbbb2
--- /dev/null
@@ -0,0 +1,188 @@
+#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);
+       }
+}
+
diff --git a/Kernel/platform-appleiie/devtty.h b/Kernel/platform-appleiie/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-appleiie/kernel.def b/Kernel/platform-appleiie/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-appleiie/ld65.cfg b/Kernel/platform-appleiie/ld65.cfg
new file mode 100644 (file)
index 0000000..5f32c42
--- /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 = $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;
+}
diff --git a/Kernel/platform-appleiie/main.c b/Kernel/platform-appleiie/main.c
new file mode 100644 (file)
index 0000000..df71342
--- /dev/null
@@ -0,0 +1,151 @@
+#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");
+}
diff --git a/Kernel/platform-appleiie/target.mk b/Kernel/platform-appleiie/target.mk
new file mode 100644 (file)
index 0000000..eb53ebc
--- /dev/null
@@ -0,0 +1 @@
+export CPU = 6502
diff --git a/Kernel/platform-appleiie/tricks.s b/Kernel/platform-appleiie/tricks.s
new file mode 100644 (file)
index 0000000..78e0dac
--- /dev/null
@@ -0,0 +1,290 @@
+;
+;      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:
diff --git a/Kernel/platform-appleiie/zeropage.inc b/Kernel/platform-appleiie/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
+