; ; TCtask - Scheduler and miscellaneous utilities ; ; V1.1 T.Wagner ; V1.2 TECON Ltd. ; name tcasm ; .model large ; public set_vect_d_ public get_vect_d_ public scheduler public schedule_ public _c_schedule public _sched_int ; public tsk_dis_int_ public tsk_ena_int_ public tsk_cli_ public tsk_sti_ public tsk_dseg_ public tsk_cseg_ public tsk_flags_ public tsk_outp_ public tsk_inp_ public tsk_nop_ public test_ ; ; include tsk.mac ; INT_FLAG = 2h ; Int enable flag in upper byte of flag reg r_eflags = 44 ; Offset of flag register on task stack ; .data? ; extrn _tsk_current: dword extrn _tsk_eligible: dword extrn _tsk_preempt: byte extrn _tsk_pretick: byte extrn _tsk_var_prior: byte ; .data ; in_sched db 0 ; .code ; ;_dgroup dw seg dgroup ;"++" ; ; ; upd_prior: Update priority of tasks in eligible queue. ; Only activated if tsk_var_prior is nonzero. ; ; NOTE: This loop is not protected by interrupt disable. ; Since it does not modify the queue itself, there ; is no danger of race conditions. ; upd_prior macro ; les di,_tsk_eligible pinc_loop: mov ax,es or ax,di ; end of chain? jz updp_end inc es:prior[di] jnz pinc_nxt dec es:prior[di] pinc_nxt: les di,es:next[di] jmp pinc_loop ; updp_end: ; endm ; ; ; The scheduler. Note that this routine is entered with the stack ; set up as for an interrupt handler. ; scheduler proc ; cli ; better safe than sorry push ds push eax mov ax,seg in_sched mov ds,ax pop eax cmp in_sched,0 ; already in the scheduler? je sched_ok ; continue if not pop ds iretd ; sched_ok: inc in_sched pop ds sti ; we can now safely enable interrupts push eax push ecx push edx push ebx push ebp push esi push edi db 66h push fs db 66h push gs db 66h push ds db 66h push es ; cmp _tsk_var_prior,0 ; je no_var_pri ; upd_prior ; update priority ; ;no_var_pri: cli ; the following is critical ; mov ax,seg _tsk_current mov ds,ax mov edi,_tsk_current ; get current tcb or edi,edi jz no_current ; mov stack[edi],esp ; store stack pointer & seg ; ;------------------------------------------------------------------------ ; ; enq Enqueue tcb in queue. For local use only. ; entry: es:di = tcb to enqueue ; exit: - ; uses: ax, cx, dx, bp, di ; mov ebx,queue[edi] ; queue head pointer or ebx,ebx jz enq_end ; Выйти если очередь пуста mov dx,prior[edi] ; Загрузить приоритет задачи enq_l: mov ecx,ebx ; Сохранить последний указатель mov ebx,next[ebx] ; Загрузить следующий or ebx,ebx ; Последний в очереди jz enq_found ; cmp dx,prior[ebx] ; Сравнить приоритеты jbe enq_l ; Если приоритет тек. задачи меньше ; чем у следующей то повторить ; enq_found: mov next[edi],ebx ; Вставить следующей найденую ; задачу mov ebx,ecx ; Последний более приоритетный mov next[ebx],edi ; last->next = task enq_end: ; no_current: mov _tsk_pretick,0 ; No preemption tick and _tsk_preempt,1 ; Turn off temp preempt flag ; wait_elig: cli mov edi,_tsk_eligible ; ; If eligible queue empty, enter waiting loop ; or edi,edi ; Если очерьдь пуста то цыклить jnz not_empty ; sti ; enable interrupts nop nop jmp wait_elig ; ; Eligible queue not empty, activate first eligible task. ; not_empty: mov eax,next[edi] ; load next pointer mov _tsk_eligible,eax ; remove from queue ; mov _tsk_current,edi ; set tcb into current mov ax,iniprior[edi] ; reset current tasks priority mov prior[edi],ax ; mov in_sched,0 ; reset scheduler active flag ; mov esi,stack[edi] ; load stack or byte ptr r_eflags+1[esi],INT_FLAG ; enable interrupts mov state[edi],ST_RUNNING ; set task state mov esp,esi ; switch stack db 66h pop es ; restore all registers db 66h pop ds db 66h pop gs db 66h pop fs pop edi pop esi pop ebp pop ebx pop edx pop ecx pop eax iretd ; scheduler endp ; ; ;-------------------------------------------------------------------------- ; ; ; _sched_int ; ; Is the scheduler entry for interrupt handlers. ; It checks if preemption is allowed, returning if not. ; The stack is assumed to be set up as on interrupt entry. ; _sched_int proc ; push ebx cmp _tsk_preempt,0 ; preempt flags 0? jne no_sched ; no scheduling if set mov ebx,_tsk_current ; current running task test flags[ebx],F_CRIT ; preemption allowed for this task? jnz no_sched ; no scheduling if flag set pop ebx ; else go schedule pop ds jmp scheduler ; no_sched: mov _tsk_pretick,1 ; Mark preemption pending pop ebx pop ds iretd ; _sched_int endp ; ; ; void far schedule (void) ; ; Entry for calling the scheduler. Rearranges the stack to ; contain flags. ; NOTE: Uses ax,bx. ; schedule_ proc ; pop eax pushfd push cs push eax cli jmp scheduler ; schedule_ endp ; ; ; void far c_schedule (void) ; ; Entry for conditionally calling the scheduler. Rearranges ; the stack to contain flags, then jumps to _sched_int. ; NOTE: Uses ax,bx. ; _c_schedule proc ; pop eax pushfd push cs push eax cli jmp _sched_int ; _c_schedule endp ; ;-------------------------------------------------------------------------- ; ; word tsk_dseg (void) ; ; Returns current contents of DS register. ; tsk_dseg_ proc mov ax,ds ret tsk_dseg_ endp ; ; word tsk_cseg (void) ; ; Returns current contents of DS register. ; tsk_cseg_ proc mov ax,cs ret tsk_cseg_ endp ; ; word tsk_flags (void) ; ; Returns current contents of Flag register. ; tsk_flags_ proc pushfd pop eax ret tsk_flags_ endp ; ; ; int tsk_dis_int (void) ; ; Returns current state of the interrupt flag (1 if ints were ; enabled), then disables interrupts. ; tsk_dis_int_ proc ;"++" ; pushf pop ax mov cl,9 shr ax,cl and ax,1 cli ret ; tsk_dis_int_ endp ; ; ; void far tsk_ena_int (int state) ; ; Enables interrupts if 'state' is nonzero. ; tsk_ena_int_ proc ;"++" or ax,ax jz teiend sti teiend: ret ; tsk_ena_int_ endp ; ; ; tsk_cli/tsk_sti: disable/enable int ; tsk_cli_ proc cli ret tsk_cli_ endp ; ; tsk_sti_ proc sti ret tsk_sti_ endp ; ; ; tsk_inp/tsk_outp: input/output from/to port ; tsk_inp_ proc ; push dx mov dx,ax in al,dx xor ah,ah pop dx ret ; tsk_inp_ endp ; ; tsk_outp_ proc ; push dx mov dx,ax pop ax out dx,al ret ; tsk_outp_ endp ; ; ; void tsk_nop (void) ; ; Do nothing. Used for very short delays. ; tsk_nop_ proc push ax ; было: push cx ; jmp tnop1 push dx ; tnop1: jmp tnop2 ; mov ah,86h ; tnop2: ret ; xor cx,cx ; mov dx,976 ; int 15h pop dx pop cx pop ax ret tsk_nop_ endp test_ proc ;********************** TEST ******************** push ds push eax mov ax,seg in_sched mov ds,ax mov eax,0b8C80h mov word ptr ds:[eax],1234h pop eax pop ds ;********************** TEST ******************** test_ endp ;------ fartr get_vect_d(byte int) get_vect_d_ proc mov bl,al mov ax,204h ;Чтение вектора прерывания 8 = es:ebx int 31h mov eax,edx movzx edx,cx ret get_vect_d_ endp ;------ void set_vect_d(byte int, fartr) set_vect_d_ proc ;al - int, cx - seg, ebx - offs cli mov edx,ebx mov bl,al mov ax,205h ;Установка вектора 8 на DS:EDX int 31h sti ret set_vect_d_ endp end