6502: Start fleshing out how the port would work
authorAlan Cox <alan@etchedpixels.co.uk>
Sun, 16 Nov 2014 16:18:48 +0000 (16:18 +0000)
committerAlan Cox <alan@etchedpixels.co.uk>
Sun, 16 Nov 2014 16:19:48 +0000 (16:19 +0000)
Very incomplete at this point

Kernel/lowlevel-6502.s
Kernel/platform-6502test/README [new file with mode: 0644]
Kernel/platform-6502test/p6502.s

index d0496a0..5d35ef5 100644 (file)
        .export unix_syscall_entry
        .export _doexec
        .export interrupt_handler
-       .export null_handler
        .export nmi_handler
-       .export trap_illegal
 
        .export outstring
        .export outstringhex
        .export outnewline
-       .export outx
-       .export outy
        .export outcharhex
 
+       .import outchar
+       .import _kernel_flag
+       .import _unix_syscall_i
 
+       .include "platform/zeropage.inc"
+
+       .segment "COMMONMEM"
+;
+;      Unlike Z80 we need to deal with systems that have no overlapping
+;      memory banks only a 'far write/far read' model (eg 6509). We pass
+;      the arguments is a single pointer therefore we expect the platform
+;      code to have copied the syscall arguments into udata then called us
+;      it also saves any registers etc for us (as it will need them too)
+;
+;      Called with interrupts off, on the kernel stack
+;      On completion U_DATA__U_ERROR an U_DATA__U_RETVAL hold the returns
+;
 unix_syscall_entry:
-_doexec:
+       sty U_DATA__U_CALLNO    ; Save the syscall code
+       lda #1
+       sta _kernel_flag        ; In kernel mode
+       jsr map_kernel          ; Ensure kernel is mapped (no-op in some cases)
+       cli                     ; Interrupts now ok
+       jsr _unix_syscall_i     ; Enter C space via the __interrupt wrapper
+       sei                     ; Interrupts back off
+       pha
+       txa
+       pha                     ; Save return code
+       lda #0
+       sta _kernel_flag
+unix_sig_exit:
+       jsr map_process_always  ; Map back process if we have common
+       rts
+
+;
+;      On 6502 the platform code is responsible for invoking the
+;      signal dispatch (as it may have to be in the stub in the
+;      process space if we have no common)
+;
+
+
+;
+;      doexec is a special case syscal exit path. As we may have no
+;      common we have to hand the last bits off to the platform code
+;
+_doexec
+       sei
+       lda #0
+       sta _kernel_flag
+       call map_process_always
+       jmp platform_doexec
+
+;
+;      Platform code has saved the registers and ensured we are in the
+;      right banks if running with no common (6509 etc). It has also
+;      done stack switches and checked for re-entrancy
+;
+;      Caller on the exit side is responsible for stack switches and
+;      checking for signals
+;
 interrupt_handler:
-null_handler:
+       jsr map_save
+       jsr map_kernel
+       lda #1
+       sta _inint
+       jsr _platform_interrupt_i       ; call via C int wrapper
+       lda #0
+       sta _inint
+       lda _kernel_flag
+       bne interrupt_k
+       jsr map_process_always          ; may have switched task
+       jr int_switch
+interrupt_k:
+       jsr map_restore
+int_switch:
+       lda #0
+       sta _inint
+       rts
+
 nmi_handler:
-trap_illegal:
+       ldx #>nmi_trap
+       lda #<nmi_trap
+       jsr outstring
+nmi_stop:
+       jmp nmi_stop
+
 outstring:
-outstringhex:
+       sta ptr1
+       stx ptr1+1
+       ldy #0
+outstringhexl:
+       lda (ptr1),y
+       cmp #0
+       beq outdone1
+       call outcharhex
+       iny
+       jmp outstringhexl
+
+outstringhex:  ; string in X,A
+       sta ptr1
+       stx ptr1+1
+       ldy #0
+outstringhexl:
+       lda (ptr1),y
+       cmp #0
+       beq outdone1
+       call outcharhex
+       iny
+       jmp outstringhexl
+
 outnewline:
-outx:
-outy:
-outcharhex:
+       pha
+       lda #10
+       jsr outchar
+       lda #10
+       jsr outchar
+       pla
        rts
+
+outcharhex:
+       pha
+       and #$f0
+       lsr a
+       lsr a
+       lsr a
+       lsr a
+       add #'0'
+       jsr outchar
+       pla
+       and #$0f
+       add #'0'
+       jmp outchar
diff --git a/Kernel/platform-6502test/README b/Kernel/platform-6502test/README
new file mode 100644 (file)
index 0000000..c3a6833
--- /dev/null
@@ -0,0 +1,47 @@
+6502 test sketching out
+
+This is not a complete port, but a sketch in progress to test that it should
+be possible to run FUZIX on a banked 6502 such as the Commodore 6509 based
+systems.
+
+The 6509 also provides an opportunity to test a very different banking
+model. There is no common area on a 6509 based system. Address 0 sets the
+64K bank to be used and swaps wholesale between banks. Address 1 set the
+bank used on two specific instructions LDA (foo),y and STA (foo),y. In other
+words the only interbank feature you have is far data pointers.
+
+
+For Fuzix this should mean we can 
+
+- select fixed banks so we hand each task a bank
+- make map_kernel/map_process/map_* in general mostly no-ops. We do
+  need to save address 1 on map_save/restore in case the kernel is mid
+  far access
+- add a new section "STUBS" which is copied to the same spot in each bank
+  and contains mini wrappers for syscall, interrupts, doexec and signal
+  dispatch
+
+Interrupts simply flip bank and go via a C __interrupt wrapper which saves
+the C stack state for the kernel, then runs the IRQ handler, then returns
+back.
+
+ISSUE: need to figure where to save the __interrupt bits as we task switch
+in irq cases
+
+
+System calls far copy the syscall code and the arguments into the udata
+rather than fishing them off the stack as Z80 does. They then switch to the
+kernel stack, set up the C stack and run the system call. When it returns
+for that process context we copy back the error and return values into the
+user bank, and bankflip back.
+
+In both cases we need to also pull over the pending signal and the vector
+for it as we need to dispatch the signal from the user bank. (*TODO*)
+
+
+The other odd case is doexec. When we exec a process we don't return via
+the system call path but instead directly exit kernel mode and jump to the
+new stack/pc. This is arguably a wart and we should instead patch the return
+stack (and fix start up). For now however we need a stub of code to do the
+dispatch. Basically we set the sp in the user bank, switch to the user bank
+and jump to the target
index 11addf2..440ed45 100644 (file)
            .export _ei
            .export _irqrestore
 
+           .import interrupt_handler
+           .import _ramsize
+           .import _procmem
+           .import nmi_handler
+           .import unix_syscall_entry
+
             .include "kernel.def"
             .include "../kernel02.def"
+           .include "zeropage.inc"
 
 ; -----------------------------------------------------------------------------
 ; COMMON MEMORY BANK (0xF000 upwards)
@@ -32,7 +39,7 @@ tm_user_sp: .word 0
 
 _trap_monitor:
            sei
-           bra _trap_monitor
+           jmp _trap_monitor
 
 _trap_reboot:
 ;          lda 0xff90
@@ -40,14 +47,21 @@ _trap_reboot:
 ;          jmp 0
 
 _di:
+           php
            sei                 ; FIXME: save old state in return to C
+           pla                 ; Old status
            rts
 _ei:
            cli                 ; on 6502 cli enables IRQs!!!
            rts
 
 _irqrestore:
-           ; FIXME - pull off C stack
+           and #4              ; IRQ flag
+           beq irq_on
+           cli
+           rts
+irq_on:
+           sei
            rts
 
 ; -----------------------------------------------------------------------------
@@ -62,17 +76,13 @@ init_hardware:
             ; set system RAM size for test purposes
            lda #1
            sta _ramsize+1
-           dea
+           lda #0
            sta _ramsize
            sta _procmem+1
            lda #192
            sta _procmem
 
-;          ; Our vectors are in high memory unlike Z80 but we still
-;          ; need vectors
-;          FIXME: need to make a C call here
-;          ldx #0
-;            jsr _program_vectors
+            jsr program_vectors_k
 
             rts
 
@@ -85,117 +95,312 @@ init_hardware:
 _program_vectors:
             ; we are called, with interrupts disabled, by both newproc() and crt0
            ; will exit with interrupts off
-;            orcc #0x10                ; di just to be sure
-
-;          jsr map_process
+           sei
+           ;
+           ; Fixme: block copy stubs segment as well if 6509.
+           ;
 
+           ; our C caller will invoke us with the pointer in x,a
+           ; just pass it on
+           jsr map_process
+program_vectors_k:
            lda #<vector
-           sta 0xFFFE
+           sta $FFFE
            lda #>vector
-           sta 0xFFFF
+           sta $FFFF
+           lda #<nmi_handler
+           sta $FFFA
+           lda #>nmi_handler
+           sta $FFFB
+           ; However tempting it may be to use BRK for system calls we
+           ; can't do this on an NMOS 6502 because the chip has brain
+           ; dead IRQ handling buts that could simply "lose" the syscall!
+           lda #JSR
+           sta syscall
+           lda #<syscall_entry
+           sta syscall+1
+           lda #>syscall_entry
+           sta syscall+2
            jsr map_kernel
            rts
 
-
-vector:
-           ; FIXME: decide whether its an IRQ or syscall and branch
-
-;            ldd #interrupt_handler
-;            ldd #unix_syscall_entry
-           rts
-
 ;
-;      Userspace mapping pages 7+  kernel mapping pages 3-5, first common 6
+;      On a fully switching system with far copies like the 6509 this is
+;      all basically a no-op
 ;
-;      Pass the table pointer in zero page ?
+;      On a banked setup the semantics are:
 ;
-map_process_always:
-;          pshs y,u
-;          ldx #U_DATA__U_PAGE
-;          jsr map_process_2
-;          puls y,u,pc
+;      map_process_always()
+;      Map the current process (ie the one with the live uarea)
 ;
-;      HL is the page table to use, A is eaten, HL is eaten
+;      map_kernel()
+;      Map the kernel
 ;
-map_process:
-;          cmpx #0
-;          bne map_process_2
+;      map_process(pageptr [in X,A])
+;      if pageptr = 0 then map_kernel
+;      else map_process using the pageptr
 ;
-;      Map in the kernel below the current common, all registers preserved
+;      map_save
+;      save the current mapping
 ;
-map_kernel:
+;      map_restore
+;      restore the saved mapping
 ;
-;      Two MMU mappings is pure luxury
+;      save/restore are used so that the kernel can play with its internal
+;      banking/mappings without having to leave interrupts off all the time
 ;
-;      Kernel map was set up by boot loader, just flip to it
+map_process_always:
+           pha
+           lda #<U_DATA__U_PAGE
+           ldx #>U_DATA__U_PAGE
+           jsr map_process_2
+           pla
+           ret
 ;
-;          lda 0xff91                  ; INIT1, use 0xFFA8 maps
-;          ora #0x01
-;          sta 0xff91
-           rts
+;      X,A points to the map table of this process
 ;
-;      User is in the FFA0 map with the top 8K as common
+map_process:
+           cmp #0
+           bne map_process_2
+           cpx
+           bne map_process_2
 ;
-;      As the core code currently does 16K happily but not 8 we just pair
-;      up pages
+;      Map in the kernel below the current common, all registers preserved
 ;
+map_kernel:
+           lda #1      ; for 6509 clean up any far copy ptr
+           sta 1
+           rts
+
+; X,A holds the map table of this process
 map_process_2:
-;          pshs x,y,a
-;          ldy #0xffa0                 ; MMU user map. We can fiddle with
-;          lda ,x+                     ; this to our hearts content
-;          sta ,y+                     ; as it's not live yet
-;          inca
-;          sta ,y+
-;          lda ,x+
-;          sta ,y+
-;          inca
-;          sta ,y+
-;          lda ,x+
-;          sta ,y+
-;          inca
-;          sta ,y+
-;          lda ,x+
-;          sta ,y+
-;          inca        
-;          sta ,y
-;          lda 0xff91
-;          anda #0xfe
-;          sta 0xff91                  ; new mapping goes live here
-;          puls x,y,a,pc               ; so had better include common!
-;
-;      Restore a saved mapping. We are guaranteed that we won't switch
-;      common copy between save and restore. Preserve all registers
-;
-;      We cheat somewhat. We have two mapping sets, so just remember
-;      which space we were in
+           tya
+           pha
+           sta ptr1
+           sty ptr1+1
+           ldy #0
+           lda (ptr1),y        ; 4 bytes if needed
+           sta map_current
+           pla
+           tay
+           rts
+
+
+;
+;      Restore the indirection register on the 6509, just in case we
+;      interrupted mid far copy
 ;
 map_restore:
-;          pshs a
-;          lda 0xff91
-;          ora saved_map
-;          sta 0xff91
-;          puls a,pc
-;          
+           pha
+           lda saved_map
+           sta 1
+           pla
+           rts
 
-;      Save the current mapping.
+;      Save the current mapping. For a 6509 just save the indirection
 ;
 map_save:
-;          pshs a
-;          lda 0xff91
-;          anda #1
-;          sta saved_map
-;          puls a,pc
+           pha
+           lda 1
+           sta saved_map
+           pla
+           rts
 
 saved_map:  .dbyt 0
-           
+map_current: .dbyt 0
 
-; outchar: Wait for UART TX idle, then print the char in a
+; outchar: Wait for UART TX idle, then print the char in a without
+; corrupting other registers
 
 outchar:
-;          pshs b
 outcharw:
 ;          ldb 0xffa0
 ;          bitb #0x02
 ;          beq outcharw
 ;          sta 0xffa1
 ;          puls b,pc
+           rts
+
+;
+;      Code that will live in each bank at the same address and is copied
+;      there on 6509 type setups. On 6502 it may well be linked once in
+;      common space
+;
+       .segment "STUBS"
+;
+;      Interrupt vector logic. Keep this in platform for the 6502 so that
+;      we can use the shorter one for the CMOS chip
+;
+;      FIXME: spot BRK instructions and turn them into a synchronous
+; SIGTRAP
+;
+vector:
+           pha
+           txa
+           pha
+           tya
+           pha
+           cld
+           tsx
+           inx
+           inx
+           inx
+           inx
+           lda $0100,X
+           and a, $10
+;
+;      FIXME: either don't care about brk or ship it somewhere like
+;      kill -SIGTRAP
+;
+           bne bogon
+;
+;      Switch to the kernel memory mapping (stack gone away)
+;
+           lda 1       ; Save the indirection vector
+           pha
+           lda #1      ; Kernel in bank 1
+           sta 1
+           sta 0
+;      The next fetch will occur from the new mapping (kernel) copy
+;      Stack has gone for a walk if we were not coming from kernel
+           tsx
+           stx istack_switched_sp              ; in uarea/stacks
+           ldx #0xC0
+           txs                                 ; our istack
+           jsr interrupt_handler
+           ldx istack_switched_sp
+           txs                                 ; entry stack
+           lda _kernel_flag
+           beq vector_um
+;
+;      Already in kernel (bank 1)
+;
+           pla
+           sta 1                               ; restore the saved indirect
+           jmp irqout
+vector_um:
+           lda U_DATA__U_PAGE                  ; may be a new task
+           sta 1                               ; flip to it
+           sta 0
+; leap back into user context (may be a different user context to the one
+; we left. Stack now valid again
+           pla                                 ; discard saved idirect
+
+irqout:
+           pla
+           tya
+           pla
+           txa
+           pla
+           rti
+
+
+;          X, A holds the syscall block
+;          Y holds the call code
+;          We assume the kernel is in bank 0
+;
+syscall_entry:
+           sei
+           sty tmp1            ; for a moment
+           sta ptr1
+           stx ptr1+1
+           lda #<U_DATA__U_ARGN
+           sta ptr2
+           lda #>U_DATA__U_ARGN
+           sta ptr2+1
+           lda #1
+           sta 1               ; magic far copy hackery
+
+           ldx #0
+           txy
+copy_args:  lda (ptr1), x      ; copy the arguments from current bank
+           sta (ptr2), y       ; will write into bank 1
+           inx
+           iny
+           cpx #8
+           bne copy_args
+           ldy tmp1            ; syscall code
+           ;
+           ; Now we need to bank and stack switch
+           ;
+           tsx
+           lda #1
+           sta 0               ; kernel banks
+           sta 1
+;
+;      We are now suddenely in the kernel copy of this, and our stack is
+;      missing in action. Access to userspace is not available
+;
+;      On a 6509 this also means our C stack is missing in action, which
+;      is quite convenient as it saves us a save and restore it. On other
+;      CPUs it might be a little less convenient
+;
+           stx U_DATA__U_SP
+           ldx #0
+;
+;      FIXME: how to handle IRQ division of stack ???
+;
+           txs                 ; Switch to the working stack
+;
+;      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_SP
+           txs
+           lda U_DATA__U_PAGE  ; our bank
+           sta 1               ; use our bank for sta (ptr), y
+;
+;      Copy the return data over
+;
+          ldy #8               ; write them after the argument block
+;
+;      As we are a user process and making a syscall our ptr1 was saved
+;      and banked out. If you had common memory this would be uglier but
+;      then you've got somewhere else to put the pointer right !
+;
+          lda U_DATA__U_ERROR
+          sta (ptr1), y
+          iny
+          lda U_DATA__U_ERROR+1
+          sta (ptr1),y
+          iny
+          lda U_DATA__U_RETVAL
+          sta (ptr1),y
+          iny
+          lda U_DATA__U_RETVAL+1
+          sta (ptr1), y
+;      FIXME Also copy over needed signal information (vector, pending stuff)
+;
+          lda 1                ; The sta,y redirection bank
+          sta 0                ; becomes our execution bank
+;
+;      We just teleported back to the copy of this code in the
+;      user process bank
+;
+;      Our C stack is valid as we simply bounced out of it for the 6509.
+;
+;
+;      FIXME: do signal dispatch - this will need C stack fixing, and
+;      basically signal dispatch is __interrupt.
+;
+
+           rts