;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2007. 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] sub eax,[next_usage_update] cmp eax,100 jb .nocounter add [next_usage_update],100 call updatecputimes .nocounter: mov al,0x20 ; send End Of Interrupt signal out 0x20,al btr dword[DONT_SWITCH], 0 jc .return 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 call find_next_task jz .return ; the same task -> skip switch @@: mov byte[DONT_SWITCH], 1 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: xor eax,eax xchg eax,[idleuse] mov [idleusesec],eax 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 align 4 find_next_task: ;info: ; Find next task to execute ;retval: ; ebx = address of the APPDATA for the selected task (slot-base) ; esi = previous slot-base ([current_slot] at the begin) ; 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 call update_counters ; edi := [TASK_BASE] Mov esi, ebx, [current_slot] .loop: cmp bh,[TASK_COUNT] jb @f xor bh, bh mov edi,CURRENT_TASK @@: inc bh ; ebx += APPDATA.size add edi,0x20 ; edi += TASKDATA.size mov al, [edi+TASKDATA.state] test al, al jz .found ; state == 0 cmp al, 5 jne .loop ; 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 ecx, [timer_ticks] sub ecx, [ebx+APPDATA.wait_begin] cmp ecx, [ebx+APPDATA.wait_timeout] jb .loop @@: mov [ebx+APPDATA.wait_param], eax ; retval for wait mov [edi+TASKDATA.state], 0 .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, esi ;esi - previous slot-base 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 ecx, APPDATA.dir_table mov eax, [ebx+ecx] ;offset>0x7F cmp eax, [esi+ecx] ;offset>0x7F je @f 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+ecx+APPDATA.dbg_regs-APPDATA.dir_table] ;offset>0x7F cld macro lodsReg [reg] { lodsd mov reg,eax } lodsReg dr0, dr1, dr2, dr3, dr7 purge lodsReg @@: ret ;end. struc MUTEX_WAITER { .next rd 1 .prev rd 1 .task rd 1 .sizeof: }; virtual at 0 MUTEX_WAITER MUTEX_WAITER end virtual ;void __fastcall mutex_init(struct mutex *lock) align 4 mutex_init: lea eax, [ecx+MUTEX.next] mov [ecx+MUTEX.count],1 mov [ecx+MUTEX.next], eax mov [ecx+MUTEX.prev], eax ret ;void __fastcall mutex_lock(struct mutex *lock) align 4 mutex_lock: dec [ecx+MUTEX.count] jns .done pushfd cli push esi sub esp, MUTEX_WAITER.sizeof mov eax, [ecx+MUTEX.prev] lea esi, [ecx+MUTEX.next] mov [ecx+MUTEX.prev], esp mov [esp+MUTEX_WAITER.next], esi mov [esp+MUTEX_WAITER.prev], eax mov [eax], esp 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 edx, [esp+MUTEX_WAITER.next] mov eax, [esp+MUTEX_WAITER.prev] mov [eax+MUTEX_WAITER.next], edx cmp [ecx+MUTEX.next], esi mov [edx+MUTEX_WAITER.prev], eax jne @F mov [ecx+MUTEX.count], 0 @@: add esp, MUTEX_WAITER.sizeof pop esi popfd .done: ret ;void __fastcall mutex_unlock(struct mutex *lock) align 4 mutex_unlock: pushfd cli lea eax, [ecx+MUTEX.next] cmp eax, [ecx+MUTEX.next] mov [ecx+MUTEX.count], 1 je @F mov eax, [eax+MUTEX_WAITER.task] mov [eax+TASKDATA.state], 0 @@: popfd ret purge MUTEX_WAITER if 0 struc TIMER { .next dd ? .exp_time dd ? .func dd ? .arg dd ? } MAX_PROIRITY 0 ; highest, used for kernel tasks MAX_USER_PRIORITY 0 ; highest priority for user processes USER_PRIORITY 7 ; default (should correspond to nice 0) MIN_USER_PRIORITY 14 ; minimum priority for user processes IDLE_PRIORITY 15 ; lowest, only IDLE process goes here NR_SCHED_QUEUES 16 ; MUST equal IDLE_PRIORYTY + 1 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