;----------------------------
;	TO-DO list
;
;	- finish scheduler
;----------------------------

;----------------------------
PROC scheduler_reschedule_normal
;----------------------------
BEGIN_IRQ
;----------------------------
;	set scheduler flag
	or dword[cpu0.scheduler_flags],SCHEDULER_NOAPIC

	jmp scheduler_normal.entry
ENDP
;----------------------------

;----------------------------
PROC scheduler_reschedule_smp
;----------------------------
BEGIN_IRQ
;----------------------------
;	get cpu ptr and tss ptr
	APIC_GET_ID eax
	mov edx,[cpu_ptr+4*eax]
	mov ebx,[tss_ptr+4*eax]
;----------------------------
;	set scheduler flag
	or dword[edx+cpu_t.scheduler_flags],SCHEDULER_NOAPIC

	jmp scheduler_smp.entry
ENDP
;----------------------------

;----------------------------
PROC scheduler_normal
;----------------------------
BEGIN_IRQ
align 4
.entry:
;----------------------------
;	move the ptr of the act thrad into eax
	mov eax,[cpu0.schdl_act_thread]
;----------------------------
;	inc the timer
	add dword[cpu0.timer],1
	adc dword[cpu0.timer+4],0
;----------------------------
;	check if we need to reschedule
	test dword[schdl_flags],SCHEDULER_RESCHEDULE
	jne .next_thread
	test dword[eax+thread_t.flags],THREAD_RESCHEDULE
	jne .next_thread
;----------------------------
;	check if the thread has more time 2 run
	sub dword[eax+thread_t.time2run],1
	jnz .end
;----------------------------
;	put thread back on a queue and get a new one
.next_thread:
	mov ecx,esp
	mov esp,[cpu0.scheduler_esp]

	CALL scheduler_enqueue_normal
	CALL dword[scheduler_dequeue]
;----------------------------
;	check if this is a ring0 thread
	mov ecx,[eax+thread_t.esp0]
	mov edx,cr3
	mov ebx,[eax+thread_t.pd]

	test ecx,ecx
	jz .stack_change
;----------------------------
;	check if we need to change cr3
	cmp ebx,edx
	je .stack_change

	mov cr3,ebx
;----------------------------
;	set new esp
.stack_change:
	mov esp,[eax+thread_t.esp3]
	mov [tss_cpu0.esp0],ecx
;----------------------------
;	test if we need to send an eoi
	btr dword[cpu0.scheduler_flags],1
	jnc .end

	RETURN_NOAPIC
;----------------------------
align 4
.end:
	RETURN_APIC
ENDP
;----------------------------

;----------------------------
PROC scheduler_smp
;----------------------------
BEGIN_IRQ
;----------------------------
;	get cpu ptr and tss ptr
	APIC_GET_ID eax
	mov edx,[cpu_ptr+4*eax]
	mov ebx,[tss_ptr+4*eax]
;----------------------------
align 4
.entry:
;----------------------------
;	inc the timer
	add dword[edx+cpu_t.timer],1
	adc dword[edx+cpu_t.timer+4],0
;----------------------------
;	move the ptr of the act thrad into eax
	mov eax,[edx+cpu_t.schdl_act_thread]
;----------------------------
;	check if we need to reschedule
	test dword[schdl_flags],SCHEDULER_RESCHEDULE
	jne .next_thread
	test dword[eax+thread_t.flags],THREAD_RESCHEDULE
	jne .next_thread
;----------------------------
;	check if the thread has more time 2 run
	sub dword[eax+thread_t.time2run],1
	jnz .end
;----------------------------
;	put thread back on a queue and get a new one
.next_thread:
	mov ecx,esp
	mov esp,[edx+cpu_t.scheduler_esp]

	push ebx edx

	CALL scheduler_enqueue_smp
	CALL dword[scheduler_dequeue_smp]
;----------------------------
;	check if this is a ring0 thread
	mov ecx,[eax+thread_t.esp0]
	mov edx,cr3
	mov ebx,[eax+thread_t.pd]

	test ecx,ecx
	jz .stack_change
;----------------------------
;	check if we need to change cr3
	cmp ebx,edx
	je .stack_change

	mov cr3,ebx
;----------------------------
;	set new esp
.stack_change:
	pop edx ebx

	mov esp,[eax+thread_t.esp3]
	mov [ebx+tss_t.esp0],ecx
;----------------------------
;	test if we need to send an eoi
	btr dword[edx+cpu_t.scheduler_flags],1
	jnc .end

	RETURN_NOAPIC
;----------------------------
align 4
.end:
	RETURN_APIC
ENDP
;----------------------------

;----------------------------
PROC scheduler_enqueue_normal
;----------------------------
BEGIN
;----------------------------
;	save esp
	mov [eax+thread_t.esp3],ecx
;----------------------------
;	look if we just ran the idle thread
	cmp eax,dword[cpu0.idle_thread]
	je .end
;----------------------------
;	get and check the flags of the thread
	and dword[eax+thread_t.flags],not THREAD_RESCHEDULE
	mov ebx,[eax+thread_t.flags]

	test ebx,THREAD_KILL
	jne .kill
	test ebx,THREAD_WAIT
	jne .wait
	test ebx,THREAD_SLEEP
	jne .sleep
;----------------------------
;	enqueue the thread into the ready queue
;	test dword[cpu0.scheduler_flags],scheduler_reschedule
;	jne .do_it

;	cmp dword[eax+thread_t.dyn_prio],0
;	je .do_it

;	sub dword[eax+thread_t.dyn_prio],1
;----------------------------
;	put it onto the ready queue
.do_it:
	mov ecx,[eax+thread_t.dyn_prio]
	mov ebx,1
	mov esi,[ready_queue_ptr]
	shl ebx,cl

	or [ready_queue_bitmap],ebx

	mov edi,[esi+4*ecx]						;edi= firstThread
	xor edx,edx

	test edi,edi
	jz .first

	mov ebx,[edi+thread_t.prev]				;ebx= firstThread.prev= lastThread
	mov [eax+thread_t.prev],ebx				;actThread.prev= lastThread
	mov [eax+thread_t.next],edx				;actThread.next= NULL
	mov [edi+thread_t.prev],eax				;firstThread.prev= actThread
	mov [ebx+thread_t.next],eax				;lastThread.next= actThread

	jmp .end
;----------------------------
;	it is the first thread in this priority
align 4
.first:
	mov [eax+thread_t.prev],eax				;actThread.prev= firstThread.prev= actThread
	mov [eax+thread_t.next],edi				;actThread.next= firstThread.next= NULL
	mov [esi+4*ecx],eax

	jmp .end
;----------------------------
;	the thread waits for something in a wait queue
align 4
.wait:
;	cmp dword[eax+thread_t.dyn_prio],31
;	je .end

;	add dword[eax+thread_t.dyn_prio],1

	jmp .end
;----------------------------
;	the owner of the thread is going to be killed
align 4
.kill:
	xor ecx,ecx
	mov [eax+thread_t.prev],ecx
	mov [eax+thread_t.next],ecx

	jmp .end
;----------------------------
;	the thread wants to sleep
align 4
.sleep:
	;to-do

	jmp .end
;----------------------------
align 4
.end:
	and dword[schdl_flags],not SCHEDULER_RESCHEDULE

	RETURN
ENDP
;----------------------------

;----------------------------
PROC scheduler_enqueue_smp
;----------------------------
BEGIN
;----------------------------
;	save esp
	mov ebx,cr0
	mov [eax+thread_t.esp3],ecx
;----------------------------
;	test if need to save the fpu
	test ebx,8
	jne .go_on

	mov edi,[eax+thread_t.ptr2fpu]
;----------------------------
;	save fpu env
.fpu_save:
	fxsave [edi]

	or ebx,8
	mov cr0,ebx
;----------------------------
;	look if we just ran the idle thread
align 4
.go_on:
	push eax

	CALL spinlock_acquire, schdl_spin

	pop eax

	cmp eax,dword[edx+cpu_t.idle_thread]
	je .end
;----------------------------
;	get and check the flags of the thread
	mov ebx,[eax+thread_t.flags]
	and dword[eax+thread_t.flags],not THREAD_RESCHEDULE

	test ebx,THREAD_KILL
	jne .kill
	test ebx,THREAD_WAIT
	jne .wait
	test ebx,THREAD_SLEEP
	jne .sleep
;----------------------------
;	enqueue the thread into the ready queue
;	test dword[cpu0.scheduler_flags],scheduler_reschedule
;	jne .do_it

;	cmp dword[eax+thread_t.dyn_prio],0
;	je .do_it

;	sub dword[eax+thread_t.dyn_prio],1
;----------------------------
;	put it onto the ready queue
.do_it:
	mov ecx,[eax+thread_t.dyn_prio]
	mov ebx,1
	mov esi,[ready_queue_ptr]
	shl ebx,cl

	or [ready_queue_bitmap],ebx

	mov edi,[esi+4*ecx]						;edi= firstThread
	xor edx,edx

	test edi,edi
	jz .first

	mov ebx,[edi+thread_t.prev]				;ebx= firstThread.prev= lastThread
	mov [eax+thread_t.prev],ebx				;actThread.prev= lastThread
	mov [eax+thread_t.next],edx				;actThread.next= NULL
	mov [edi+thread_t.prev],eax				;firstThread.prev= actThread
	mov [ebx+thread_t.next],eax				;lastThread.next= actThread

	jmp .end
;----------------------------
;	it is the first thread in this priority
align 4
.first:
	mov [eax+thread_t.prev],eax				;actThread.prev= firstThread.prev= actThread
	mov [eax+thread_t.next],edi				;actThread.next= firstThread.next= NULL
	mov [esi+4*ecx],eax

	jmp .end
;----------------------------
;	the thread waits for something in a wait queue
align 4
.wait:
;	cmp dword[eax+thread_t.dyn_prio],31
;	je .test_schd_queue

;	add dword[eax+thread_t.dyn_prio],1

	jmp .end
;----------------------------
;	the owner of the thread is going to be killed
align 4
.kill:
	xor ecx,ecx
	mov [eax+thread_t.prev],ecx
	mov [eax+thread_t.next],ecx

	jmp .end
;----------------------------
;	the thread wants to sleep
align 4
.sleep:
	;to-do

	jmp .end
;----------------------------
align 4
.end:
	and dword[schdl_flags],not SCHEDULER_RESCHEDULE

	add eax,thread_t.flags
	CALL spinlock_release, eax

	RETURN
ENDP
;----------------------------

;----------------------------
PROC scheduler_dequeue_normal
;----------------------------
BEGIN
;----------------------------
;	get the thread with the highest priority
	mov eax,[run_queue_bitmap]
	mov esi,[run_queue_ptr]
;----------------------------
;	get the highest priority thread
.get_thread:
	bsr eax,eax
	jz .change_queues

	mov ebx,[esi+4*eax]						;ebx= firstThread
	mov edx,[ebx+thread_t.next]				;edx= firstThread.next= secondThread
	mov ecx,[ebx+thread_t.prev]				;ecx= firstThread.prev= lastThread

	test edx,edx
	jz .last

	mov [esi+4*eax],edx						;firstThread= secondThread
	mov [edx+thread_t.prev],ecx				;secondThread.prev= lastThread

	mov eax,ebx
;----------------------------
;	move the needed vales from the thread struc in the right regs
.init:
	cmp eax,[cpu0.schdl_act_thread]
	je .set_time

	mov [cpu0.schdl_act_thread],eax
;----------------------------
;	set base addr for fs and gs regs
	push eax

	CALL gdt_set_base, dword[cpu0.fs], eax

	pop eax

	add eax,thread_t.free_start
	CALL gdt_set_base, dword[cpu0.gs], eax

	jmp .set_time
;----------------------------
;	this is the last thread in the queue, so del the bit in the bitmap
align 4
.last:
	xor ecx,ecx
	mov edx,1
	mov [esi+4*eax],ecx
	mov ecx,eax
	shl edx,cl

	xor [run_queue_bitmap],edx

	mov eax,ebx

	jmp .init
;----------------------------
;	we have to change the queues
align 4
.change_queues:
	mov eax,[run_queue_bitmap]
	mov ebx,[run_queue_ptr]
	xchg eax,[ready_queue_bitmap]
	xchg ebx,[ready_queue_ptr]
	mov [run_queue_bitmap],eax
	mov [run_queue_ptr],ebx

	mov eax,[run_queue_bitmap]
	mov esi,[run_queue_ptr]

	test eax,eax
	jnz .get_thread
;----------------------------
;	there is no thread so we take the idle thread
	mov eax,[cpu0.idle_thread]

	jmp .init
;----------------------------
align 4
.set_time:
;----------------------------
;	we remove the thread from the queue and we need to give the thread some time 2 run
	mov eax,[cpu0.schdl_act_thread]
	xor ebx,ebx
	mov ecx,32

	mov [eax+thread_t.prev],ebx
	sub ecx,[eax+thread_t.dyn_prio]
	mov [eax+thread_t.next],ebx

	cmp ecx,32
	jne .end

	mov ecx,1
;----------------------------
.end:
	mov [eax+thread_t.time2run],ecx

	RETURN
ENDP
;----------------------------

;----------------------------
PROC scheduler_dequeue_normal_smp, this_cpu
;----------------------------
BEGIN
;----------------------------
;	get the thread with the highest priority
	mov eax,[run_queue_bitmap]
	mov esi,[run_queue_ptr]
;----------------------------
;	get the highest priority thread
.get_thread:
	bsr eax,eax
	jz .change_queues

	mov ebx,[esi+4*eax]						;ebx= firstThread
	mov edx,[ebx+thread_t.next]				;edx= firstThread.next= secondThread
	mov ecx,[ebx+thread_t.prev]				;ecx= firstThread.prev= lastThread

	test edx,edx
	jz .last

	mov [esi+4*eax],edx						;firstThread= secondThread
	mov [edx+thread_t.prev],ecx				;secondThread.prev= lastThread

	mov eax,ebx
;----------------------------
;	move the needed vales from the thread struc in the right regs
.init:
	mov edx,[this_cpu]
	cmp eax,[edx+cpu_t.schdl_act_thread]
	je .set_time

	mov [edx+cpu_t.schdl_act_thread],eax
;----------------------------
;	set base addr for fs and gs regs
	push eax

	CALL gdt_set_base, dword[edx+cpu_t.fs], eax

	pop eax

	add eax,thread_t.free_start
	CALL gdt_set_base, dword[edx+cpu_t.gs], eax

	jmp .set_time
;----------------------------
;	this is the last thread in the queue, so del the bit in the bitmap
align 4
.last:
	xor ecx,ecx
	mov edx,1
	mov [esi+4*eax],ecx
	mov ecx,eax
	shl edx,cl

	xor [run_queue_bitmap],edx

	mov eax,ebx

	jmp .init
;----------------------------
;	we have to change the queues
align 4
.change_queues:
	mov eax,[run_queue_bitmap]
	mov ebx,[run_queue_ptr]
	xchg eax,[ready_queue_bitmap]
	xchg ebx,[ready_queue_ptr]
	mov [run_queue_bitmap],eax
	mov [run_queue_ptr],ebx

	mov eax,[run_queue_bitmap]
	mov esi,[run_queue_ptr]

	test eax,eax
	jnz .get_thread
;----------------------------
;	there is no thread so we take the idle thread
	mov edx,[this_cpu]
	mov eax,[edx+cpu_t.idle_thread]

	jmp .init
;----------------------------
align 4
.set_time:
;----------------------------
;	we removed the thread from the queue and we need to give the thread some time 2 run
	CALL spinlock_release, schdl_spin

	mov edx,[this_cpu]
	xor ebx,ebx
	mov eax,[edx+cpu_t.schdl_act_thread]
	mov ecx,32

	mov [eax+thread_t.prev],ebx
	sub ecx,[eax+thread_t.dyn_prio]
	mov [eax+thread_t.next],ebx

	cmp ecx,32
	jne .end

	mov ecx,1
;----------------------------
.end:
	mov [eax+thread_t.time2run],ecx

	push eax

	add eax,thread_t.flags
	CALL spinlock_acquire, eax

	pop eax

	RETURN
ENDP
;----------------------------

;----------------------------
PROC scheduler_dequeue_intel
;----------------------------
BEGIN
;----------------------------
;	get the thread with the highest priority
	mov eax,[run_queue_bitmap]
	mov esi,[run_queue_ptr]
;----------------------------
;	get the highest priority thread
.get_thread:
	bsr eax,eax
	jz .change_queues

	mov ebx,[esi+4*eax]						;ebx= firstThread
	mov edx,[ebx+thread_t.next]				;edx= firstThread.next= secondThread
	mov ecx,[ebx+thread_t.prev]				;ecx= firstThread.prev= lastThread

	test edx,edx
	jz .last

	mov [esi+4*eax],edx						;firstThread= secondThread
	mov [edx+thread_t.prev],ecx				;secondThread.prev= lastThread

	mov eax,ebx
;----------------------------
;	move the needed vales from the thread struc in the right regs
.init:
	cmp eax,[cpu0.schdl_act_thread]
	je .set_time

	mov [cpu0.schdl_act_thread],eax
;----------------------------
;	set base addr for fs and gs regs
	push eax

	CALL gdt_set_base, dword[cpu0.fs], eax

	pop eax

	add eax,thread_t.free_start
	CALL gdt_set_base, dword[cpu0.gs], eax
;----------------------------
;	write the esp value for the ring0 code into the msr reg
	mov eax,[fs:thread_t.esp0]
	mov ecx,176h
	xor edx,edx

	wrmsr

	jmp .set_time
;----------------------------
;	this is the last thread in the queue, so del the bit in the bitmap
align 4
.last:
	xor ecx,ecx
	mov edx,1
	mov [esi+4*eax],ecx
	mov ecx,eax
	shl edx,cl

	xor [run_queue_bitmap],edx

	mov eax,ebx

	jmp .init
;----------------------------
;	we have to change the queues
align 4
.change_queues:
	mov eax,[run_queue_bitmap]
	mov ebx,[run_queue_ptr]
	xchg eax,[ready_queue_bitmap]
	xchg ebx,[ready_queue_ptr]
	mov [run_queue_bitmap],eax
	mov [run_queue_ptr],ebx

	mov eax,[run_queue_bitmap]
	mov esi,[run_queue_ptr]

	test eax,eax
	jnz .get_thread
;----------------------------
;	there is no thread so we take the idle thread
	mov eax,[cpu0.idle_thread]

	jmp .init
;----------------------------
align 4
.set_time:
;----------------------------
;	we removed the thread from the queue and we need to give the thread some time 2 run
	mov eax,[cpu0.schdl_act_thread]
	xor ebx,ebx
	mov ecx,32

	mov [eax+thread_t.prev],ebx
	sub ecx,[eax+thread_t.dyn_prio]
	mov [eax+thread_t.next],ebx

	cmp ecx,32
	jne .end

	mov ecx,1
;----------------------------
.end:
	mov [eax+thread_t.time2run],ecx

	RETURN
ENDP
;----------------------------

;----------------------------
PROC scheduler_dequeue_intel_smp, this_cpu
;----------------------------
BEGIN
;----------------------------
;	get the thread with the highest priority
	mov eax,[run_queue_bitmap]
	mov esi,[run_queue_ptr]
;----------------------------
;	get the highest priority thread
.get_thread:
	bsr eax,eax
	jz .change_queues

	mov ebx,[esi+4*eax]						;ebx= firstThread
	mov edx,[ebx+thread_t.next]				;edx= firstThread.next= secondThread
	mov ecx,[ebx+thread_t.prev]				;ecx= firstThread.prev= lastThread

	test edx,edx
	jz .last

	mov [esi+4*eax],edx						;firstThread= secondThread
	mov [edx+thread_t.prev],ecx				;secondThread.prev= lastThread

	mov eax,ebx
;----------------------------
;	move the needed vales from the thread struc in the right regs
.init:
	mov edx,[this_cpu]
	cmp eax,[edx+cpu_t.schdl_act_thread]
	je .set_time

	mov [edx+cpu_t.schdl_act_thread],eax
;----------------------------
;	set base addr for fs and gs regs
	push eax

	CALL gdt_set_base, dword[edx+cpu_t.fs], eax

	pop eax

	add eax,thread_t.free_start
	CALL gdt_set_base, dword[edx+cpu_t.gs], eax
;----------------------------
;	write the esp value for the ring0 code into the msr reg
	mov eax,[fs:thread_t.esp0]
	mov ecx,176h
	xor edx,edx

	wrmsr

	jmp .set_time
;----------------------------
;	this is the last thread in the queue, so del the bit in the bitmap
align 4
.last:
	xor ecx,ecx
	mov edx,1
	mov [esi+4*eax],ecx
	mov ecx,eax
	shl edx,cl

	xor [run_queue_bitmap],edx

	mov eax,ebx

	jmp .init
;----------------------------
;	we have to change the queues
align 4
.change_queues:
	mov eax,[run_queue_bitmap]
	mov ebx,[run_queue_ptr]
	xchg eax,[ready_queue_bitmap]
	xchg ebx,[ready_queue_ptr]
	mov [run_queue_bitmap],eax
	mov [run_queue_ptr],ebx

	mov eax,[run_queue_bitmap]
	mov esi,[run_queue_ptr]

	test eax,eax
	jnz .get_thread
;----------------------------
;	there is no thread so we take the idle thread
	mov edx,[this_cpu]
	mov eax,[edx+cpu_t.idle_thread]

	jmp .init
;----------------------------
align 4
.set_time:
;----------------------------
;	we removed the thread from the queue and we need to give the thread some time 2 run
	CALL spinlock_release, schdl_spin

	mov edx,[this_cpu]
	xor ebx,ebx
	mov eax,[edx+cpu_t.schdl_act_thread]
	mov ecx,32

	mov [eax+thread_t.prev],ebx
	sub ecx,[eax+thread_t.dyn_prio]
	mov [eax+thread_t.next],ebx

	cmp ecx,32
	jne .end

	mov ecx,1
;----------------------------
.end:
	mov [eax+thread_t.time2run],ecx

	push eax

	add eax,thread_t.flags
	CALL spinlock_acquire, eax
	
	pop eax

	RETURN
ENDP
;----------------------------

;----------------------------
PROC scheduler_add_scheduler, ptr2thread
;----------------------------
BEGIN
	cli

	CALL spinlock_acquire, schdl_spin
;----------------------------
;	add thread to ready queue
	mov esi,[ptr2thread]					;esi= actThread
	mov ebx,1
	mov ecx,[esi+thread_t.dyn_prio]
	mov edi,[ready_queue_ptr]
	shl ebx,cl

	or [ready_queue_bitmap],ebx

	mov eax,[edi+4*ecx]						;eax= firstThread
	xor edx,edx

	test eax,eax
	jz .first

	mov ebx,[eax+thread_t.prev]				;ebx= firstThread.prev= lastThread
	mov [esi+thread_t.prev],ebx				;actThread.prev= lastThread
	mov [esi+thread_t.next],edx				;actThread.next= NULL
	mov [eax+thread_t.prev],esi				;firstThread.prev= actThread
	mov [ebx+thread_t.next],esi				;lastThread.next= actThread

	jmp .end
;----------------------------
;	it is the 1st thread in this priority queue
align 4
.first:
	mov [esi+thread_t.prev],esi				;actThread.prev= firstThread.prev= actThread
	mov [esi+thread_t.next],eax				;actThread.next= firstThread.next= NULL
	mov [edi+4*ecx],esi
;----------------------------
align 4
.end:
	or dword[schdl_flags],SCHEDULER_RESCHEDULE
	
	CALL spinlock_release, schdl_spin

	sti

	RETURN
ENDP
;----------------------------