#undef INT_TOC5
#define INT_TOC5 PM_TaskSwitch  // set task switch interrupt vector



// -----------------------------------------
        SECT SECT_ZEROPAGE
*
* MODULE PROCMAN.ASM
*

PM_CurrentProcess       RMB 2             // store for corrent process
PM_TB_MainProcess       RMB PM_LEN_TB   // process block for first process
PM_TempSP               RMB 2           // for temporary SP storage


// -----------------------------------------
        SECT SECT_INIT
*
* MODULE PROCMAN.ASM
*
        JSR SIO_PutString
        FCC 'PM:  Initialising...'
        FCB 13,10,0

        LDX #PM_TB_MainProcess // task block for first process
        STX PM_CurrentProcess   // make it the current process,
        STX PM_OFS_TB_NEXT,X  // the next process,
        STX PM_OFS_TB_PREV,X  // and the previous process
        LDD #DEFAULT_TICKS
        STD PM_OFS_TB_TICKS,X // give it the default number of ticks
        LDD #NULL
        STD PM_OFS_TB_MEMLIST,X // owns no memory
        LDD #INIT_STACK
        STD PM_OFS_TB_TOS,X     // top of process's stack

        JSR SIO_PutString
        FCC 'PM:  Init task created'
        FCB 13,10,0

        LDD TCNT                        // get the current timer count
        ADDD PM_OFS_TB_TICKS,X        // add time slice of process
        STD TOC5                        // set output compare register
        BSET TMSK1 TMSK1_OC5I           // enable interrupts on OC5
        LDAA #TFLG1_OC5F
        STAA TFLG1                      // clear the OC5F flag
        CLI                             // enable interrupts

        JSR SIO_PutString
        FCC 'PM:  Switcher started'
        FCB 13,10,0

// -----------------------------------------
        SECT SECT_CODE
*
* MODULE PROCMAN.ASM
*

PM_TaskSwitch  // task switch interrupt handler
        LDX PM_CurrentProcess
        STS PM_OFS_TB_SP,X   // store stack pointer for outgoing process
        LDX PM_OFS_TB_NEXT,X // get incoming process
        STX PM_CurrentProcess  // set incoming process as current process
        LDS PM_OFS_TB_SP,X   // load stack pointer for incoming process
        LDD TCNT
        ADDD PM_OFS_TB_TICKS,X  // set TOC5 to end of time slice
        STD TOC5

        LDAA #TFLG1_OC5F          // reset OC5F flag to allow next interrupt
        STAA TFLG1

        RTI                    // switch to next process

_idle // void idle(); gives up the remainder of the current time slice
PM_Idle
PM_SwitchNow // causes an immediate task switch
        PSHY  // push all registers as if interrupt had
        PSHX  // occurred (PC is already on stack)
        PSHA  // these seem backwards but
        PSHB  // the hc11 is just weird...
        TPA
        PSHA
        SEI   // disable interrupts
        BRA PM_TaskSwitch  // switch task

PM_AddTask // adds task whos task block is pointed to by X to task list
           // the 'next' and 'prev' fields are changed, all others must
           // be set up beforehand
           // Adds process between current process and next
        LDD PM_CurrentProcess
        STD PM_OFS_TB_PREV,X // set up 'Prev' in new process
        XGDX // X -> current process, D -> new process
        XGDY // Y -> new process
        SEI // interrupts must be disabled here
        LDD PM_OFS_TB_NEXT,X // get process to be after new process
        STD PM_OFS_TB_NEXT,Y // set up 'Next' in new process
        XGDY // D -> new process, Y -> proc to be after new proc
        STD PM_OFS_TB_NEXT,X // set up 'Next' in current process
        STD PM_OFS_TB_PREV,Y // set up 'Prev' in process after new
        XGDX // point X back to new process
        CLI
        RTS

_fork   // int fork(int stacksize)
        // forks, returning 0 to the child and the child PID to the parent
        // the child's stack size is set to the given size in bytes
        TSX
        LDD 2,X
        BSR PM_Fork
        XGDX
        RTS

PM_Fork // UNIX style fork
        // D holds size of new stack
        // Returns to child with D=0
        // Returns to parent with X=child PID
        // X=PM_FORKFAILED if fork failed
        LDX #MM_Locked
        JSR PM_LockX

        JSR MM_BlockAlloc // try to allocate space for stack
        BNE PM_Fork_Failed_
        JMP PM_Fork_Failed
PM_Fork_Failed_

        XGDX
        XGDY // Y points to stack BLOCK

        LDD #PM_LEN_TB

        JSR MM_BlockAlloc // allocate space for task block
        BNE PM_Fork_Failed2_
        JMP PM_Fork_Failed2
PM_Fork_Failed2_

        LDAA #MM_BLOCK_TYPE_TB
        STAA MM_OFS_BLOCK_TYPE,X // set memory block type to task block
        XGDX
        ADDD #MM_BLOCK_LEN
        XGDX  // X now points to start of task block
        LDD #NULL
        STD PM_OFS_TB_MEMLIST,X // new process owns no memory
        STY PM_OFS_TB_STACK,X // set start of stack BLOCK

        PSHY
        PULA
        PULB
        ADDD MM_OFS_BLOCK_SIZE,Y
        XGDY
        DEY // Y now holds top of stack


        JSR SIO_PutString
        FCC 'PM: Fork child TOS='
        FCB SIO_Put_Y
        FCC ' PID='
        FCB SIO_Put_X,13,10,0

        STY PM_OFS_TB_TOS,X // set top of stack
        STS PM_TempSP

        LDD PM_OFS_TB_TOS,X
        LDS PM_OFS_TB_TOS,X // set SP to top of new stack

        XGDX // store new TB in D

        LDY PM_CurrentProcess
        LDX PM_OFS_TB_TOS,Y // set X to top of current stack
        XGDY // Y holds new TB

PM_Fork_CopyStack     // copy the stack from the old to the new task
        CPX PM_TempSP            // check if finished
        BEQ PM_Fork_StackCopied
        LDAA 0,X                 // get a byte from the old stack

        DEX
        PSHA                     // put the byte onto the new stack
        BRA PM_Fork_CopyStack

PM_Fork_StackCopied
        // set up child stack contents ready for task switch
        LDX #NULL
        PSHY      // value unimportant
        PSHX      // X=0 means process is child
        PSHA      // Values unimportant
        PSHB      //
        TPA       //
        PSHA      //
        XGDY
        XGDX //  X points to child's TB
        STS PM_OFS_TB_SP,X // store SP for new process

        LDS PM_TempSP // restore parent's SP
        LDY PM_CurrentProcess
        LDD PM_OFS_TB_TICKS,Y
        STD PM_OFS_TB_TICKS,X // child starts with same priority as parent
        LDD PM_OFS_TB_STATUS,Y
        STD PM_OFS_TB_STATUS,X // and same status
        JSR PM_AddTask  // add child's task to task list
        BRA PM_Fork_End
PM_Fork_Failed2
        //        DEALLOCATE STACK MEMORY!!!
PM_Fork_Failed
        LDX #PM_FORKFAILED
PM_Fork_End
        PSHX
        LDX #MM_Locked
        JSR PM_UnlockX
        PULX
        CPX #NULL
        RTS