fox32os/kernel/task.asm

289 lines
5.5 KiB
NASM

; task switching routines
; add a new task to the queue and jump to it immediately
; inputs:
; r0: task ID
; r1: initial instruction pointer
; r2: initial stack pointer (remember that the stack grows down!)
; r3: pointer to code block to free when task ends, or zero for none
; r4: pointer to stack block to free when task ends, or zero for none
; outputs:
; none
new_task:
; mark this task ID as used
bse [task_id_bitmap], r0
mov r6, r4 ; stack block pointer
mov r5, r3 ; code block pointer
mov r4, r2 ; stack pointer
mov r3, r1 ; instruction pointer
mov r2, r0 ; task ID
; add to the queue
mov r0, [task_queue_ptr]
call task_store
mov [task_queue_ptr], r0
jmp save_state_and_yield_task
; switch to the next task in the queue
; no registers are saved upon task yield
; if a register must be saved across a yield, push it before the yield and pop it after the yield
; inputs:
; none
; outputs:
; none
yield_task:
; add the current task back into the queue
mov r0, current_task ; get the current task struct
call task_load
pop r3 ; pop the return address off of the stack
mov r4, rsp
mov r0, [task_queue_ptr]
call task_store
mov [task_queue_ptr], r0
jmp yield_task_0
; push all registers and switch to the next task in the queue
; inputs:
; none
; outputs:
; none
save_state_and_yield_task:
push rfp
push r31
push r30
push r29
push r28
push r27
push r26
push r25
push r24
push r23
push r22
push r21
push r20
push r19
push r18
push r17
push r16
push r15
push r14
push r13
push r12
push r11
push r10
push r9
push r8
push r7
push r6
push r5
push r4
push r3
push r2
push r1
push r0
call yield_task
pop r0
pop r1
pop r2
pop r3
pop r4
pop r5
pop r6
pop r7
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15
pop r16
pop r17
pop r18
pop r19
pop r20
pop r21
pop r22
pop r23
pop r24
pop r25
pop r26
pop r27
pop r28
pop r29
pop r30
pop r31
pop rfp
ret
; yield for at least the specified number of ms
; inputs:
; r0: minimum ms to yield for
; outputs:
; none
sleep_task:
push r0
push r1
in r1, 0x80000706
add r0, r1
sleep_task_loop:
call save_state_and_yield_task
in r1, 0x80000706
cmp r1, r0
iflt jmp sleep_task_loop
pop r1
pop r0
ret
; switch to the next task without adding the current task back into the queue
; this will automatically free the task's code and stack blocks
; inputs:
; none
; outputs:
; none
end_current_task:
mov r0, current_task ; get the current task struct
call task_load
bcl [task_id_bitmap], r2 ; mark this task ID as unused
mov r0, r5 ; code block pointer
cmp r0, 0
ifnz call free_memory
mov r0, r6 ; stack block pointer
cmp r0, 0
ifnz call free_memory
end_current_task_no_mark_no_free:
pop r0 ; pop the return address off of the stack
cmp [task_queue_ptr], task_queue_bottom
ifz jmp task_empty
yield_task_0:
mov r0, task_queue_bottom
call task_load
mov r0, current_task
call task_store
mov r1, task_queue_bottom
yield_task_1:
add r1, TASK_SIZE
cmp [task_queue_ptr], r1
ifz jmp yield_task_2
mov r0, r1
call task_load
mov r0, r1
sub r0, TASK_SIZE
call task_store
jmp yield_task_1
yield_task_2:
mov r0, current_task
call task_load
sub [task_queue_ptr], TASK_SIZE
mov rsp, r4
jmp r3
; get the next unused task ID, starting at 1
; inputs:
; none
; outputs:
; r0: task ID, or zero if all IDs are used
get_unused_task_id:
mov r0, 1
get_unused_task_id_loop:
bts [task_id_bitmap], r0
ifz ret
inc r0
cmp r0, 32
iflt jmp get_unused_task_id_loop
; if we reach this point, then all task IDs are used
mov r0, 0
ret
; get the task ID of the currently running task
; inputs:
; none
; outputs:
; r0: task ID
get_current_task_id:
push r2
push r3
push r4
push r5
push r6
mov r0, current_task
call task_load
mov r0, r2
pop r6
pop r5
pop r4
pop r3
pop r2
ret
; check if a task ID is used
; inputs:
; r0: task ID
; outputs:
; Z flag: set if unused, reset if used
is_task_id_used:
bts [task_id_bitmap], r0
ret
task_load:
mov r2, [r0] ; task ID
add r0, 4
mov r3, [r0] ; instruction pointer
add r0, 4
mov r4, [r0] ; stack pointer
add r0, 4
mov r5, [r0] ; code block pointer
add r0, 4
mov r6, [r0] ; stack block pointer
add r0, 4
ret
task_store:
mov [r0], r2 ; task ID
add r0, 4
mov [r0], r3 ; instruction pointer
add r0, 4
mov [r0], r4 ; stack pointer
add r0, 4
mov [r0], r5 ; code block pointer
add r0, 4
mov [r0], r6 ; stack block pointer
add r0, 4
ret
task_empty:
mov r0, task_panic_str
mov r1, task_queue_bottom ; show the address of the task queue in the panic brk output
mov r2, [task_queue_ptr] ; show the the task queue pointer in the panic brk output
call panic
task_panic_str: data.str "Scheduler starved, task queue empty!" data.8 10 data.8 0
const TASK_SIZE: 20
task_id_bitmap: data.32 0
current_task:
data.32 0 ; task ID
data.32 0 ; instruction pointer
data.32 0 ; stack pointer
data.32 0 ; code block pointer
data.32 0 ; stack block pointer
task_queue_ptr: data.32 task_queue_bottom
task_queue_bottom: data.fill 0, 640 ; 32 tasks * 5 entries per task * 4 bytes per word = 640