view level2/modules/kernel/mc09krn.asm @ 3278:ea1afb494127

defs: Add Bt.Sec for atari and corsham
author Tormod Volden <debian.tormod@gmail.com>
date Sat, 07 Mar 2020 23:52:40 +0100
parents 6eb2edad80d8
children
line wrap: on
line source

********************************************************************
* krn - NitrOS-9 Level 2 Kernel
*
* $Id$
*
* Edt/Rev  YYYY/MM/DD  Modified by
* Comment
* ------------------------------------------------------------------
*  19r6    2002/08/21  Boisy G. Pitre
* Assembles to the os9p1 module that works on my NitrOS-9 system.
*
*  19r7    2002/09/26  Boisy G. Pitre
* Added check for CRC feature bit in init module
*
*  19r8    2003/09/22  Boisy G. Pitre
* Back-ported to OS-9 Level Two.
*
*  19r8    2004/05/22  Boisy G. Pitre
* Renamed to 'krn'
*
*  19r9    2004/07/12  Boisy G. Pitre
* F$SRqMem now properly scans the DAT images of the system to update
* the D.SysMem map.

        nam     krn
        ttl     NitrOS-9 Level 2 Kernel

        IFP1
        use     defsfile
        ENDC

* defines for customizations
Revision        set     9       module revision
Edition set     19      module Edition
Where   equ     $F000   absolute address of where Kernel starts in memory

        mod     eom,MName,Systm,ReEnt+Revision,entry,0

MName   fcs     /Krn/
        fcb     Edition



* Might as well have this here as just past the end of Kernel...
DisTable
        fdb     L0CD2+Where     D.Clock absolute address at the start
        fdb     XSWI3+Where     D.XSWI3
        fdb     XSWI2+Where     D.XSWI2
        fdb     D.Crash         D.XFIRQ crash on an FIRQ
        fdb     XIRQ+Where      D.XIRQ
        fdb     XSWI+Where      D.XSWI
        fdb     D.Crash         D.XNMI crash on an NMI
        fdb     $0055           D.ErrRst ??? Not used as far as I can tell
        fdb     Sys.Vec+Where   Initial Kernel system call vector
DisSize equ     *-DisTable
* ^
* Code using 'SubSiz', below, assumes that SubStrt follows on directly after
* the end of DisTable. Therefore, DO NOT ADD ADD ANYTHING BETWEEN THESE 2 LABELS
* v
LowSub  equ     $0160           start of low memory subroutines
SubStrt equ     *
* D.Flip0 - switch to system task 0
R.Flip0 equ     *
        IFNE    H6309
        aim     #$FE,<D.TINIT   map type 0
        lde     <D.TINIT        another 2 bytes saved if GRFDRV does: tfr cc,e
        ste     >DAT.Task       and we can use A here, instead of E
        ELSE
        pshs    a
        lda     <D.TINIT        Get value from shadow
      IFNE  mc09
        anda    #$BF            force TR=0
        sta     <D.TINIT        Update shadow
        sta     >MMUADR         Update MMU
      ELSE
        anda    #$FE            force TR=0
        sta     <D.TINIT
        sta     >DAT.Task
      ENDC
        puls    a
        ENDC
        clr     <D.SSTskN
        tfr     x,s
        tfr     a,cc
        rts
SubSiz  equ     *-SubStrt
* ^
* Code around L0065, below, assumes that Vectors follows on directly after
* the end of R.Flip0. Therefore, DO NOT ADD ADD ANYTHING BETWEEN THESE 2 LABELS
* v
* Interrupt service routine
Vectors jmp     [<-(D.SWI3-D.XSWI3),x]  (-$10) (Jmp to 2ndary vector)

      IFNE  mc09
CPUVect fdb SWI3VCT+Where       SWI3  at $FFF2
        fdb SWI2VCT+Where       SWI2  at $FFF4
        fdb FIRQVCT+Where       FIRQ  at $FFF6
        fdb IRQVCT+Where        IRQ   at $FFF8
        fdb SWIVCT+Where        SWI   at $FFFA
        fdb NMIVCT+Where        NMI   at $FFFC
        fdb $0000+Where         RESET at $FFFE
      ENDC

* [NAC HACK 2016Dec07] to do a real reset on Multicomp09 need first to
* disable the MMU and re-enable the ROM. Maybe implement a little blob
* of code to do that? Otherwise, implement some kind of crash/dump.

* Initialize the system block (the lowest 8Kbytes of memory)
* rel.asm has cleared the DP already, so start at address $100.
entry   equ     *
      IFNE    H6309
        ldq     #$01001f00      start address to clear & # bytes to clear
        leay    <entry+2,pc     point to a 0
        tfm     y,d+
        std     <D.CCStk        set pointer to top of global memory to $2000
        lda     #$01            set task user table to $0100
      ELSE
        ldx     #$100           start address
        ldy     #$2000-$100     bytes to clear
        clra
        clrb
L001C   std     ,x++            clear it 16-bits at a time
        leay    -2,y
        bne     L001C
        stx     <D.CCStk        Set pointer to top of global memory to $2000
        inca                    D = $0100
      ENDC

* Set up system variables in DP
        std     <D.Tasks        set Task Structure pointer to $0100
        addb    #$20
        std     <D.TskIPt       set Task image table pointer to $0120
        clrb

********************************************************************
* The memory block map is a data structure that is used to manage
* physical memory. Physical memory is assigned in 8Kbyte "blocks".
* 256 bytes are reserved for the map and so the maximum physical
* memory size is 256*8Kbyte=2Mbyte. D.BlkMap is a pointer to the
* start of the map (set to $0200, below). D.BlkMap+2 is a pointer
* to the end of the map. Rather than simply setting it to $0300,
* the end pointer is set by the memory sizing routine at L0111.
* (Presumably) this makes it faster to search for unused pages
* and also acts as the mechanism to avoid assigning non-existent
* memory. A value of 0 indicates an unused block and since the
* system block has been initialised to 0 (above) every block starts
* off marked as unused. Initial reservation of blocks occurs
* below, after the memory sizing.
* See "Level 2 flags" in os9.d for other byte values.

        inca                    set memory block map start pointer
        std     <D.BlkMap       to $0200

        inca                    set system service dispatch table pointer
        std     <D.SysDis       to 0x300
        inca                    set user dispatch table pointer to $0400
        std     <D.UsrDis
        inca                    set process descriptor block pointer to $0500
        std     <D.PrcDBT
        inca                    set system process descriptor pointer to $0600
        std     <D.SysPrc
        std     <D.Proc         set user process descriptor pointer to $0600
        adda    #$02            set stack pointer to $0800
        tfr     d,s
        inca                    set system stack base pointer to $0900
        std     <D.SysStk
        std     <D.SysMem       set system memory map ptr $0900
        inca                    set module directory start ptr to $0a00
        std     <D.ModDir
        std     <D.ModEnd       set module directory end ptr to $0a00
        adda    #$06            set secondary module directory start to $1000
        std     <D.ModDir+2
        std     <D.ModDAT       set module directory DAT pointer to $1000
        std     <D.CCMem        set pointer to beginning of global memory to $1000
* In following line, CRC=ON if it is STA <D.CRC, CRC=OFF if it is a STB <D.CRC
        stb     <D.CRC          set CRC checking flag to off

* Initialize interrupt vector tables in DP by moving pointer data down from DisTable

      IFNE  mc09
* Brett's ccbkrn identified this as a bug in the original code..
* which has not been fixed. Should be easy to demonstrate which is
* correct..
        leay    DisTable,pcr   point to table of absolute vector addresses
      ELSE
        leay    <DisTable,pcr   point to table of absolute vector addresses
      ENDC
        ldx     #D.Clock        where to put it in memory
        IFNE    H6309
        ldf     #DisSize        size of the table - E=0 from TFM, above
        tfm     y+,x+           move it over
        ELSE
        ldb     #DisSize
l@
        lda     ,y+             load a byte from source
        sta     ,x+             store a byte to dest
        decb                    bump counter
        bne     l@              loop if we're not done
        ENDC

* Initialize D.Flip0 routine in low memory by copying lump of code down from R.Flip0.
* ASSUME: Y left pointing to R.Flip0 by previous copy loop.

        ldu     #LowSub         somewhere in block 0 that's never modified
        stu     <D.Flip0        switch to system task 0
        IFNE    H6309
        ldf     #SubSiz         size of it
        tfm     y+,u+           copy it over
        ELSE
        ldb     #SubSiz
Loop2   lda     ,y+             load a byte from source
        sta     ,u+             and save to destination
        decb                    bump counter
        bne     Loop2           loop if not done
        ENDC

* Initialize secondary interrupt vectors to all point to Vectors for now
* ASSUME: Y left pointing to Vectors by previous copy loop
        tfr     y,u             move the pointer to a faster register
L0065   stu     ,x++            Set all IRQ vectors to go to Vectors for now
        cmpx    #D.NMI
        bls     L0065

      IFNE  mc09
* Initialize CPU vectors
        leay    CPUVect,pcr     Data source
        ldx     #$FFF2          Data destination
        ldb     #14             7 vectors to copy
L0067   lda     ,y+
        sta     ,x+
        decb
        bne     L0067
      ENDC

* Initialize user interupt vectors
        ldx     <D.XSWI2        Get SWI2 (os9 command) service routine pointer
        stx     <D.UsrSvc       Save it as user service routine pointer
        ldx     <D.XIRQ         Get IRQ service routine pointer
        stx     <D.UsrIRQ       Save it as user IRQ routine pointer

        leax    >SysCall,pc     Setup System service routine entry vector
        stx     <D.SysSvc
        stx     <D.XSWI2

        leax    >S.SysIRQ,pc    Setup system IRQ service vector
        stx     <D.SysIRQ
        stx     <D.XIRQ

        leax    >S.SvcIRQ,pc    Setup in system IRQ service vector
        stx     <D.SvcIRQ
        leax    >S.Poll,pc      Setup interrupt polling vector
        stx     <D.Poll         ORCC #$01;RTS
        leax    >S.AltIRQ,pc    Setup alternate IRQ vector: pts to an RTS
        stx     <D.AltIRQ

        lda     #'K             debug: signal that we are in Kernel
        jsr     <D.BtBug

        leax    >S.Flip1,pc     Setup change to task 1 vector
        stx     <D.Flip1

* Setup System calls
        leay    >SysCalls,pc    load y with address of table, below
        lbsr    SysSvc          copy table below into dispatch table

* Initialize system process descriptor
        ldu     <D.PrcDBT       get process table pointer
        ldx     <D.SysPrc       get system process pointer

* These overlap because it is quicker than trying to strip hi byte from X
        stx     ,u              save it as first process in table
        stx     1,u             save it as the second as well
      IFNE    H6309
        oim     #$01,P$ID,x     Set process ID to 1 (inited to 0)
        oim     #SysState,P$State,x     Set to system state (inited to 0)
      ELSE
        ldd     #$01*256+SysState
        sta     P$ID,x          set PID to 1
        stb     P$State,x       set state to system (*NOT* zero )
      ENDC
        clra                    set System task as task #0
        sta     <D.SysTsk
        sta     P$Task,x
        coma                    Setup its priority & age ($FF)
        sta     P$Prior,x
        sta     P$Age,x
        leax    <P$DATImg,x     point to DAT image
        stx     <D.SysDAT       save it as a pointer in DP
* actually, since block 0 is tfm'd to be zero, we can skip the next 2 lines
      IFNE    H6309
        clrd
      ELSE
        clra
        clrb
      ENDC
        std     ,x++            initialize 1st block to 0 (for this DP)

********************************************************************
* The DAT image is a data structure that is used to indicate which
* Dynamic Address Translator (DAT) mapping registers are in use.

* [NAC HACK 2016Dec06] future: I should be able to make this 7 if not 8..
* DAT.BlCt-ROMCount-RAMCount = 8 - 1 - 1 = 6
        lda     #$06            initialize the rest of the blocks to be free
        ldu     #DAT.Free
L00EF   stu     ,x++            store free "flag"
        deca                    bump counter
        bne     L00EF           loop if not done

        ldu     #KrnBlk         Block where the kernel will live
        stu     ,x

        ldx     <D.Tasks        Point to task user table
        inc     ,x              mark first 2 in use (system & GrfDrv)
        inc     1,x

********************************************************************
* The system memory map is a data structure that is used to manage
* the 64Kbyte CPU address space. D.SysMem is a pointer to the start
* of the map (set to $0900, above) and the map is a fixed size of
* 256 bytes. Each byte in the map represents one 256-byte "page"
* (256 entries of 256 bytes is 64Kbytes). A value of 0 indicates
* an unused page and since the system block has been initialised
* to 0 (above) every page starts off marked as unused.
* See "Level 2 flags" in os9.d for other byte values.

* Update the system memory map to reserve the area used for
* global memory.
        ldx     <D.SysMem       Get system memory map pointer
        ldb     <D.CCStk        Get MSB of top of CC memory
* X indexes the system memory map.
* B represents the number of 256-byte pages available.
* Walk through the map changing the corresponding elements
* from 0 (the initialisation value) to 1 (indicating 'used'). Higher
* entries in the map remain as 0 (indicating 'unused').
L0104   inc     ,x+             Mark it as used
        decb                    Done?
        bne     L0104           No, go back till done

********************************************************************
* Deduce how many 8Kbyte blocks of physical memory are available and
* update the memory block map end pointer (D.BlkMap+2) accordingly
        ldx     <D.BlkMap       get ptr to 8k block map
        inc     <KrnBlk,x       mark block holding kernel as used
      IFNE  mc09
        inc     <$00,x          mark block $00 as used (global memory)
* For mc09 memory size is 512Kbyte or 1MByte. For now, hard-wire
* the memory size to 512Kbyte.
        ldd     #$0240
      ELSE
* This memory sizing routine uses location at X (D.BlkMap) as
* a scratch location. At exit, it leaves this location at 1 which
* has the (until now) undocumented side-effect of marking block 0
* as used. It is essential that this is done because that block
* does need to be reserved; it's used for global memory.
        IFNE    H6309
        ldq     #$00080100      e=Marker, D=Block # to check
L0111   asld                    get next block #
        stb     >DAT.Regs+5     Map block into block 6 of my task
        ste     >-$6000,x       save marker to that block
        cmpe    ,x              did it ghost to block 0?
        bne     L0111           No, keep going till ghost is found
        stb     <D.MemSz        Save # 8k mem blocks that exist
        addr    x,d             add number of blocks to block map start
        ELSE
        ldd     #$0008
L0111   aslb
        rola
        stb     >DAT.Regs+5
        pshs    a
        lda     #$01
        sta     >-$6000,x
        cmpa    ,x
        puls    a
        bne     L0111
        stb     <D.MemSz
        pshs    x
        addd    ,s++
        ENDC
      ENDC
        std     <D.BlkMap+2     save memory block map end pointer

********************************************************************
* Initial reservation of blocks in the memory block map. Code above
* reserved one block (block 0) for global memory and one block
* (usually block $3F) for krn.
*
* At this point, the value of D indicates the memory size:
* $0210 - 128k  ( 16, 8KByte blocks)
* $0220 - 256k  ( 32, 8KByte blocks)
* $0240 - 512k  ( 64, 8KByte blocks)
* $0280 - 1024k (128, 8KByte blocks)
* $0300 - 2048k (256, 8KByte blocks)
        bitb    #%00110000      block above 128K-256K?
        beq     L0170           yes, no need to mark block map
        tstb                    2 meg?
        beq     L0170           yes, skip this
* Mark blocks from 128k-256K to block $3F as NOT RAM
        abx                     add maximum block number to block map start
        leax    -1,x            Skip good blocks that are RAM
        lda     #NotRAM         Not RAM flag
        subb    #$3F            Calculate # blocks to mark as not RAM
L0127   sta     ,x+             Mark them all
        decb
        bne     L0127

* ASSUME: however we got here, B=0
L0170   ldx     #Bt.Start       start address of the boot track in memory
        lda     #18             size of the boot track is $1800

* Verify the modules in the boot track and update/build a module index
        lbsr    I.VBlock
        bsr     L01D2           go mark system map

* See if init module is in memory already
L01B0   leax    <init,pc        point to 'Init' module name
        bsr     link            try & link it
        bcc     L01BF           no error, go on
L01B8   os9     F$Boot          error linking init, try & load boot file
        bcc     L01B0           got it, try init again
        bra     L01CE           error, re-booting do D.Crash

* So far, so good. Save pointer to init module and execute krnp2
L01BF   stu     <D.Init         Save init module pointer
        lda     Feature1,u      Get feature byte #1 from init module
        bita    #CRCOn          CRC feature on?
        beq     ShowI           if not, continue
        inc     <D.CRC          else inc. CRC flag

ShowI   lda     #'i             debug: signal that we found the init module
        jsr     <D.BtBug

L01C1   leax    <krnp2,pc       Point to its name
        bsr     link            Try to link it
        bcc     L01D0           It worked, execute it
        os9     F$Boot          It doesn't exist try re-booting
        bcc     L01C1           No error's, let's try to link it again
L01CE   jmp     <D.Crash        obviously can't do it, crash machine
L01D0   jmp     ,y              execute krnp2

* Update the system memory map to reserve the area used by the kernel
L01D2   ldx     <D.SysMem       Get system memory map pointer
        ldd     #NotRAM*256+(Bt.Start/256)      B = MSB of start of the boot
        abx                     point to Bt.Start - start of boot track
        comb                    we have $FF-$ED pages to mark as inUse
        sta     b,x             Mark I/O as not RAM
L01DF   lda     #RAMinUse       get inUse flag
L01E1   sta     ,x+             mark this page
        decb                    done?
        bne     L01E1           no, keep going
        ldx     <D.BlkMap       get pointer to start of block map
        sta     <KrnBlk,x       mark kernel block as RAMinUse, instead of ModInBlk
S.AltIRQ        rts             return

* Link module pointed to by X
link    lda     #Systm          Attempt to link system module
        os9     F$Link
        rts

init    fcs     'Init'
krnp2   fcs     'krnp2'

* Service vector call pointers
SysCalls        fcb     F$Link
        fdb     FLink-*-2
        fcb     F$PrsNam
        fdb     FPrsNam-*-2
        fcb     F$CmpNam
        fdb     FCmpNam-*-2
        fcb     F$CmpNam+SysState
        fdb     FSCmpNam-*-2
        fcb     F$CRC
        fdb     FCRC-*-2
        fcb     F$SRqMem+SysState
        fdb     FSRqMem-*-2
        fcb     F$SRtMem+SysState
        fdb     FSRtMem-*-2
        fcb     F$AProc+SysState
        fdb     FAProc-*-2
        fcb     F$NProc+SysState
        fdb     FNProc-*-2
        fcb     F$VModul+SysState
        fdb     FVModul-*-2
        fcb     F$SSvc+SysState
        fdb     FSSvc-*-2
        fcb     F$SLink+SysState
        fdb     FSLink-*-2
        fcb     F$Boot+SysState
        fdb     FBoot-*-2
        fcb     F$BtMem+SysState
        fdb     FSRqMem-*-2
        IFNE    H6309
        fcb     F$CpyMem
        fdb     FCpyMem-*-2
        ENDC
        fcb     F$Move+SysState
        fdb     FMove-*-2
        fcb     F$AllImg+SysState
        fdb     FAllImg-*-2
        fcb     F$SetImg+SysState
        fdb     FFreeLB-*-2
        fcb     F$FreeLB+SysState
        fdb     FSFreeLB-*-2
        fcb     F$FreeHB+SysState
        fdb     FFreeHB-*-2
        fcb     F$AllTsk+SysState
        fdb     FAllTsk-*-2
        fcb     F$DelTsk+SysState
        fdb     FDelTsk-*-2
        fcb     F$SetTsk+SysState
        fdb     FSetTsk-*-2
        fcb     F$ResTsk+SysState
        fdb     FResTsk-*-2
        fcb     F$RelTsk+SysState
        fdb     FRelTsk-*-2
        fcb     F$DATLog+SysState
        fdb     FDATLog-*-2
        fcb     F$LDAXY+SysState
        fdb     FLDAXY-*-2
        fcb     F$LDDDXY+SysState
        fdb     FLDDDXY-*-2
        fcb     F$LDABX+SysState
        fdb     FLDABX-*-2
        fcb     F$STABX+SysState
        fdb     FSTABX-*-2
        fcb     F$ELink+SysState
        fdb     FELink-*-2
        fcb     F$FModul+SysState
        fdb     FFModul-*-2
        fcb     F$VBlock+SysState
        fdb     FVBlock-*-2
        IFNE    H6309
        fcb     F$DelRAM
        fdb     FDelRAM-*-2
        ENDC
        fcb     $80

* SWI3 vector entry
XSWI3   lda     #P$SWI3         point to SWI3 vector
        fcb     $8C             skip 2 bytes

* SWI vector entry
XSWI    lda     #P$SWI          point to SWI vector
        ldx     <D.Proc         get process pointer
        ldu     a,x             user defined SWI[x]?
        beq     L028E           no, go get option byte
GoUser  lbra    L0E5E           Yes, go call users's routine

* SWI2 vector entry
XSWI2   ldx     <D.Proc         get current process descriptor
        ldu     P$SWI2,x        any SWI vector?
        bne     GoUser          yes, go execute it

* Process software interupts from a user state
* Entry: X=Process descriptor pointer of process that made system call
*        U=Register stack pointer
L028E   ldu     <D.SysSvc       set system call processor to system side
        stu     <D.XSWI2
        ldu     <D.SysIRQ       do the same thing for IRQ's
        stu     <D.XIRQ
        IFNE    H6309
        oim     #SysState,P$State,x     mark process as in system state
        ELSE
        lda     P$State,x
        ora     #SysState
        sta     P$State,x
        ENDC
* copy register stack to process descriptor
        sts     P$SP,x          save stack pointer
        leas    (P$Stack-R$Size),x      point S to register stack destination

        IFNE    H6309
        leau    R$Size-1,s      point to last byte of destination register stack
        leay    -1,y            point to caller's register stack in $FEE1
        ldw     #R$Size         size of the register stack
        tfm     y-,u-
        leau    ,s              needed because the TFM is u-, not -u (post, not pre)
        ELSE
* Note!  R$Size MUST BE an EVEN number of bytes for this to work!
        leau    R$Size,s        point to last byte of destination register stack
        lda     #R$Size/2
Loop3   ldx     ,--y
        stx     ,--u
        deca
        bne     Loop3
        ENDC
        andcc   #^IntMasks
* B=function code already from calling process: DON'T USE IT!
        ldx     R$PC,u          get where PC was from process
        leax    1,x             move PC past option
        stx     R$PC,u          save updated PC to process
* execute function call
        ldy     <D.UsrDis       get user dispatch table pointer
        lbsr    L033B           go execute option
        IFNE    H6309
        aim     #^IntMasks,R$CC,u       Clear interrupt flags in caller's CC
        ELSE
        lda     R$CC,u
        anda    #^IntMasks
        sta     R$CC,u
        ENDC
        ldx     <D.Proc         get current process ptr
        IFNE    H6309
        aim     #^(SysState+TimOut),P$State,x   Clear system & timeout flags
        ELSE
        lda     P$State,x
        anda    #^(SysState+TimOut)
        sta     P$State,x
        ENDC

* Check for image change now, which lets stuff like F$MapBlk and F$ClrBlk
* do the short-circuit thing, too.  Adds about 20 cycles to each system call.
        lbsr    TstImg          it doesn't hurt to call this twice
        lda     P$State,x       get current state of the process
        ora     <P$Signal,x     is there a pending signal?
        sta     <D.Quick        save quick return flag
        beq     AllClr          if nothing's have changed, do full checks

DoFull  bsr     L02DA           move the stack frame back to user state
        lbra    L0D80           go back to the process

* add ldu P$SP,x, etc...
AllClr  equ     *
        IFNE    H6309
        inc     <D.QCnt
        aim     #$1F,<D.QCnt
        beq     DoFull          every 32 system calls, do the full check
        ldw     #R$Size         --- size of the register stack
        ldy     #Where+SWIStack --- to stack at top of memory
        orcc    #IntMasks
        tfm     u+,y+           --- move the stack to the top of memory
        ELSE
        lda     <D.QCnt
        inca
        anda    #$1F
        sta     <D.QCnt
        beq     DoFull
        ldb     #R$Size
        ldy     #Where+SWIStack
        orcc    #IntMasks
Loop4   lda     ,u+
        sta     ,y+
        decb
        bne     Loop4
        ENDC
        lbra    BackTo1         otherwise simply return to the user

* Copy register stack from user to system
* Entry: U=Ptr to Register stack in process dsc
L02CB   pshs    cc,x,y,u        preserve registers
        ldb     P$Task,x        get task #
        ldx     P$SP,x  get stack pointer
        lbsr    L0BF3           calculate block offset (only affects A&X)
        leax    -$6000,x        adjust pointer to where memory map will be
        bra     L02E9           go copy it

* Copy register stack from system to user
* Entry: U=Ptr to Register stack in process dsc
L02DA   pshs    cc,x,y,u        preserve registers
        ldb     P$Task,x        get task # of destination
        ldx     P$SP,x          get stack pointer
        lbsr    L0BF3           calculate block offset (only affects A&X)
        leax    -$6000,x        adjust pointer to where memory map will be
        exg     x,y             swap pointers & copy
* Copy a register stack
* Entry: X=Source
*        Y=Destination
*        A=Offset into DAT image of stack
*        B=Task #
L02E9   leau    a,u             point to block # of where stack is
      IFNE  mc09
        orcc    #IntMasks       shutdown interupts while we do this

        lda     #5
        bsr     prepmmu         Select block 5

        lda     1,u             get first block
        ldb     3,u             get a second just in case of overlap

        sta     >MMUDAT         Set value for block 5

        lda     #6
        bsr     prepmmu         Select block 6

        stb     >MMUDAT         Set value for block 6

        ldb     #R$Size
Loop5   lda     ,x+
        sta     ,y+
        decb
        bne     Loop5
        ldx     <D.SysDAT       remap the blocks we took out

        lda     #5
        bsr     prepmmu         Select block 5

        lda     $0B,x
        ldb     $0D,x

        sta     >MMUDAT         Restore value for block 5
        lda     #6
        bsr     prepmmu         Select block 6

        stb     >MMUDAT         Restore value for block 6
      ELSE
        lda     1,u             get first block
        ldb     3,u             get a second just in case of overlap
        orcc    #IntMasks       shutdown interupts while we do this
        std     >DAT.Regs+5     map blocks in
      IFNE    H6309
        ldw     #R$Size         get size of register stack
        tfm     x+,y+           copy it
      ELSE
        ldb     #R$Size
Loop5   lda     ,x+
        sta     ,y+
        decb
        bne     Loop5
      ENDC
        ldx     <D.SysDAT       remap the blocks we took out
        lda     $0B,x
        ldb     $0D,x
        std     >DAT.Regs+5
      ENDIF
        puls    cc,x,y,u,pc     restore & return

      IFNE  mc09
* A holds the MMU register we want to select. Merge in
* the stored value and write the result to MMUADR. This is
* a desperate attempt to save a few bytes..
prepmmu
        ora     <D.TINIT        Merge with current MMU mask
        sta     >MMUADR         Select block
        rts
      ENDIF


* Process software interupts from system state
* Entry: U=Register stack pointer
SysCall leau    ,s              get pointer to register stack
        lda     <D.SSTskN       Get system task # (0=SYSTEM, 1=GRFDRV)
        clr     <D.SSTskN       Force to System Process
        pshs    a               Save the system task number
        lda     ,u              Restore callers CC register (R$CC=$00)
        tfr     a,cc            make it current
        ldx     R$PC,u          Get my caller's PC register
        leax    1,x             move PC to next position
        stx     R$PC,u          Save my caller's updated PC register
        ldy     <D.SysDis       get system dispatch table pointer
        bsr     L033B           execute system call
        puls    a               restore system state task number
        lbra    L0E2B           return to process

* Entry: X = system call vector to jump to
Sys.Vec jmp     ,x              execute service call

* Execute system call
* Entry: B=Function call #
*        Y=Function dispatch table pointer (D.SysDis or D.UsrDis)
L033B
        lslb                    is it a I/O call? (Also multiplys by 2 for offset)
        bcc     L0345           no, go get normal vector
* Execute I/O system calls
        ldx     IOEntry,y       get IOMan vector
* Execute the system call
L034F   pshs    u               preserve register stack pointer
        jsr     [D.SysVec]      perform a vectored system call
        puls    u               restore pointer
L0355   tfr     cc,a            move CC to A for stack update
        bcc     L035B           go update it if no error from call
        stb     R$B,u           save error code to caller's B
L035B   ldb     R$CC,u          get callers CC, R$CC=$00
        IFNE    H6309
        andd    #$2FD0          [A]=H,N,Z,V,C [B]=E,F,I
        orr     b,a             merge them together
        ELSE
        anda    #$2F            [A]=H,N,Z,V,C
        andb    #$D0            [B]=E,F,I
        pshs    b
        ora     ,s+
        ENDC
        sta     R$CC,u          return it to caller, R$CC=$00
        rts

* Execute regular system calls
L0345
        clra                    clear MSB of offset
        ldx     d,y             get vector to call
        bne     L034F           it's initialized, go execute it
        comb                    set carry for error
        ldb     #E$UnkSvc       get error code
        bra     L0355           return with it

        use     fssvc.asm

        use     flink.asm

        use     fvmodul.asm

        use     ffmodul.asm

        use     fprsnam.asm

        use     fcmpnam.asm

        use     fsrqmem.asm

*         use   fallram.asm


        IFNE    H6309
        use     fdelram.asm
        ENDC

        use     fallimg.asm

        use     ffreehb.asm

        use     fdatlog.asm

        use     fld.asm

        IFNE    H6309
        use     fcpymem.asm
        ENDC

        use     fmove.asm

        use     fldabx.asm

        use     falltsk.asm

        use     faproc.asm

* System IRQ service routine
XIRQ    ldx     <D.Proc         get current process pointer
        sts     P$SP,x          save the stack pointer
        lds     <D.SysStk       get system stack pointer
        ldd     <D.SysSvc       set system service routine to current
        std     <D.XSWI2
        ldd     <D.SysIRQ       set system IRQ routine to current
        std     <D.XIRQ
        jsr     [>D.SvcIRQ]     execute irq service
        bcc     L0D5B

        ldx     <D.Proc         get current process pointer
        ldb     P$Task,x
        ldx     P$SP,x          get it's stack pointer

        pshs    u,d,cc          save some registers
        leau    ,s              point to a 'caller register stack'
        lbsr    L0C40           do a LDB 0,X in task B
        puls    u,d,cc          and now A ( R$A,U ) = the CC we want

        ora     #IntMasks       disable it's IRQ's
        lbsr    L0C28           save it back
L0D5B   orcc    #IntMasks       shut down IRQ's
        ldx     <D.Proc         get current process pointer
        tst     <D.QIRQ         was it a clock IRQ?
        lbne    L0DF7           if not, do a quick return

        lda     P$State,x       Get it's state
        bita    #TimOut         Is it timed out?
        bne     L0D7C           yes, wake it up
* Update active process queue
        ldu     #(D.AProcQ-P$Queue)     point to active process queue
        ldb     #Suspend        get suspend flag
L0D6A   ldu     P$Queue,u       get a active process pointer
        beq     L0D78
        bitb    P$State,u       is it suspended?
        bne     L0D6A           yes, go to next one in chain
        ldb     P$Prior,x       get current process priority
        cmpb    P$Prior,u       do we bump this one?
        blo     L0D7C

L0D78   ldu     P$SP,x
        bra     L0DB9

L0D7C   anda    #^TimOut
        sta     P$State,x

L0D80   equ     *
L0D83   bsr     L0D11           activate next process

        use     fnproc.asm

* The following routines must appear no earlier than $E00 when assembled, as
* they have to always be in the vector RAM pages ($FE00-$FEFF)

      IFNE  mc09
* Copied nicer automatic padding from ccbkrn
* CCB: this code (after pad) start assembling *before* 0xfe00, it's too big to
* fit into the memory as stated above!!!!

PAD     fill    $00,($0dfc-*)   fill memory to ensure the above happens
      ELSE
PAD     fill    $00,($0df1-*)   fill memory to ensure the above happens
      ENDC


* Default routine for D.SysIRQ
S.SysIRQ
        lda     <D.SSTskN       Get current task's GIME task # (0 or 1)
        beq     FastIRQ         Use super-fast version for system state
        clr     <D.SSTskN       Clear out memory copy (task 0)
        jsr     [>D.SvcIRQ]     (Normally routine in Clock calling D.Poll)
        inc     <D.SSTskN       Save task # for system state
      IFNE  mc09
        lda     #$40            mc09 MMU Task 1
        ora     <D.TINIT        Merge task bit into Shadow version
        sta     <D.TINIT        Update shadow
        sta     >MMUADR         Save to MMU as well
      ELSE
        lda     #1              Task 1
        ora     <D.TINIT        Merge task bit into Shadow version
        sta     <D.TINIT        Update shadow
        sta     >DAT.Task       Save to GIME as well
      ENDC
        bra     DoneIRQ         Check for error and exit

FastIRQ jsr     [>D.SvcIRQ]     (Normally routine in Clock calling D.Poll)
DoneIRQ bcc     L0E28   No error on IRQ, exit
        IFNE    H6309
        oim     #IntMasks,0,s   Setup RTI to shut interrupts off again
        ELSE
        lda     ,s
        ora     #IntMasks
        sta     ,s
        ENDC
L0E28   rti

* return from a system call
L0E29   clra                    Force System task # to 0 (non-GRDRV)
L0E2B   ldx     <D.SysPrc       Get system process dsc. ptr
        lbsr    TstImg          check image, and F$SetTsk (PRESERVES A)
        orcc    #IntMasks       Shut interrupts off
        sta     <D.SSTskN       Save task # for system state
        beq     Fst2            If task 0, we're done
      IFNE  mc09
        lda     #$40            [NAC HACK 2016Dec07] hope only 1 bit means anything..
        ora     <D.TINIT        Merge task bit into Shadow version
        sta     <D.TINIT        Update shadow
        sta     >MMUADR         Save to MMU
      ELSE
        ora     <D.TINIT        Merge task bit into Shadow version
        sta     <D.TINIT        Update shadow
        sta     >DAT.Task       Save to GIME as well
      ENDC
Fst2    leas    ,u              Stack ptr=U & return
        rti

* Switch to new process, X=Process descriptor pointer, U=Stack pointer
L0E4C   equ     *
        IFNE    H6309
        oim     #$01,<D.TINIT   switch GIME shadow to user state
        lda     <D.TINIT
        ELSE
        lda     <D.TINIT
      IFNE  mc09
        ora     #$40
      ELSE
        ora     #$01
      ENDC
        sta     <D.TINIT
        ENDC
      IFNE  mc09
        sta     >MMUADR         save it to MMU
      ELSE
        sta     >DAT.Task       save it to GIME
      ENDC
        leas    ,y              point to new stack
        tstb                    is the stack at SWISTACK?
        bne     MyRTI           no, we're doing a system-state rti

        IFNE    H6309
        ldf     #R$Size         E=0 from call to L0E8D before
        ldu     #Where+SWIStack point to the stack
        tfm     u+,y+           move the stack from top of memory to user memory
        ELSE
        ldb     #R$Size
        ldu     #Where+SWIStack point to the stack
RtiLoop lda     ,u+
        sta     ,y+
        decb
        bne     RtiLoop
        ENDC
MyRTI   rti                     return from IRQ


* Execute routine in task 1 pointed to by U
* comes from user requested SWI vectors
L0E5E   equ     *
        IFNE    H6309
        oim     #$01,<D.TINIT   switch GIME shadow to user state
        ldb     <D.TINIT
        ELSE
        ldb     <D.TINIT
      IFNE  mc09
        orb     #$40
      ELSE
        orb     #$01
      ENDC
        stb     <D.TINIT
        ENDC
      IFNE  mc09
        stb     >MMUADR
      ELSE
        stb     >DAT.Task
      ENDC
        jmp     ,u

* Flip to task 1 (used by GRF/WINDInt to switch to GRFDRV) (pointed to
*  by <D.Flip1). All regs are already preserved on stack for the RTI
S.Flip1 ldb     #2              get Task image entry numberx2 for Grfdrv (task 1)
        bsr     L0E8D           copy over the DAT image
        IFNE    H6309
        oim     #$01,<D.TINIT
        lda     <D.TINIT        get copy of GIME Task side
        ELSE
        lda     <D.TINIT
      IFNE  mc09
        ora     #$40            force TR=1 in mc09 MMU
      ELSE
        ora     #$01            force TR=1
      ENDC
        sta     <D.TINIT
        ENDC
      IFNE  mc09
        sta     >MMUADR         save it to MMU register
      ELSE
        sta     >DAT.Task       save it to GIME register
      ENDC
        inc     <D.SSTskN       increment system state task number
        rti                     return

* Setup MMU in task 1, B=Task # to swap to, shifted left 1 bit
L0E8D   cmpb    <D.Task1N       are we going back to the same task
        beq     L0EA3           without the DAT image changing?
        stb     <D.Task1N       nope, save current task in map type 1
      IFNE  mc09
        ldu     <D.TskIPt       get task image pointer table
        ldu     b,u             get address of DAT image

        lda     <D.TINIT
        adda    #8              1st MMU value for process's mappings

* COME HERE FROM FALLTSK
* Update 8 MMU mappings.
* A = MMUADR value for 1st MMU register to update
* U = address of DAT image to update into MMU
L0E93   ldb     #8              number of MMU mappings to set
        pshs    b               squirrel it away
        leau    1,u             point to actual MMU block for 1st mapping

L0E9B   ldb     ,u++            get a bank, point to next bank
        std     >MMUADR         save it to MMU
        inca                    next mapsel value
        dec     ,s
        bne     L0E9B           no, keep going
        leas    1,s             done. Tidy up the stack
      ELSE
        ldx     #DAT.Regs+8     get MMU start register for process's
        ldu     <D.TskIPt       get task image pointer table
        ldu     b,u             get address of DAT image
* COME HERE FROM FALLTSK
* Update 8 MMU mappings.
* X = address of 1st DAT MMU register to update
* U = address of DAT image to update into MMU
L0E93   leau    1,u             point to actual MMU block
        IFNE    H6309
        lde     #4              get # banks/2 for task
        ELSE
        lda     #4
        pshs    a
        ENDC
L0E9B   lda     ,u++            get a bank
        ldb     ,u++            and next one
        std     ,x++            Save it to MMU
        IFNE    H6309
        dece                    done?
        ELSE
        dec     ,s
        ENDC
        bne     L0E9B           no, keep going
        IFEQ    H6309
        leas    1,s             done. Tidy up the stack
        ENDC
      ENDC
L0EA3   rts                     return

* Execute FIRQ vector (called from $FEF4)
FIRQVCT ldx     #D.FIRQ         get DP offset of vector
        bra     L0EB8           go execute it

* Execute IRQ vector (called from $FEF7)
IRQVCT  orcc    #IntMasks       disable IRQ's
        ldx     #D.IRQ  get DP offset of vector

* Execute interrupt vector, B=DP Vector offset
      IFNE mc09
L0EB8   lda     #$a0            [NAC HACK 2016Dec08] add equates..
        sta     >MMUADR         Force to System State (Task 0)
        clra
        tfr     a,dp            ASSUME: A=0 from earlier
MapGrf  equ     *               come here from elsewhere, too.
        lda     <D.TINIT
        anda    #$BF            force TR=0 in mc09 MMU shadow
        sta     <D.TINIT
MapT0   sta     >MMUADR         come here from elsewhere, too.
        jmp     [,x]            execute it
      ELSE
* Execute interrupt vector, B=DP Vector offset
L0EB8   clra                    (faster than CLR >$xxxx)
        sta     >DAT.Task       Force to Task 0 (system state)
        IFNE    H6309
        tfr     0,dp            setup DP
        ELSE
        tfr     a,dp            ASSUME: A=0 from earlier
        ENDC
MapGrf  equ     *               come here from elsewhere, too.
        IFNE    H6309
        aim     #$FE,<D.TINIT   switch GIME shadow to system state
        lda     <D.TINIT        set GIME again just in case timer is used
        ELSE
        lda     <D.TINIT
        anda    #$FE
        sta     <D.TINIT
        ENDC
MapT0   sta     >DAT.Task       come here from elsewhere, too.
        jmp     [,x]            execute it
      ENDC



* Execute SWI3 vector (called from $FEEE)
SWI3VCT orcc    #IntMasks       disable IRQ's
        ldx     #D.SWI3         get DP offset of vector
        bra     SWICall         go execute it

* Execute SWI2 vector (called from $FEF1)
SWI2VCT orcc    #IntMasks       disasble IRQ's
        ldx     #D.SWI2         get DP offset of vector

* This routine is called from an SWI, SWI2, or SWI3
* saves 1 cycle on system-system calls
* saves about 200 cycles (calls to I.LDABX and L029E) on grfdrv-system,
*  or user-system calls.
SWICall ldb     [R$PC,s]        get callcode of the system call
      IFNE  mc09
* [NAC HACK 2016Dec08] confused? it says, "go to map type 1" but
* it is setting a 0.
        lda     #$a0            [NAC HACK 2016Dec08] add equates..
        sta     >MMUADR         Force to System State (Task 0)
        clra
      ELSE
* NOTE: Alan DeKok claims that this is BAD.  It crashed Colin McKay's
* CoCo 3.  Instead, we should do a clra/sta >DAT.Task.
*         clr   >DAT.Task       go to map type 1
        clra
        sta     >DAT.Task
      ENDC
* set DP to zero
        IFNE    H6309
        tfr     0,dp
        ELSE
        tfr     a,dp            ASSUME: A=0 from earlier
        ENDC

* These lines add a total of 81 addition cycles to each SWI(2,3) call,
* and 36 bytes+12 for R$Size in the constant page at $FExx
*  It takes no more time for a SWI(2,3) from system state than previously,
* ... and adds 14 cycles to each SWI(2,3) call from grfdrv... not a problem.
* For processes that re-vector SWI, SWI3, it adds 81 cycles.  BUT SWI(3)
* CANNOT be vectored to L0EBF cause the user SWI service routine has been
* changed
        lda     <D.TINIT        get map type flag
      IFNE  mc09
        bita    #$40            check it without changing it in mc09 MMU
      ELSE
        bita    #$01            check it without changing it
      ENDC

* Change to LBEQ R.SysSvc to avoid JMP [,X]
* and add R.SysSvc STA >DAT.Task ???
        beq     MapT0           in map 0: restore hardware and do system service
        tst     <D.SSTskN       get system state 0,1
        bne     MapGrf          if in grfdrv, go to map 0 and do system service

* the preceding few lines are necessary, as all SWI's still pass thru
* here before being vectored to the system service routine... which
* doesn't copy the stack from user state.
      IFNE  mc09
        sta     >MMUADR         go to map type X again to get user's stack
      ELSE
        sta     >DAT.Task       go to map type X again to get user's stack
      ENDC
* a byte less, a cycle more than ldy #$FEED-R$Size, or ldy #$F000+SWIStack
        leay    <SWIStack,pc    where to put the register stack: to $FEDF
        tfr     s,u             get a copy of where the stack is
        IFNE    H6309
        ldw     #R$Size         get the size of the stack
        tfm     u+,y+           move the stack to the top of memory
        ELSE
        pshs    b
        ldb     #R$Size
Looper  lda     ,u+
        sta     ,y+
        decb
        bne     Looper
        puls    b
        ENDC
        bra     L0EB8           and go from map type 1 to map type 0

* Execute SWI vector (called from $FEFA)
SWIVCT  ldx     #D.SWI          get DP offset of vector
        bra     SWICall         go execute it

* Execute NMI vector (called from $FEFD)
NMIVCT  ldx     #D.NMI          get DP offset of vector
        bra     L0EB8           go execute it

* The end of the kernel module is here
        emod
eom     equ     *

* What follows after the kernel module is the register stack, starting
* at $FEDD (6309) or $FEDF (6809).  This register stack area is used by
* the kernel to save the caller's registers in the $FEXX area of memory
* because it doesn't get "switched out" no matter the contents of the
* MMU registers.
SWIStack
        fcc     /REGISTER STACK/ same # bytes as R$Size for 6809
        IFNE    H6309
        fcc     /63/             if 6309, add two more bytes of space
        ENDC

        fcb     $55              D.ErrRst

      IFNE  mc09
* For Multicomp09, the processor vectors are in RAM so they can be loaded
* with the service addresses directly, instead of requiring another indirection
* The vectors are set up by a data table copy of CPUVect
      ELSE
* This list of addresses ends up at $FEEE after the kernel track is loaded
* into memory.  All interrupts come through the 6809 vectors at $FFF0-$FFFE
* and get directed to here.  From here, the BRA takes CPU control to the
* various handlers in the kernel.
        bra     SWI3VCT SWI3 vector comes here
        nop
        bra     SWI2VCT SWI2 vector comes here
        nop
        bra     FIRQVCT FIRQ vector comes here
        nop
        bra     IRQVCT  IRQ vector comes here
        nop
        bra     SWIVCT  SWI vector comes here
        nop
        bra     NMIVCT  NMI vector comes here
        nop
      ENDC

* The final byte (eg the NOP after bra NMIVCT) should be at offset $EFF
* and will end up at address $FEFF in physical memory. If any code above
* is changed, you must inspect the listing and adjust the addresses at
* the label PAD.
        end