;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; ;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; ;; Distributed under terms of the GNU General Public License ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; $Revision$ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; IRQ0 HANDLER (TIMER INTERRUPT) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; align 32 irq0: pushad Mov ds, ax, app_data mov es, ax inc [timer_ticks] mov eax, [timer_ticks] call playNote ; <<<--- Speaker driver sub eax, [next_usage_update] cmp eax, 100 jb .nocounter add [next_usage_update], 100 call updatecputimes .nocounter: xor ecx, ecx ; send End Of Interrupt signal call irq_eoi mov bl, SCHEDULE_ANY_PRIORITY call find_next_task jz .return ; if there is only one running process call do_change_task .return: popad iretd align 4 change_task: pushfd cli pushad mov bl, SCHEDULE_ANY_PRIORITY call find_next_task jz .return ; the same task -> skip switch call do_change_task .return: popad popfd ret uglobal align 4 ; far_jump: ; .offs dd ? ; .sel dw ? context_counter dd 0 ;noname & halyavin next_usage_update dd 0 timer_ticks dd 0 ; prev_slot dd ? ; event_sched dd ? endg align 4 update_counters: mov edi, [TASK_BASE] rdtsc sub eax, [edi+TASKDATA.counter_add] ; time stamp counter add add [edi+TASKDATA.counter_sum], eax ; counter sum ret align 4 updatecputimes: mov ecx, [TASK_COUNT] mov edi, TASK_DATA .newupdate: xor eax, eax xchg eax, [edi+TASKDATA.counter_sum] mov [edi+TASKDATA.cpu_usage], eax add edi, 0x20 loop .newupdate ret ;TODO: Надо бы убрать использование do_change_task из V86... ; и после этого перенести обработку TASKDATA.counter_add/sum в do_change_task align 4 do_change_task: ;param: ; ebx = address of the APPDATA for incoming task (new) ;warning: ; [CURRENT_TASK] and [TASK_BASE] must be changed before (e.g. in find_next_task) ; [current_slot] is the outcoming (old), and set here to a new value (ebx) ;scratched: eax,ecx,esi mov esi, ebx xchg esi, [current_slot] ; set new stack after saving old mov [esi+APPDATA.saved_esp], esp mov esp, [ebx+APPDATA.saved_esp] ; set new thread io-map Mov dword [page_tabs+((tss._io_map_0 and -4096) shr 10)],eax,[ebx+APPDATA.io_map] Mov dword [page_tabs+((tss._io_map_1 and -4096) shr 10)],eax,[ebx+APPDATA.io_map+4] ; set new thread memory-map mov eax, [ebx+APPDATA.process] cmp eax, [current_process] je @f mov [current_process], eax mov eax, [eax+PROC.pdt_0_phys] mov cr3, eax @@: ; set tss.esp0 Mov [tss._esp0],eax,[ebx+APPDATA.saved_esp0] mov edx, [ebx+APPDATA.tls_base] cmp edx, [esi+APPDATA.tls_base] je @f mov [tls_data_l+2], dx shr edx, 16 mov [tls_data_l+4], dl mov [tls_data_l+7], dh mov dx, app_tls mov fs, dx @@: ; set gs selector unconditionally Mov gs,ax,graph_data ; set CR0.TS cmp bh, byte[fpu_owner] ;bh == incoming task (new) clts ;clear a task switch flag je @f mov eax, cr0 ;and set it again if the owner or eax, CR0_TS ;of a fpu has changed mov cr0, eax @@: ; set context_counter (only for user pleasure ???) inc [context_counter] ;noname & halyavin ; set debug-registers, if it's necessary test byte[ebx+APPDATA.dbg_state], 1 jz @f xor eax, eax mov dr6, eax lea esi, [ebx+APPDATA.dbg_regs] cld macro lodsReg [reg] { lodsd mov reg, eax } lodsReg dr0, dr1, dr2, dr3, dr7 purge lodsReg @@: ret ;end. struct MUTEX_WAITER list LHEAD task dd ? type dd ? ends RWSEM_WAITING_FOR_WRITE equ 0 RWSEM_WAITING_FOR_READ equ 1 ;void __fastcall mutex_init(struct mutex *lock) align 4 mutex_init: mov [ecx+MUTEX.wait_list.next], ecx mov [ecx+MUTEX.wait_list.prev], ecx mov [ecx+MUTEX.count], 1 ret ;void __fastcall mutex_lock(struct mutex *lock) align 4 mutex_lock: dec [ecx+MUTEX.count] jns .done pushfd cli sub esp, sizeof.MUTEX_WAITER list_add_tail esp, ecx ;esp= new waiter, ecx= list head mov edx, [TASK_BASE] mov [esp+MUTEX_WAITER.task], edx .forever: mov eax, -1 xchg eax, [ecx+MUTEX.count] dec eax jz @F mov [edx+TASKDATA.state], 1 call change_task jmp .forever @@: mov eax, ecx list_del esp cmp [eax+MUTEX.wait_list.next], eax jne @F mov [eax+MUTEX.count], 0 @@: add esp, sizeof.MUTEX_WAITER popfd .done: ret ;void __fastcall mutex_unlock(struct mutex *lock) align 4 mutex_unlock: pushfd cli mov eax, [ecx+MUTEX.wait_list.next] cmp eax, ecx mov [ecx+MUTEX.count], 1 je @F mov eax, [eax+MUTEX_WAITER.task] mov [eax+TASKDATA.state], 0 @@: popfd ret ;void __fastcall init_rwsem(struct rw_semaphore *sem) align 4 init_rwsem: mov [ecx+RWSEM.wait_list.next], ecx mov [ecx+RWSEM.wait_list.prev], ecx mov [ecx+RWSEM.count], 0 ret ;void __fastcall down_read(struct rw_semaphore *sem) align 4 down_read: pushfd cli mov eax, [ecx+RWSEM.count] test eax, eax js @F cmp ecx, [ecx+RWSEM.wait_list.next] je .ok @@: sub esp, sizeof.MUTEX_WAITER mov eax, [TASK_BASE] mov [esp+MUTEX_WAITER.task], eax mov [esp+MUTEX_WAITER.type], RWSEM_WAITING_FOR_READ mov [eax+TASKDATA.state], 1 list_add_tail esp, ecx ;esp= new waiter, ecx= list head call change_task add esp, sizeof.MUTEX_WAITER popfd ret .ok: inc eax mov [ecx+RWSEM.count], eax popfd ret ;void __fastcall down_write(struct rw_semaphore *sem) align 4 down_write: pushfd cli sub esp, sizeof.MUTEX_WAITER mov edx, [TASK_BASE] mov [esp+MUTEX_WAITER.task], edx mov [esp+MUTEX_WAITER.type], RWSEM_WAITING_FOR_WRITE mov [edx+TASKDATA.state], 1 list_add_tail esp, ecx ;esp= new waiter, ecx= list head xor eax, eax not eax .forever: test eax, [ecx+RWSEM.count] jz @F mov [edx+TASKDATA.state], 1 call change_task jmp .forever @@: mov [ecx+RWSEM.count], eax list_del esp add esp, sizeof.MUTEX_WAITER popfd ret ;void __fastcall up_read(struct rw_semaphore *sem) align 4 up_read: pushfd cli dec [ecx+RWSEM.count] jnz @F mov eax, [ecx+RWSEM.wait_list.next] cmp eax, ecx je @F mov eax, [eax+MUTEX_WAITER.task] mov [eax+TASKDATA.state], 0 @@: popfd ret ;void __fastcall up_write(struct rw_semaphore *sem) align 4 up_write: pushfd cli mov eax, [ecx+RWSEM.wait_list.next] mov [ecx+RWSEM.count], 0 cmp ecx, eax je .done mov edx, [eax+MUTEX_WAITER.type] test edx, edx jnz .wake mov eax, [eax+MUTEX_WAITER.task] mov [eax+TASKDATA.state], 0 .done: popfd ret .wake: push ebx push esi push edi xor esi, esi mov edi, ecx .wake_list: mov ebx, [eax+MUTEX_WAITER.list.next] list_del eax mov edx, [eax+MUTEX_WAITER.task] mov [edx+TASKDATA.state], 0 inc esi cmp edi, ebx je .wake_done mov ecx, [ebx+MUTEX_WAITER.type] test ecx, ecx jz .wake_done mov eax, ebx jmp .wake_list .wake_done: add [edi+RWSEM.count], esi pop edi pop esi pop ebx popfd ret purge MUTEX_WAITER purge RWSEM_WAITING_FOR_WRITE purge RWSEM_WAITING_FOR_READ MAX_PRIORITY = 0 ; highest, used for kernel tasks USER_PRIORITY = 1 ; default IDLE_PRIORITY = 2 ; lowest, only IDLE thread goes here NR_SCHED_QUEUES = 3 ; MUST equal IDLE_PRIORYTY + 1 uglobal ; [scheduler_current + i*4] = zero if there are no threads with priority i, ; pointer to APPDATA of the current thread with priority i otherwise. align 4 scheduler_current rd NR_SCHED_QUEUES endg ; Add the given thread to the given priority list for the scheduler. ; in: edx -> APPDATA, ecx = priority proc scheduler_add_thread ; 1. Acquire the lock. spin_lock_irqsave SchedulerLock ; 2. Store the priority in APPDATA structure. mov [edx+APPDATA.priority], ecx ; 3. There are two different cases: the given list is empty or not empty. ; In first case, go to 6. Otherwise, advance to 4. mov eax, [scheduler_current+ecx*4] test eax, eax jz .new_list ; 4. Insert the new item immediately before the current item. mov ecx, [eax+APPDATA.in_schedule.prev] mov [edx+APPDATA.in_schedule.next], eax mov [edx+APPDATA.in_schedule.prev], ecx mov [eax+APPDATA.in_schedule.prev], edx mov [ecx+APPDATA.in_schedule.next], edx ; 5. Release the lock and return. spin_unlock_irqrestore SchedulerLock ret .new_list: ; 6. Initialize the list with one item and make it the current item. mov [edx+APPDATA.in_schedule.next], edx mov [edx+APPDATA.in_schedule.prev], edx mov [scheduler_current+ecx*4], edx ; 7. Release the lock and return. spin_unlock_irqrestore SchedulerLock ret endp ; Remove the given thread from the corresponding priority list for the scheduler. ; in: edx -> APPDATA proc scheduler_remove_thread ; 1. Acquire the lock. spin_lock_irqsave SchedulerLock ; 2. Remove the item from the corresponding list. mov eax, [edx+APPDATA.in_schedule.next] mov ecx, [edx+APPDATA.in_schedule.prev] mov [eax+APPDATA.in_schedule.prev], ecx mov [ecx+APPDATA.in_schedule.next], eax ; 3. If the given thread is the current item in the list, ; advance the current item. ; 3a. Check whether the given thread is the current item; ; if no, skip the rest of this step. mov ecx, [edx+APPDATA.priority] cmp [scheduler_current+ecx*4], edx jnz .return ; 3b. Set the current item to eax; step 2 has set eax = next item. mov [scheduler_current+ecx*4], eax ; 3c. If there were only one item in the list, zero the current item. cmp eax, edx jnz .return mov [scheduler_current+ecx*4], 0 .return: ; 4. Release the lock and return. spin_unlock_irqrestore SchedulerLock ret endp SCHEDULE_ANY_PRIORITY = 0 SCHEDULE_HIGHER_PRIORITY = 1 ;info: ; Find next task to execute ;in: ; bl = SCHEDULE_ANY_PRIORITY: ; consider threads with any priority ; bl = SCHEDULE_HIGHER_PRIORITY: ; consider only threads with strictly higher priority than the current one, ; keep running the current thread if other ready threads have the same or lower priority ;retval: ; ebx = address of the APPDATA for the selected task (slot-base) ; edi = address of the TASKDATA for the selected task ; ZF = 1 if the task is the same ;warning: ; [CURRENT_TASK] = bh , [TASK_BASE] = edi -- as result ; [current_slot] is not set to new value (ebx)!!! ;scratched: eax,ecx proc find_next_task call update_counters spin_lock_irqsave SchedulerLock push NR_SCHED_QUEUES ; If bl == SCHEDULE_ANY_PRIORITY = 0, loop over all NR_SCHED lists. ; Otherwise, loop over first [APPDATA.priority] lists. test bl, bl jz .start mov ebx, [current_slot] mov edi, [TASK_BASE] mov eax, [ebx+APPDATA.priority] test eax, eax jz .unlock_found mov [esp], eax .start: xor ecx, ecx .priority_loop: mov ebx, [scheduler_current+ecx*4] test ebx, ebx jz .priority_next .task_loop: mov ebx, [ebx+APPDATA.in_schedule.next] mov edi, ebx shr edi, 3 add edi, CURRENT_TASK - (SLOT_BASE shr 3) mov al, [edi+TASKDATA.state] test al, al jz .task_found ; state == 0 cmp al, 5 jne .task_next ; state == 1,2,3,4,9 ; state == 5 pushad ; more freedom for [APPDATA.wait_test] call [ebx+APPDATA.wait_test] mov [esp+28], eax popad or eax, eax jnz @f ; testing for timeout mov eax, [timer_ticks] sub eax, [ebx+APPDATA.wait_begin] cmp eax, [ebx+APPDATA.wait_timeout] jb .task_next xor eax, eax @@: mov [ebx+APPDATA.wait_param], eax ; retval for wait mov [edi+TASKDATA.state], 0 .task_found: mov [scheduler_current+ecx*4], ebx ; If we have selected a thread with higher priority ; AND rescheduling is due to IRQ, ; turn the current scheduler list one entry back, ; so the current thread will be next after high-priority thread is done. mov ecx, [esp] cmp ecx, NR_SCHED_QUEUES jz .unlock_found mov eax, [current_slot] mov eax, [eax+APPDATA.in_schedule.prev] mov [scheduler_current+ecx*4], eax .unlock_found: pop ecx spin_unlock_irqrestore SchedulerLock .found: mov [CURRENT_TASK], bh mov [TASK_BASE], edi rdtsc ;call _rdtsc mov [edi+TASKDATA.counter_add], eax; for next using update_counters cmp ebx, [current_slot] ret .task_next: cmp ebx, [scheduler_current+ecx*4] jnz .task_loop .priority_next: inc ecx cmp ecx, [esp] jb .priority_loop mov ebx, [current_slot] mov edi, [TASK_BASE] jmp .unlock_found endp if 0 struc TIMER { .next dd ? .exp_time dd ? .func dd ? .arg dd ? } uglobal rdy_head rd 16 endg align 4 pick_task: xor eax, eax .pick: mov ebx, [rdy_head+eax*4] test ebx, ebx jz .next mov [next_task], ebx test [ebx+flags.billable] jz @F mov [bill_task], ebx @@: ret .next: inc eax jmp .pick ; param ; eax= task ; ; retval ; eax= task ; ebx= queue ; ecx= front if 1 or back if 0 align 4 shed: cmp [eax+.tics_left], 0;signed compare mov ebx, [eax+.priority] setg ecx jg @F mov edx, [eax+.tics_quantum] mov [eax+.ticks_left], edx cmp ebx, (IDLE_PRIORITY-1) je @F inc ebx @@: ret ; param ; eax= task align 4 enqueue: call shed;eax cmp [rdy_head+ebx*4], 0 jnz @F mov [rdy_head+ebx*4], eax mov [rdy_tail+ebx*4], eax mov [eax+.next_ready], 0 jmp .pick @@: test ecx, ecx jz .back mov ecx, [rdy_head+ebx*4] mov [eax+.next_ready], ecx mov [rdy_head+ebx*4], eax jmp .pick .back: mov ecx, [rdy_tail+ebx*4] mov [ecx+.next_ready], eax mov [rdy_tail+ebx*4], eax mov [eax+.next_ready], 0 .pick: call pick_proc;select next task ret end if