/*On each core, tasks are linked in a
circular doubly-linked list queue with
the Executive task as the head.  On Core0,
the queue order represents the front-to-back
window stack order with the window mgr
as the wallpaper.

The scheduler is round-robin.  It checks
if a task is ready and runs it or skips it.
Swapping tasks just involves storing and
restoring regs (no disk I/O for virtual
memory and no addr map changes).  It is
always fully identity-mapped on all cores.
Tasks can be switched in half a microsecond.

The scheduler checks if a task is
waiting for a certain time or waiting
on a message and skips if not ready.
A task runs until it voluntarily yields ctrl
with a call to Yield().  Tasks waiting on I/O
often loop, checking for a status and
Yielding.  This does not really degrade
performance, but pegs the CPU Load.

The scheduler checks for a few keys:

<CTRL-ALT-x> kill a task.
<CTRL-ALT-DEL> reboots.
<CTRL-ALT-n> Next task.
<CTRL-ALT-c> breaks execution of a program.

Each core has its own circular task queue.
For AP processors, they have an "Executive" task
which stays in a loop waiting for jobs or
requests to spawn tasks.  See CoreAPExecutiveTask().
*/

U0 TaskFocusNext()
{
        CTask *task, *_task = sys_focus_task;

        sys_focus_task = NULL;
        if (!_task)
                _task = sys_task;
        task = _task->next_task;
        do
        {
                if (!Bt(&task->win_inhibit, WIf_SELF_FOCUS))
                {
                        sys_focus_task = task;
                        CallExtNum(EXT_WIN_TO_TOP, task, TRUE);
                        return;
                }
                task = task->next_task;
        }
        while (task != _task);
}

asm {
TASK_CONTEXT_SAVE::
//OUT:  RSI=FS
                                PUSH            RSI
                                PUSHFD
                                XOR             RSI, RSI
                                MOV             RSI, FS:CTask.addr[RSI]
                                POP             U64 CTask.rflags[RSI]
                                POP             U64 CTask.rsi[RSI]
                                MOV             U64 CTask.rax[RSI], RAX

/*Divert the stack to the Task memory
and push onto it and divert it back.
It's a little faster.
*/
                                MOV             RAX, RSP
                                LEA             RSP, U64 CTask.r15+8[RSI]
                                PUSH            R15
                                PUSH            R14
                                PUSH            R13
                                PUSH            R12
                                PUSH            R11
                                PUSH            R10
                                PUSH            R9
                                PUSH            R8
                                PUSH            RDI
                                PUSH            RBP
                                PUSH            RBX
                                PUSH            RDX
                                PUSH            RCX
                                MOV             RSP, RAX

                                MOV             RAX, U64 CTask.fpu_mmx[RSI]
                                FXSAVE          U64 [RAX]

                                MOV             RDX, U64 CTask.bpt_list[RSI]
@@05:                   TEST            RDX, RDX
                                JZ                      @@10
                                MOV             RDI, U64 CBpt.addr[RDX]
                                MOV             AL,  U8 CBpt.val[RDX]
                                MOV             U8 [RDI], AL
                                MOV             RDX, U64 CBpt.next[RDX]
                                JMP             @@05
@@10:                   RET

//************************************
_TASK_CONTEXT_RESTORE::
                                XOR             RAX, RAX
                                INC             U64 GS:CCPU.swap_counter[RAX]
                                MOV             RSI, FS:CTask.addr[RAX]
                                BT                      U32 CTask.rflags[RSI], RFLAGf_INT
                                JNC             @@05
                                BTS             U32 GS:CCPU.cpu_flags[RAX], CPUf_RAN_A_TASK
@@05:                   BT                      U64 CTask.task_flags[RSI], TASKf_DISABLE_BPTS
                                JC                      @@15
                                MOV             RDX, U64 CTask.bpt_list[RSI]
@@10:                   TEST            RDX, RDX
                                JZ                      @@15
                                MOV             RDI, U64 CBpt.addr[RDX]
                                MOV             U8 [RDI], OC_BPT
                                MOV             RDX, U64 CBpt.next[RDX]
                                JMP             @@10

@@15:                   INC             U64 CTask.swap_counter[RSI]

                                MOV             RAX, U64 CTask.fpu_mmx[RSI]
                                FXRSTOR         U64 [RAX]

                                MOV             RAX, RSP
                                LEA             RSP, U64 CTask.rcx[RSI]
                                POP             RCX
                                POP             RDX
                                POP             RBX
                                POP             RBP
                                POP             RDI
                                POP             R8
                                POP             R9
                                POP             R10
                                POP             R11
                                POP             R12
                                POP             R13
                                POP             R14
                                POP             R15
                                MOV             RSP, RAX

                                MOV             RAX, U64 CTask.rax[RSI]
                                PUSH            CGDT.ds
                                PUSH            U64 CTask.rsp[RSI]
                                PUSH            U64 CTask.rflags[RSI]
                                PUSH            CGDT.cs64
                                PUSH            U64 CTask.rip[RSI]
                                MOV             RSI, U64 CTask.rsi[RSI]
                                IRET

//************************************
END_RSI_TASK:
                                MOV             RAX, RSI
                                CALL            SET_FS_BASE
_TASK_END_NOW::
                                CALL            &TaskEnd
                                MOV             RSI, RAX
                                CALL            SET_FS_BASE
                                JMP             I8 RESTORE_RSI_TASK

_YIELD::
                                PUSHFD
                                TEST            U8 [SYS_SEMAS + SEMA_SINGLE_USER * DEFAULT_CACHE_LINE_WIDTH], 1
                                JZ                      @@05
                                POPFD                                   //If single user, don't change task.
                                RET

@@05:                   CLI
                                CALL            TASK_CONTEXT_SAVE
                                MOV             EBX, U32 _RET
                                MOV             U64 CTask.rip[RSI], RBX
                                POP             U64 CTask.rflags[RSI]
                                MOV             U64 CTask.rsp[RSI], RSP
                                MOV             RSI, U64 CTask.next_task[RSI]

RESTORE_RSI_TASK:
                                TEST            U64 [SYS_CTRL_ALT_FLAGS], 1 << CTRL_ALT_DEL|1 << CTRL_ALT_TAB|1 << CTRL_ALT_X|1 << CTRL_ALT_C
                                JNZ             HANDLE_SYSF_KEY_EVENT

RESTORE_RSI_TASK2:
@@20:                   BT                      U64 CTask.task_flags[RSI], TASKf_KILL_TASK
                                JC                      END_RSI_TASK
                                TEST            U64 CTask.task_flags[RSI], 1 << TASKf_AWAITING_MESSAGE | 1 << TASKf_SUSPENDED
                                JNZ             @@25

                                MOV             RAX, U64 [&counts.jiffies]
                                CMP             U64 CTask.wake_jiffy[RSI], RAX
                                JG                      @@25            //Jmp if not ready, yet.

                                MOV             RAX, RSI
                                CALL            SET_FS_BASE
                                JMP             I32 _TASK_CONTEXT_RESTORE

@@25:                   MOV             RSI, U64 CTask.next_task[RSI]
                                XOR             RAX, RAX
                                CMP             U64 GS:CCPU.executive_task[RAX], RSI
                                JNE             @@20            //Jmp if not Executive
                                BTR             U32 GS:CCPU.cpu_flags[RAX], CPUf_RAN_A_TASK
                                JC                      @@20            //Jmp if had chance for IRQ already
                                MOV             RAX, U64 GS:CCPU.idle_task[RAX]
                                MOV             RSP, U64 CTask.stack[RAX]
                                ADD             RSP, MEM_DEFAULT_STACK + CTaskStack.stack_base  //Reset to top
                                CALL            SET_FS_BASE
                                STI             //Restore idle task so we can unmask IRQs.
                                HLT
SYS_IDLE_PT::
                                CLI

RESTORE_EXECUTIVE_TASK_IF_READY:
                                XOR             RAX, RAX
                                MOV             RSI, GS:CCPU.executive_task[RAX]
                                JMP             RESTORE_RSI_TASK

HANDLE_SYSF_KEY_EVENT:
                                MOV             RAX, RSI
                                CALL            SET_FS_BASE
                                XOR             RBX, RBX
                                MOV             RAX, GS:CCPU.num[RBX]
                                TEST            RAX, RAX
                                JNZ             I32 RESTORE_RSI_TASK2

                                MOV             EAX, U32 SYS_CTRL_ALT_FLAGS
                                LOCK
                                BTR             U32 [RAX], CTRL_ALT_DEL
                                JC                      I32 &Reboot

                                CMP             U64 GS:CCPU.idle_task[RBX], RSI
                                JE                      RESTORE_EXECUTIVE_TASK_IF_READY

                                LOCK
                                BTR             U32 [RAX], CTRL_ALT_TAB
                                JNC             @@05
                                CALL            &TaskFocusNext
                                JMP             I32 RESTORE_FS_TASK

@@05:                   LOCK
                                BTR             U32 [RAX], CTRL_ALT_X
                                JC                      END_FOCUS_USER
                                LOCK
                                BTR             U32 [RAX], CTRL_ALT_C
                                JNC             I32 RESTORE_RSI_TASK

BREAK_FOCUS_USER:
                                MOV             RSI, U64 [SYS_FOCUS_TASK]
                                TEST            RSI, RSI
                                JZ                      RESTORE_EXECUTIVE_TASK_IF_READY
                                BT                      U64 CTask.win_inhibit[RSI], WIf_SELF_FOCUS
                                JC                      I32 RESTORE_RSI_TASK
                                LOCK
                                BTR             U64 CTask.task_flags[RSI], TASKf_BREAK_LOCKED
                                JNC             @@10
                                LOCK
                                BTS             U64 CTask.task_flags[RSI], TASKf_PENDING_BREAK
                                JMP             I32 RESTORE_RSI_TASK

@@10:                   MOV             RAX, &Break
                                MOV             U64 CTask.rip[RSI], RAX
                                BT                      U64 CTask.task_flags[RSI], TASKf_BREAK_TO_SHIFT_ESC
                                JC                      I32 RESTORE_RSI_TASK

//Do these now, in case interrupt happens.
                                MOV             U64 CTask.wake_jiffy[RSI], 0
                                PUSH            RSI
                                CALL            &TaskResetAwaitingMessage
                                JMP             I32 RESTORE_RSI_TASK

END_FOCUS_USER:
                                MOV             RSI, U64 [SYS_FOCUS_TASK]
                                CALL            &TaskFocusNext
                                TEST            RSI, RSI
                                JZ                      I32 RESTORE_EXECUTIVE_TASK_IF_READY
                                MOV             RAX, RSI
                                CALL            SET_FS_BASE
                                BT                      U64 CTask.win_inhibit[RSI], WIf_SELF_FOCUS
                                JC                      I32 RESTORE_RSI_TASK
                                LOCK
                                BTS             U64 CTask.task_flags[RSI], TASKf_KILL_TASK
                                JMP             I32 END_RSI_TASK

RESTORE_FS_TASK:
                                XOR             RSI, RSI
                                MOV             RSI, FS:CTask.addr[RSI]
                                JMP             I32 RESTORE_RSI_TASK
}

_extern _TASK_CONTEXT_RESTORE   U0 TaskContextRestore(); //Restore a task context.
_extern _YIELD                                  U0 Yield();                              //Yield cpu to next task.
_extern _TASK_END_NOW                   U0 TaskEndNow();                 //Terminate current task.

U0 TaskQueueIns(CTask *task, CTask *pred=NULL)
{//Insert a task in the scheduler running task queue.
//You have no business with this, probably.
        CTask *last;

        PUSHFD
        CLI
        if (!pred)
                pred = Fs;
        last = pred->last_task;
        last->next_task = pred->last_task = task;
        task->last_task = last;
        task->next_task = pred;
        POPFD
}

U0 TaskQueueRemove(CTask *task)
{//Remove a task from the scheduler running task queue.
//Use Suspend().
        CTask *next, *last;

        PUSHFD
        CLI
        next = task->next_task;
        last = task->last_task;
        last->next_task = next;
        next->last_task = last;
        POPFD
}

U0 TaskQueueInsChild(CTask *task)
{
        CTask *last, *pred;

        PUSHFD
        CLI
        pred = task->parent_task->last_child_task;
        last = pred->last_sibling_task;
        last->next_sibling_task = pred->last_sibling_task = task;
        task->last_sibling_task = last;
        task->next_sibling_task = pred;
        POPFD
}