Tvùrce webu je i pro tebe! Postav tøeba web. Bez grafika. Bez kodéra. Hned.
wz

SCHEDULE.ASM

Scheduler


; =============================================================================
;
;                              Litos - Scheduler
;
; =============================================================================
; Run structures of tasks:
;    WakeUpList - tasks prepared for running, marked TASK_WAKEUP. WakeUpList
;                 is separate for each CPU. It is lockable.
;    RunQueue - running tasks, marked TASK_RUNNING. Each CPU has its own
;               RunQueue, it is not lockable. RunQueue contains 2 task queues,
;               current and expired. After running out of all time quantum
;               each task is moved from current queue into expired queue.
;               If current queue contains no task, queues are exchange and 
;               tasks start run with new time quantum.
;    SleepList - list of sleeping tasks (TASK_SLEEP state).
;    PauseList - list of paused tasks (TASK_PAUSE state).
;
; Task run-cycle:
;   - Only task can put itself into waiting state. It change its state from
;     TASK_RUNNING to TASK_FALLASLEEP and call scheduler.
;   - Scheduler moves fall-asleep task into sleep list.
;   - Task can be waked-up with an alarm (if its sleep time elapses) or with
;     another task (moving task into WakeUpList and marking TASK_WAKEUP)
;     or with sending signal to the task.
;   - Scheduler moves tasks from wake-up list into its run-queue. If task has
;     a non-blocked signal it wakes it up, too.
;
; All system timing is refered to system time (100-nanosecs from system start).
; =============================================================================

		CODE_SECTION	32

QUANTUM_MAX	EQU	150*TIME_1MS	; maximal time quantum in 100-ns
QUANTUM_MIN	EQU	2*TIME_1MS_REAL	; minimal time quantum in 100-ns

; -----------------------------------------------------------------------------
;                          Lock/unlock sleep list
; -----------------------------------------------------------------------------
; NOTES:	Use macro SLEEPLOCK to lock, SLEEPUNLOCK to unlock.
; -----------------------------------------------------------------------------

; ------------- Sleep lock function

%ifdef	SMP
		LOCK_LockFnc SleepLock,SleepListLock ; declare sleep lock
%endif

; ------------- Macro - call sleep lock function

%macro		SLEEPLOCK 0
%ifdef	SMP
		call	SleepLock	; call sleep lock function
%endif
%endmacro

; ------------- Macro - sleep unlock

%macro		SLEEPUNLOCK 0
%ifdef	SMP
		LOCK_Unlock SleepListLock ; unlock sleep list
%endif
%endmacro

; -----------------------------------------------------------------------------
;                          Lock/unlock pause list
; -----------------------------------------------------------------------------
; NOTES:	Use macro PAUSELOCK to lock, PAUSEUNLOCK to unlock.
; -----------------------------------------------------------------------------

; ------------- Pause lock function

%ifdef	SMP
		LOCK_LockFnc PauseLock,PauseListLock ; declare pause lock
%endif

; ------------- Macro - call pause lock function

%macro		PAUSELOCK 0
%ifdef	SMP
		call	PauseLock	; call pause lock function
%endif
%endmacro

; ------------- Macro - pause unlock

%macro		PAUSEUNLOCK 0
%ifdef	SMP
		LOCK_Unlock PauseListLock ; unlock pause list
%endif
%endmacro

; -----------------------------------------------------------------------------
;                          Initialize scheduler
; -----------------------------------------------------------------------------

; ------------- Allocate buffer for run-queues (528 to 16896 bytes)

SchedInit:	mov	eax,QUEUE_size*2 ; EAX <- size of task queue * 2
		mov	esi,[CPUNum]	; ESI <- number of CPUs
		mul	esi		; EAX <- size of buffer
		call	SysMemAlloc	; get system memory (no errors yet)
		xchg	eax,edi		; EDI <- pointer to queue buffer

; ------------- Initialize task queues

		mov	edx,edi		; EDX <- task queues
		lea	ebx,[esi*2]	; EBX <- number of queues * 2
		xor	ecx,ecx		; ECX <- 0
SchedInit2:	mov	[edx+QUEUE_Total],ecx ; clear total number of tasks
		mov	[edx+QUEUE_Mask],ecx ; clear mask of non-empty lists
		add	edx,byte QUEUE_List ; EDX <- lists of TASKs

; ------------- Initialize lists of TASKs (-> ECX=0)

		mov	cl,QUEUE_LISTS	; ECX <- number of task levels
SchedInit3:	LISTINIT edx		; initialize list head
		add	edx,byte LIST_size ; EDX <- next list head
		loop	SchedInit3	; initialize next list head
		dec	ebx		; counter of queues
		jnz	SchedInit2	; initialize next queue

; ------------- Prepare to initialize table of run-queues

		mov	edx,RunQueue	; EDX <- table of run-queues
		mov	eax,RunQueueAddr; EAX <- addresses of run-queue tables

; ------------- Address of run-queue

SchedInit4:	mov	[eax],edx	; store address of run-queue
		add	eax,byte 4	; increase address

; ------------- Current and expired task queue

		mov	[edx+RUNQ_Current],edi ; current task queue
		add	edi,QUEUE_size	; EDI <- next task queue
		mov	[edx+RUNQ_Expired],edi ; expired task queue
		add	edi,QUEUE_size	; EDI <- next task queue

; ------------- Other variables

		mov	[edx+RUNQ_NextSched],ecx ; reschedule request
		mov	[edx+RUNQ_Elapsed],ecx ; clear elapsed time
		mov	[edx+RUNQ_Total],ecx ; total tasks in run-queue

; ------------- Next run-queue

		add	edx,byte RUNQ_size ; EDX <- address of next run-queue
		dec	esi		; counter of CPUs
		jnz	SchedInit4	; initialize table for next CPU
		ret

; -----------------------------------------------------------------------------
;              Insert task into task queue (at the end of the list)
; -----------------------------------------------------------------------------
; INPUT:	EBX = task
;		ECX = index in the task list (0 to 31)
;		EDX = task queue
; -----------------------------------------------------------------------------

; ------------- Push registers

TaskEnqueue:	push	eax		; push EAX
		push	ecx		; push ECX
		push	edx		; push EDX

; ------------- Store pointer to new task queue

		mov	[ebx+TASK_TaskQueue],edx ; store pointer to task queue
		mov	[ebx+TASK_QueueLev],ecx ; current level in task queue

; ------------- Increase number of tasks

		inc	dword [edx+QUEUE_Total] ; increase number of tasks

; ------------- Set queue mask

		xor	eax,eax		; EAX <- 0
		inc	eax		; EAX <- 1
		shl	eax,cl		; EAX <- index mask
		or	[edx+QUEUE_Mask],eax ; set queue mask

; ------------- Store task into the list

		lea	eax,[edx+QUEUE_List+ecx*LIST_size] ; EAX <- list
		lea	ecx,[ebx+TASK_Queue] ; ECX <- task list entry
		LISTLAST eax, ecx, edx	; add task to the end of queue

; ------------- Pop registers

		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                       Delete task from task queue
; -----------------------------------------------------------------------------
; INPUT:	EBX = task
; -----------------------------------------------------------------------------

; ------------- Push registers

TaskDequeue:	push	eax		; push EAX
		push	ebx		; push EBX
		push	ecx		; push ECX
		push	edx		; push EDX

; ------------- Get task queue (-> EDX)

		mov	edx,[ebx+TASK_TaskQueue] ; EDX <- task queue
		mov	ecx,[ebx+TASK_QueueLev] ; ECX <- level in task queue

; ------------- Decrease number of tasks

		dec	dword [edx+QUEUE_Total] ; decrease number of tasks

; ------------- Detach task from the list

		lea	ebx,[ebx+TASK_Queue] ; EBX <- task list entry
		LISTDEL	ebx, eax, ebx	; detach task from the list

; ------------- Clear mask

		cmp	eax,ebx		; is list empty?
		jne	TaskDequeue2	; list is not empty
		xor	eax,eax		; EAX <- 0
		inc	eax		; EAX <- 1
		shl	eax,cl		; EAX <- index mask
		xor	[edx+QUEUE_Mask],eax ; clear queue mask

; ------------- Pop registers

TaskDequeue2:	pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	ebx		; pop EBX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                  Move task in task queue into another level
; -----------------------------------------------------------------------------
; INPUT:	EBX = task
;		ECX = new index in the task list (0 to 31)
; -----------------------------------------------------------------------------

; ------------- Push registers

TaskMovePrior:	push	eax		; push EAX
		push	edx		; push EDX

; ------------- Check if index changed

		mov	eax,[ebx+TASK_QueueLev] ; EAX <- level in task queue
		cmp	eax,ecx		; current index changed?
		je	TaskMovePrior4	; current index not changed

; ------------- Requeue task

		mov	edx,[ebx+TASK_TaskQueue] ; EDX <- task queue
		call	TaskDequeue	; detach task from the queue
		call	TaskEnqueue	; attach task into the queue

; ------------- Pop registers

TaskMovePrior4:	pop	edx		; pop EDX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;            Move task from current task queue into expired task queue
; -----------------------------------------------------------------------------
; INPUT:	EBX = task
;		ESI = run-queue
; -----------------------------------------------------------------------------

; ------------- Push registers

TaskMoveExp:	push	ecx			; push ECX
		push	edx			; push EDX

; ------------- Prepare new time quantum

		xchg	eax,ecx			; ECX <- push EAX
		call	TaskQuantum		; calculate time quantum
		xchg	eax,ecx			; EAX <- pop EAX,ECX <- quantum
		add	[ebx+TASK_Remaining],ecx ; new remaining time
		cmp	dword [ebx+TASK_Remaining],QUANTUM_MAX ; max. time
		jbe	TaskMoveExp1
		mov	dword [ebx+TASK_Remaining],QUANTUM_MAX ; limit time

; ------------- Decrement temporaly priority

TaskMoveExp1:	dec	byte [ebx+TASK_AddPrio]	; decrement temporary priority
		jns	TaskMoveExp2		; priority is OK
		inc	byte [ebx+TASK_AddPrio]	; return zero value

; ------------- Dequeue task from current task queue

TaskMoveExp2:	mov	ecx,[ebx+TASK_QueueLev] ; ECX <- level in task queue
		call	TaskDequeue		; detach task from the queue

; ------------- Enqueue task into expire task queue

		mov	edx,[esi+RUNQ_Expired]	; expired task queue
		call	TaskEnqueue		; enqueue task into the queue

; ------------- Pop registers

		pop	edx			; pop EDX
		pop	ecx			; pop ECX
		ret

; -----------------------------------------------------------------------------
;                   Calculate task priority for running task
; -----------------------------------------------------------------------------
; INPUT:	EBX = task
; OUTPUT:	EAX = priority
; -----------------------------------------------------------------------------

; ------------- Prepare bonus for running task (remaining time 2550000 max.)

TaskCalcPrio:	mov	eax,[ebx+TASK_Remaining] ; EAX <- remaining time
		sar	eax,16		; 1 level per 6 ms, max. 38
		jns	TaskCalcPrio2	; bonus is positive
		xor	eax,eax		; limit negative bonus
TaskCalcPrio2:	cmp	eax,byte 38	; maximal bonus
		jle	TaskCalcPrio3	; bonus is OK
		xor	eax,eax		; EAX <- 0
		mov	al,38		; limit bonus

; ------------- Add static priority (-> -31 to +120)

TaskCalcPrio3:	add	al,[ebx+TASK_AbsPrio] ; AL <- absolute priority
		add	al,[ebx+TASK_RelPrio] ; AL <- add relative priority
		add	al,[ebx+TASK_AddPrio] ; AL <- add temporaly increment

; ------------- Increase priority for task with user focus (-> max. +126)

		cmp	byte [ebx+TASK_Focus],0 ; has it user focus?
		je	TaskCalcPrio4	; it has not user focus
		add	al,6		; increase priority

; ------------- Limit maximal priority

TaskCalcPrio4:	cmp	al,[ebx+TASK_MaxPrio] ; maximal priority
		jle	TaskCalcPrio6	; priority is OK
		mov	al,[ebx+TASK_MaxPrio] ; limit maximal priority

; ------------- Limit minimal priority

TaskCalcPrio6:	cmp	al,PRIORITY_MIN	; minimal priority
		jge	TaskCalcPrio8	; priority is OK
		mov	al,PRIORITY_MIN	; limit minimal priority
TaskCalcPrio8:	ret

; -----------------------------------------------------------------------------
;              Calculate task time quantum (from current priority)
; -----------------------------------------------------------------------------
; INPUT:	EBX = task
;		ESI = run-queue (in which is the task placed)
; OUTPUT:	EAX = task time quantum
; -----------------------------------------------------------------------------
;    1 task:  32 to 150 ms, normal 124 ms
;    2 tasks: 16 to 118 ms, mormal  62 ms
;    5 tasks:  6 to  47 ms, mormal  24 ms
;   10 tasks:  3 to  24 ms, mormal  12 ms
;   20 tasks:  2 to  12 ms, mormal   6 ms
;   50 tasks:  2 to   5 ms, mormal   3 ms
; -----------------------------------------------------------------------------

; ------------- Push registers

TaskQuantum:	push	ecx		; push ECX
		push	edx		; push EDX

; ------------- Basic time quantum based on the task priority (-> 2M to 18M)

		movzx	eax,byte [ebx+TASK_Priority] ; EAX <- current priority
		mov	cl,al		; CL <- store priority
		add	al,5		; EAX <- basic quantum
		shl	eax,16		; EAX <- quantum << 16

; ------------- Real-time tasks can have maximal time quantums

		cmp	cl,PRIORITY_MAXUSR ; is it real-time task?
		ja	TaskQuantum2	; it is real-time task, use max.quantum

; ------------- Decrease time quantum with number of running tasks

		mov	ecx,[esi+RUNQ_Total] ; ECX <- number of running tasks
		xor	edx,edx		; EDX <- 0
		div	ecx		; EAX <- time quantum

; ------------- Limit minimal and maximal time quantum

TaskQuantum2:	cmp	eax,QUANTUM_MIN	; check minimal time quantum
		jae	TaskQuantum4	; time quantum is OK
		mov	eax,QUANTUM_MIN	; limit minimal time quantum
TaskQuantum4:	cmp	eax,QUANTUM_MAX	; check maximal time quantum
		jbe	TaskQuantum6	; time quantum is OK
		mov	eax,QUANTUM_MAX	; limit maximal time quantum

; ------------- Pop registers

TaskQuantum6:	pop	edx		; pop EDX
		pop	ecx		; pop ECX
		ret

; -----------------------------------------------------------------------------
;                         Store task into sleep list
; -----------------------------------------------------------------------------
; INPUT:	EBX = task (in fall asleep state, its sleep time must be set)
; NOTES:	Task must not be in any task list.
; -----------------------------------------------------------------------------

; ------------- Push registers

TaskGoWait:	push	eax		; push EAX
		push	ebx		; push EBX
		push	ecx		; push ECX

; ------------- Disable interrupts

		pushf			; push flags
		cli			; disable interrupts

; ------------- Lock sleep list

		SLEEPLOCK		; lock sleep list

; ------------- Set state to SLEEP

		mov	byte [ebx+TASK_State],TASK_SLEEP ; set sleep state

; ------------- Store task into sleep list

		mov	eax,SleepList	; EAX <- sleep list
		add	ebx,TASK_Queue	; EBX <- link
		LISTADD eax, ebx, ecx	; add task after found entry

; ------------- Start alarm

		add	ebx,byte TASK_Alarm-TASK_Queue ; EBX <- alarm
		call	AlarmStart	; start alarm

; ------------- Unlock sleep list

		SLEEPUNLOCK		; unlock sleep list

; ------------- Enable interrupts

		popf			; pop flags

; ------------- Pop registers

		pop	ecx		; pop ECX
		pop	ebx		; pop EBX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                        Store task into wake-up list
; -----------------------------------------------------------------------------
; INPUT:	EBX = task
; NOTES:	Task must not be in any task list.
; -----------------------------------------------------------------------------

; ------------- Push registers

TaskGoWakeUp:	pusha			; push all registers

; ------------- Mark task as waking-up

		mov	byte [ebx+TASK_State],TASK_WAKEUP ; task is waking up

; ------------- Find CPU with lowest overhead (-> ESI)

		mov	edx,RunQueue	; EDX <- run-queue table
		xor	eax,eax		; EAX <- 0
		dec	eax		; EAX <- -1, initial number of tasks
		xor	ecx,ecx		; ECX <- 0, first CPU index
TaskGoWakeUp2:	cmp	eax,[edx+RUNQ_Total] ; check number of tasks
		jbe	TaskGoWakeUp4	; it is not better CPU
		mov	eax,[edx+RUNQ_Total] ; EAX <- new number of tasks
		mov	esi,ecx		; ESI <- index of best CPU
TaskGoWakeUp4:	add	edx,byte RUNQ_size ; EDX <- new run-queue
		inc	ecx		; increase CPU number
		cmp	ecx,[CPUNum]	; all CPU?
		jb	TaskGoWakeUp2	; test next CPU

; ------------- Prefer old CPU

		movzx	ecx,byte [ebx+TASK_CPU] ; ECX <- old CPU
		mov	edx,[RunQueueAddr+ecx*4] ; EDX <- run-queue of old CPU
		cmp	eax,[edx+RUNQ_Total] ; has this CPU equal tasks?
		jne	TaskGoWakeUp6	; minimal CPU is better
		mov	esi,ecx		; ESI <- use old CPU

; ------------- Lock wake-up list

TaskGoWakeUp6:	pushf			; push flags
		cli			; disable interrupts
%ifdef SMP
		lea	edx,[WakeUpLock+esi*SPINLOCK_size] ; EDX <- lock
		call	SpinLockEDX	; lock wake-up list
%endif
; ------------- Store task into wake-up list

		lea	eax,[WakeUpList+esi*LIST_size] ; EAX <- wake-up list
		add	ebx,TASK_Queue	; EBX <- task queue
		LISTLAST eax,ebx,ecx	; store task into list

; ------------- Reschedule run-queue request

		mov	eax,[RunQueueAddr+esi*4] ; EAX <- run-queue
		RESCHEDULE eax		; schedule request

; ------------- Unlock wake-up list
%ifdef SMP
		LOCK_Unlock edx		; unlock wake-up list
%endif
		popf			; pop flags (and enable interrupts)

; ------------- Pop registers

		popa			; pop all registers
		ret

; -----------------------------------------------------------------------------
;                       Sleep callback function
; -----------------------------------------------------------------------------
; INPUT:	ECX = task
;		EBX = alarm structure
; -----------------------------------------------------------------------------

SleepWakeUp:	and	dword [ebx+ALARM_Expire],byte 0 ; mark as expired
		and	dword [ebx+ALARM_Expire+4],byte 0
		mov	ebx,ecx		; EBX <- task

; WakeUpTask must follow.

; -----------------------------------------------------------------------------
;            Wake up task, if it is sleeping (add task to wake-up list)
; -----------------------------------------------------------------------------
; INPUT:	EBX = task (can be NULL)
; -----------------------------------------------------------------------------

; ------------- Quick test if task is sleeping

WakeUpTask:	or	ebx,ebx		; is it NULL?
		jz	short WakeUpTask9 ; invalid task
		cmp	byte [ebx+TASK_State],TASK_SLEEP ; is task sleeping?
		jne	short WakeUpTask9 ; task is not sleeping

; ------------- Push registers

		push	eax		; push EAX
		push	ecx		; push ECX
		push	edx		; push EDX

; ------------- Disable interrupts

		pushf			; push flags
		cli			; disable interrupts

; ------------- Lock sleep list

		SLEEPLOCK		; lock sleep list

; ------------- Check if task is still sleeping

		cmp	byte [ebx+TASK_State],TASK_SLEEP ; is task sleeping?
		je	WakeUpTask2	; task is still sleeping

; ------------- Someone woke it up, unlock sleep list and exit

		SLEEPUNLOCK		; unlock sleep list
		popf			; pop flags (and enable interrupts)
		jmp	short WakeUpTask8 ; exit

; ------------- Mark task as waking-up

WakeUpTask2:	mov	byte [ebx+TASK_State],TASK_WAKEUP ; task is waking up

; ------------- Remove task from the sleep list

		lea	eax,[ebx+TASK_Queue] ; EAX <- link to sleep list
		LISTDEL	eax,ecx,edx	; remove task from the sleep list

; ------------- Stop alarm (if it is running)

		push	ebx		; push EBX
		add	ebx,TASK_Alarm	; EBX <- alarm
		call	AlarmStop	; stop alarm
		pop	ebx		; pop EBX

; ------------- Unlock sleep list

		SLEEPUNLOCK		; unlock sleep list

; ------------- Enable interrupts

		popf			; pop flags (and enable interrupts)

; ------------- Store task (in EBX) into wake-up list

		call	TaskGoWakeUp	; store task into wake-up list

; ------------- Pop registers

WakeUpTask8:	pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	eax		; pop EAX
WakeUpTask9:	ret

; -----------------------------------------------------------------------------
;                 Run tasks from wake-up list (for current CPU)
; -----------------------------------------------------------------------------

; ------------- Push registers

RunWakeUp:	pusha			; push all registers

; ------------- Disable interrupts

		pushf			; push flags
		cli			; disable interrupts

; ------------- Get current CPU (-> ECX)

		CURRENT	ecx		; ECX <- get current task
		movzx	ecx,byte [ecx+TASK_CPU] ; ECX <- current CPU

; ------------- Get run-queue (-> ESI)

		mov	esi,[RunQueueAddr+ecx*4] ; ESI <- run-queue

; ------------- Lock wake-up list (-> EDX)
%ifdef SMP
		lea	edx,[WakeUpLock+ecx*SPINLOCK_size] ; EDX <- lock
		LOCK_Lock edx		; lock wake-up list
%endif
; ------------- Get next task from wake-up list

		lea	edi,[WakeUpList+ecx*LIST_size] ; EDI <- wake-up list
RunWakeUp2:	mov	ebx,[edi+LIST_Next] ; EBX <- next task
		cmp	ebx,edi		; is wake-up list empty?
		je	RunWakeUp8	; wake-up list is empty
		LISTDEL	ebx,ecx,eax	; remove task from the list

; ------------- Mark task as running

		sub	ebx,TASK_Queue	; EBX <- task
		mov	byte [ebx+TASK_State],TASK_RUNNING ; mark as running

; ------------- Calculate task priority

		call	TaskCalcPrio	; calculate task priority
		push	eax		; push EAX (task priority)
		mov	[ebx+TASK_Priority],al ; store current priority

; ------------- Set run-queue and current CPU

		mov	[ebx+TASK_RunQueue],esi ; set current run-queue
		CURRENT	ecx		; ECX <- get current task
		mov	cl,[ecx+TASK_CPU] ; CL <- current CPU
		mov	[ebx+TASK_CPU],cl ; set current CPU

; ------------- Insert task into run-queue

		pop	ecx		; pop ECX (task priority)
		push	edx		; push EDX
		mov	edx,[esi+RUNQ_Current] ; EDX <- current task queue
		call	TaskEnqueue	; insert task into run-queue
		pop	edx		; pop EDX
		inc	dword [esi+RUNQ_Total] ; increase number of tasks
		jmp	short RunWakeUp2 ; next task

; ------------- Unlock wake-up list

RunWakeUp8:
%ifdef SMP
		LOCK_Unlock edx		; unlock wake-up list
%endif
; ------------- Enable interrupts

		popf			; pop flags

; ------------- Pop registers

		popa			; pop all registers
		ret

; -----------------------------------------------------------------------------
;                   Store current running task into pause-list
; -----------------------------------------------------------------------------

; ------------- Push registers

TaskStop:	push	eax		; push EAX
		push	ebx		; push EBX
		push	edx		; push EDX

; ------------- Get current task (-> EBX)

		CURRENT	ebx		; EBX <- get current task

; ------------- Disable interrupts

		pushf			; push flags
		cli			; disable interrupts

; ------------- Detach task from the running queue

		fnsave	[ebx+TASK_FPU]	; save FPU state
		call	TaskDequeue	; detach task from the queue
		mov	edx,[ebx+TASK_RunQueue] ; EDX <- run-queue
		dec	dword [edx+RUNQ_Total] ; decrease current tasks

; ------------- Lock pause-queue

		PAUSELOCK		; lock pause-queue

; ------------- Set state to PAUSE and store task into the list

		lea	eax,[ebx+TASK_Queue] ; EAX <- link
		mov	byte [eax+TASK_State-TASK_Queue],TASK_PAUSE ; set pause
		mov	ebx,PauseList	; EBX <- pause list
		call	ListAdd		; add task into the list

; ------------- Unlock wait-queue

		PAUSEUNLOCK		; unlock pause-queue

; ------------- Enable interrupts

		popf			; pop flags

; ------------- Pop registers

		pop	edx		; pop EDX
		pop	ebx		; pop EBX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;            Wake up task from pause-list (add task to wake-up list)
; -----------------------------------------------------------------------------
; INPUT:	EBX = task
; NOTES:	It checks if the task is in the pause-list.
; -----------------------------------------------------------------------------

; ------------- Quick test if task is paused

TaskContinue:	cmp	byte [ebx+TASK_State],TASK_PAUSE ; is task paused?
		jne	TaskContinue9	; task is not paused

; ------------- Push registers

		push	eax		; push EAX

; ------------- Disable interrupts

		pushf			; push flags
		cli			; disable interrupts

; ------------- Lock pause-queue

		PAUSELOCK		; lock pause-queue

; ------------- Check if task is still paused

		cmp	byte [ebx+TASK_State],TASK_PAUSE ; is task paused?
		je	TaskContinue2	; task is still paused

; ------------- Someone woke it up, unlock pause-queue and exit

		PAUSEUNLOCK		; unlock wait-queue
		popf			; pop flags (and enable interrupts)
		jmp	short TaskContinue8 ; exit

; ------------- Mark task as waking-up

TaskContinue2:	mov	byte [ebx+TASK_State],TASK_WAKEUP ; task is waking up

; ------------- Remove task from the pause-queue

		lea	eax,[ebx+TASK_Queue] ; EAX <- link to pause-queue
		call	ListDel		; remove task from the pause-queue

; ------------- Unlock wait-queue

		PAUSEUNLOCK		; unlock wait-queue

; ------------- Enable interrupts

		popf			; pop flags (and enable interrupts)

; ------------- Store task (in EBX) into wake-up list

		call	TaskGoWakeUp	; store task into wake-up list

; ------------- Pop registers

TaskContinue8:	pop	eax		; pop EAX
TaskContinue9:	ret

; -----------------------------------------------------------------------------
;            Prepare to fall asleep current task (task must be running)
; -----------------------------------------------------------------------------
; INPUT:	EDX:EAX = length of sleep (in 100-nanosec), negative=infinitely
; -----------------------------------------------------------------------------

; ------------- Push registers

PrepSleepTask:	push	eax		; push EAX
		push	ebx		; push EBX
		push	ecx		; push ECX
		push	edx		; push EDX

; ------------- Calculate time of end of sleep (-> EDX:EAX)

		or	edx,edx		; is it infinity?
		js	PrepSleepTask2	; wait infinitely
		xchg	eax,ebx		; EBX <- required time LOW
		mov	ecx,edx		; ECX <- required time HIGH
		call	GetSystemTime	; get system time
		add	eax,ebx		; EAX <- end of sleep LOW
		adc	edx,ecx		; EDX <- end of sleep HIGH

; ------------- Get current task (-> EBX)

PrepSleepTask2:	CURRENT	ebx		; EBX <- get current task

; ------------- Store time of end of sleep

		mov	[ebx+TASK_Alarm+ALARM_Expire],eax ; end of sleep LOW
		mov	[ebx+TASK_Alarm+ALARM_Expire+4],edx ; end of sleep HIGH

; ------------- Clear lock-semaphore pointer

;		and	dword [ebx+TASK_LockSem],byte 0 ; clear lock-semaphore

; ------------- Mark task as falling asleep

		mov	byte [ebx+TASK_State],TASK_FALLASLEEP ; fall asleep

; ------------- Pop registers

		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	ebx		; pop EBX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                   Fall asleep current task (task must be running)
; -----------------------------------------------------------------------------
; INPUT:	EDX:EAX = length of sleep (in 100-nanosec), negative=infinitely
; -----------------------------------------------------------------------------

; ------------- Prepare fall asleep task

SleepTask:	call	PrepSleepTask	; prepare to fall asleep task

; ------------- Call scheduler (and switch to another task)

		jmp	Schedule	; call scheduler

; -----------------------------------------------------------------------------
;                       Preparre to continue task sleep
; -----------------------------------------------------------------------------
; NOTES:	Time of end of sleep must be set.
; -----------------------------------------------------------------------------

; ------------- Push registers

PrepSleepCont:	push	ebx		; push EBX

; ------------- Get current task (-> EBX)

		CURRENT	ebx		; EBX <- get current task

; ------------- Mark task as falling asleep

		mov	byte [ebx+TASK_State],TASK_FALLASLEEP ; fall asleep

; ------------- Pop registers

		pop	ebx		; pop EBX
		ret

; -----------------------------------------------------------------------------
;               Continue task sleep (e.i. after interrupt by signal)
; -----------------------------------------------------------------------------
; NOTES:	Time of end of sleep must be set.
; -----------------------------------------------------------------------------

; ------------- Prepare to continue task sleep

SleepCont:	call	PrepSleepCont	; prepare to continue task sleep

; ------------- Call scheduler (and switch to another task)

		jmp	Schedule	; call scheduler

; -----------------------------------------------------------------------------
;                        Sleep current task for a time
; -----------------------------------------------------------------------------
; INPUT:	EAX = length of sleep in [ms]
; -----------------------------------------------------------------------------

; ------------- Push registers

Sleep:		push	eax		; push EAX
		push	edx		; push EDX

; ------------- Recalculate time to 100-ns

		mov	edx,TIME_1MS	; EDX <- time 1 ms
		mul	edx		; recalc time to 100-ns

; ------------- Fall asleep

		call	SleepTask	; fall asleep with required time

; ------------- Check if time expired

Sleep2:         CURRENT	edx		; EDX <- get current task
		xor	eax,eax		; EAX <- 0
		cmp	eax,[edx+TASK_Alarm+ALARM_Expire]; check sleep time LOW
		jne	Sleep4		; time didn't expire
		cmp	eax,[edx+TASK_Alarm+ALARM_Expire+4] ; sleep time HIGH
		je	Sleep8		; sleep time expired

; ------------- Continue sleep (due to signal came)

Sleep4:		call	SleepCont	; continue sleep
		jmp	short Sleep2	; next test

; ------------- Pop regigters

Sleep8:		pop	edx		; pop EDX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                      Sleep current task for a short time
; -----------------------------------------------------------------------------
; INPUT:	AL = length of sleep in [ms]
; -----------------------------------------------------------------------------

SleepShort:	push	eax		; push EAX
		movzx	eax,al		; EAX <- sleep time
		call	Sleep		; sleep
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;               Sleep current task for an interruptible time
; -----------------------------------------------------------------------------
; INPUT:	EAX = length of sleep in [ms]
; OUTPUT:	EAX = remaining length of sleep in [ms], 0 = time expired (CY)
;		CY = time expired (EAX = 0)
; NOTES: Other process can interrupt sleeping with WakeUpTask.
;        To continue in non-expired sleep use SleepContInt.
; -----------------------------------------------------------------------------

; ------------- Push registers

SleepInt:	push	edx		; push EDX

; ------------- Recalculate time to 100-ns

		mov	edx,TIME_1MS	; EDX <- time 1 ms
		mul	edx		; recalc time to 100-ns

; ------------- Fall asleep

		call	SleepTask	; fall asleep with required time

; ------------- Recalculate remaining time to [ms]

SleepInt4:	push	ebx		; push EBX
	        CURRENT	ebx		; EBX <- get current task
		mov	eax,[ebx+TASK_Alarm+ALARM_Expire]; get sleep time LOW
		mov	edx,[ebx+TASK_Alarm+ALARM_Expire+4] ; sleep time HIGH
		mov	ebx,TIME_1MS	; EBX <- time 1 ms
		div	ebx		; EAX <- remaining time
		pop	ebx		; pop EBX
		cmp	eax,byte 1	; check if any time rests (CY=timeout)

; ------------- Pop regigters

		pop	edx		; pop EDX
		ret

; -----------------------------------------------------------------------------
;                      Continue task sleep interruptible
; -----------------------------------------------------------------------------
; OUTPUT:	EAX = remaining length of sleep in [ms], 0 = time expired (CY)
;		CY = time expired (EAX = 0)
; NOTES: Time of end of sleep must be set.
;	 Other process can interrupt sleeping with WakeUpTask.
; -----------------------------------------------------------------------------

; ------------- Push registers

SleepIntCont:	push	edx		; push EDX

; ------------- Get current task (-> EDX)

		CURRENT	edx		; EDX <- get current task

; ------------- Mark task as falling asleep

		mov	byte [edx+TASK_State],TASK_FALLASLEEP ; fall asleep

; ------------- Call scheduler (and switch to another task)

		call	Schedule	; call scheduler
		jmp	short SleepInt4

; -----------------------------------------------------------------------------
;                             NMI service
; -----------------------------------------------------------------------------
; INPUT:	EAX = elapsed time since last service (in 100-ns)
;		EBX = current task
;		ESI = current run-queue
; -----------------------------------------------------------------------------

; ------------- Signal memory error

DoNMI:		btr	dword [esi+RUNQ_Flags],0 ; signal memory error?
		jnc	DoNMI2		; memory error not signaled

; !!! TODO display message

; ------------- Signal I/O error

DoNMI2:		btr	dword [esi+RUNQ_Flags],1 ; signal I/O error?
		jnc	DoNMI3		; I/O error not signaled

; !!! TODO display message

; ------------- Signal watchdog

DoNMI3:		btr	dword [esi+RUNQ_Flags],2 ; signal watchdog?
		jnc	DoNMI5		; I/O error not signaled
		test	byte [NMIWatchdog],B0 ; use watchdog?
		jz	DoNMI4		; don't use watchdog

; !!! TODO serve watchdog

; ------------- Other error

DoNMI4:

; !!! TODO display message

; ------------- Count before reenabling memory and I/O check

DoNMI5:		test	byte [esi+RUNQ_Flags],B3 ; memory,I/O check disabled?
		jz	DoNMI6		; check is not disabled
		sub	dword [esi+RUNQ_CountMem],eax ; count time
		jns	DoNMI6		; time is not elapsed yet

; ------------- Reenable memory and I/O check

		and	byte [esi+RUNQ_Flags],~B3 ; memory,I/O check enabled
		push	eax		; push EAX
		in	al,61h		; AL <- get current state of port
		and	al,B0+B1	; clear reserved bits, enable check
		out	61h,al		; enable memory and I/O check
		pop	eax		; pop EAX
		
; ------------- Count before reenabling watchdog check

DoNMI6:		test	byte [esi+RUNQ_Flags],B4 ; watchdog check disabled?
		jz	DoNMI7		; check is not disabled
		sub	dword [esi+RUNQ_CountDog],eax ; count time
		jns	DoNMI7		; time is not elapsed yet
		and	byte [esi+RUNQ_Flags],~B4 ; watchdog check enabled

; ------------- Pop registers

DoNMI7:		ret

; -----------------------------------------------------------------------------
;                              Scheduler
; -----------------------------------------------------------------------------
; Stack of Scheduler is hardcoded in TaskClone.
;   [ESP+6*4]: return
;   [ESP+5*4]: eax
;   [ESP+4*4]: ebx
;   [ESP+3*4]: ecx
;   [ESP+2*4]: edx
;   [ESP+1*4]: esi
;   [ESP+0*4]: flags

; ------------- Push registers

Schedule:	push	eax		; push EAX
		push	ebx		; push EBX
		push	ecx		; push ECX
		push	edx		; push EDX
		push	esi		; push ESI

; ------------- Get current task (-> EBX)

Schedule1:	CURRENT	ebx		; EBX <- get current task

; ------------- Run slow interrupt handlers

		call	IRQRunSlow	; run slow interrupt handlers

; ------------- Run tasks from wake-up list

		call	RunWakeUp	; run tasks from wake-up list

; ------------- Disable interrupts

		pushf			; push flags
		cli			; disable interrupts

; ------------- Get current run-queue (-> ESI)

		mov	esi,[ebx+TASK_RunQueue] ; ESI <- run-queue

; ------------- Unmark request to scheduling

		xor	eax,eax		; EAX <- 0
		inc	eax		; EAX <- 1
		mov	[esi+RUNQ_NextSched],eax ; remove scheduling request

; ------------- Invalid task state (task is locked)

;		cmp	byte [ebx+TASK_State],TASK_LOCKED ; is task locked?
;		je	Schedule12	; task is locked

; ------------- Fall asleep current task (idle task cannot sleep)

		cmp	byte [ebx+TASK_State],TASK_FALLASLEEP ; fall asleep?
		jne	Schedule13	; task is running
		cmp	ebx,[esi+RUNQ_Idle] ; is it idle task?
		jne	Schedule11	; it is not idle task
		mov	byte [ebx+TASK_State],TASK_RUNNING ; continue running
		jmp	short Schedule13 ; sleeping of idle task is not allowed

; ------------- Remove task from run-queue

Schedule11:	fnsave	[ebx+TASK_FPU]	; save FPU state
		call	TaskDequeue	; detach task from the queue
		dec	dword [esi+RUNQ_Total] ; decrease current tasks

; ------------- Store task into wait-queue

		call	TaskGoWait	; store task into wait-queue
Schedule12:	xor	ebx,ebx		; EBX <- 0, task is invalid
		jmp	short Schedule16 ; expire queue test

; ------------- Signals

Schedule13:	call	DoSignal	; service signals
		jc	Schedule12	; task killed

; ------------- Get elapsed time since last service

		xor	eax,eax		; EAX <- new elapsed time
		xchg	eax,[esi+RUNQ_Elapsed] ; EAX <- elapsed time

; ------------- Shift task statistik

		add	[ebx+TASK_RunTime],eax ; shift running time of task
		adc	dword [ebx+TASK_RunTime+4],byte 0 ; carry

; ------------- Serve NMI

		test	byte [esi+RUNQ_Flags],B0+B1+B2+B3+B4 ; any event?
		jz	Schedule15	; no event
		call	DoNMI		; NMI service

; ------------- Count remaining time of current task

Schedule15:	sub	[ebx+TASK_Remaining],eax ; decrease remaining time
		jg	Schedule2	; remaining time is OK
		call	TaskMoveExp	; move task into expire array
Schedule16:	mov	eax,[esi+RUNQ_Current] ; EAX <- current queue
		cmp	dword [eax+QUEUE_Mask],byte 0 ; any other task?
		jne	Schedule3	; there is any other task
		xchg	eax,[esi+RUNQ_Expired] ; exchange with expired queue
		mov	[esi+RUNQ_Current],eax ; new current queue

; ------------- Expire idle task if there are any other tasks

		cmp	dword [esi+RUNQ_Total],byte 1 ; more than 1 task?
		je	Schedule3	; there is only idle task
		push	ebx		; push current task
		mov	ebx,[esi+RUNQ_Idle] ; EBX <- idle task
		call	TaskMoveExp	; move task into expire array
		pop	ebx		; pop current task
		jmp	short Schedule3

; ------------- Change priority of the task

Schedule2:	call	TaskCalcPrio	; recalc new priority
		xchg	eax,ecx		; ECX <- new priority
		call	TaskMovePrior	; change priority of the task
		mov	[ebx+TASK_Priority],cl	; store new priority

; ------------- Get new most priority task (-> EDX)

Schedule3:	mov	edx,[esi+RUNQ_Current] ; EDX <- current run-queue
		mov	ecx,[edx+QUEUE_Mask] ; ECX <- mask
		bsr	ecx,ecx		; find highest priority
		mov	edx,[edx+QUEUE_List+ecx*LIST_size+LIST_Next]
		sub	edx,TASK_Queue	; EDX <- new task

; ------------- Check if task changed

		cmp	edx,ebx		; task changed?
		je	Schedule5	; task not changed

; ------------- If old task has low remaining time, move it to expired queue

		or	ebx,ebx		; is task valid?
		jz	Schedule42	; task is not valid
		cmp	dword [ebx+TASK_Remaining],5*TIME_1MS ; low time?
		jae	Schedule4	; enough time remains
		cmp	byte [ebx+TASK_State],TASK_RUNNING ; is it valid task?
		jne	Schedule4	; task went sleep
		mov	eax,[ebx+TASK_TaskQueue] ; EAX <- task queue
		cmp	eax,[esi+RUNQ_Current] ; is it in current queue?
		jne	Schedule4	; it is already expired
		call	TaskMoveExp	; move task into expire array

; ------------- Save FPU state

Schedule4:	fnsave	[ebx+TASK_FPU]	; save FPU state

; ------------- Switch task (it does not realy jump, it only sets TSS register)

Schedule42:	jmp	far [edx+TASK_TR-4] ; switch to next task
ScheduleRet:	clts			; clear task-switched flag

; ------------- Get current task (-> EBX)

		CURRENT	ebx		; EBX <- get current task

; ------------- Get current run-queue (-> ESI)

		mov	esi,[ebx+TASK_RunQueue] ; ESI <- run-queue

; ------------- Restore FPU state

		frstor	[ebx+TASK_FPU]	; restore FPU state

; ------------- Set counter to next scheduling

Schedule5:	mov	ecx,[ebx+TASK_Remaining] ; ECX <- remaining time
		xchg	ecx,[esi+RUNQ_NextSched] ; set scheduling counter
		or	ecx,ecx		; any scheduling request?
		jnz	Schedule9
		popf
		jmp	Schedule1	; next schedule

; ------------- Enable interrupts

Schedule9:	popf			; enable interrupts

; ------------- Pop registers

		pop	esi		; pop ESI
		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	ebx		; pop EBX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                                   Data
; -----------------------------------------------------------------------------

		DATA_SECTION

; ------------- Lists of wake-up tasks

		align	4, db 0
WakeUpList:	
		%rep	CPU_MAX		; wake-up lists
		LISTHEAD
		%endrep
%ifdef SMP
		align	4, db 0
WakeUpLock:	
		%rep	CPU_MAX		; wake-up locks
		SPINLOCK
		%endrep
%endif
; ------------- List of sleeping tasks
		align	4, db 0
SleepList:	LISTHEAD		; list of sleeping tasks
%ifdef SMP
SleepListLock:	SPINLOCK		; lock of sleeping tasks
%endif
; ------------- List of paused tasks

		align	4, db 0
PauseList:	LISTHEAD		; list of paused tasks
%ifdef SMP
PauseListLock:	SPINLOCK		; lock of paused tasks
%endif
; -----------------------------------------------------------------------------
;                            Uninitialized data
; -----------------------------------------------------------------------------

		BSS_SECTION

; ------------- Table of run-queues (one run-queue per CPU)

		align	4, resb 1
RunQueue:	resb	RUNQ_size*CPU_MAX ; table of run-queues
RunQueueAddr:	resd	CPU_MAX		; addresses of run-queue of CPUs

Back to source browser