From 1d3fa0fac046f736db35a35ad157a96524699f9c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 16 Nov 2014 16:18:48 +0000 Subject: [PATCH] 6502: Start fleshing out how the port would work Very incomplete at this point --- Kernel/lowlevel-6502.s | 136 ++++++++++- Kernel/platform-6502test/README | 47 ++++ Kernel/platform-6502test/p6502.s | 377 ++++++++++++++++++++++++------- 3 files changed, 463 insertions(+), 97 deletions(-) create mode 100644 Kernel/platform-6502test/README diff --git a/Kernel/lowlevel-6502.s b/Kernel/lowlevel-6502.s index d0496a0b..5d35ef5b 100644 --- a/Kernel/lowlevel-6502.s +++ b/Kernel/lowlevel-6502.s @@ -3,28 +3,142 @@ .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 #vector - sta 0xFFFF + sta $FFFF + 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+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 + 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+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+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 -- 2.34.1