/*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 registers (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 }