; =============================================================================
;
; Litos - Semaphore and mutex
;
; =============================================================================
CODE_SECTION 32
; ------------- Macro - initialized semaphore (%1 = counter, usually 1)
%macro SEMAPHORE 1
TLOCK ; task lock
dd %1 ; counter
dd 0 ; no sleepers
%endmacro
; ------------- Macro - initialized mutex
%macro MUTEX 0
SEMAPHORE SEM_UNLOCKED ; semaphore
dd 0 ; owner
dd 0 ; recursion counter
%endmacro
; ------------- Macro - initialize semaphore (%1=pointer, %2=counter)
%macro SEM_Init 2
TLOCK_Init %1 ; initialize task lock
mov dword [%1+SEM_Count],%2 ; set counter
and dword [%1+SEM_Sleepers],byte 0 ; clear sleepers flag
%endmacro
; ------------- Macro - initialize mutex (%1=pointer)
%macro MUTEX_Init 1
push eax ; push EAX
xor eax,eax ; EAX <- 0
mov [%1+SEM_Sleepers],eax ; clear sleepers flag
mov [%1+SEM_Owner],eax ; clear owner
mov [%1+SEM_Recursion],eax ; recursion counter
inc eax ; EAX <- 1
mov dword [%1+SEM_Count],eax ; set counter to 1
TLOCK1_Init %1 ; initialize task lock
pop eax ; pop EAX
%endmacro
; -----------------------------------------------------------------------------
; Initialize semaphore
; -----------------------------------------------------------------------------
; INPUT: EAX = initial counter
; EBX = pointer to semaphore SEM
; -----------------------------------------------------------------------------
SemInit: SEM_Init ebx,eax ; initialize semaphore
ret
; -----------------------------------------------------------------------------
; Initialize mutex
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to mutex MUT
; -----------------------------------------------------------------------------
MutexInit: MUTEX_Init ebx ; initialize mutex
ret
; -----------------------------------------------------------------------------
; Semaphore down callback function
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to semaphore SEM
; EDX = 1 on first passage else 0
; OUTPUT: AL = 0: sleep and wait for resource become free
; AL = 1: resource successfully locked
; EDX = 0
; -----------------------------------------------------------------------------
; ------------- Get sleepers and add undo of my lock (only in first passage)
SemDownCB: mov eax,[ebx+SEM_Sleepers] ; 1=any sleeper, 0=no sleeper
add eax,edx ; EAX <- 0, 1, 2 (sleepers + my undo)
xor edx,edx ; EDX <- 0, no other undo
; ------------- Count other sleepers to lock counter
dec eax ; other sleepers + my lock
%ifdef SMP
lock ; CPU instruction lock
%endif
add [ebx+SEM_Count],eax ; add other sleepers and test
js SemDownCB4 ; lock not caught
; ------------- We have caught the semaphore, stop waiting
xor eax,eax ; EAX <- 0
mov [ebx+SEM_Sleepers],eax ; clear sleeper flag
inc eax ; EAX <- 1, continue flag
ret
; ------------- We continue waiting
SemDownCB4: xor eax,eax ; EAX <- 0
inc eax ; EAX <- 1
mov [ebx+SEM_Sleepers],eax ; mark that any task is waiting
dec eax ; EAX <- 0, go to sleep flag
ret
; -----------------------------------------------------------------------------
; Semaphore down (lock semaphore)
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to semaphore SEM
; -----------------------------------------------------------------------------
; ------------- Try to lock semaphore
SemDown:
%ifdef SMP
lock ; CPU instruction lock
%endif
dec dword [ebx+SEM_Count] ; decrease counter
js SemDown2 ; semaphore is already locked
ret
; ------------- Push registers
SemDown2: push eax ; push EAX
push ecx ; push ECX
push edx ; push EDX
; ------------- Prepare flag to undo my try to lock (-> EDX)
xor edx,edx ; EDX <- 0
inc edx ; EDX <- 1, flag of first passage
; ------------- Lock semaphore
xor eax,eax ; EAX <- 0
dec eax ; EAX <- -1, infinitely wait
mov ecx,SemDownCB ; ECX <- lock semaphore callback
call TaskSleepFnc ; lock semaphore
; ------------- Pop registers
pop edx ; pop EDX
pop ecx ; pop ECX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Try to semaphore down callback function
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to semaphore SEM
; OUTPUT: AL = 0: semaphore not cautch
; AL = 1: semaphore can be locked, wake-up next task
; -----------------------------------------------------------------------------
; ------------- Clear sleepers
SemTryDownCB: xor eax,eax ; EAX <- 0, no other sleeper
xchg eax,[ebx+SEM_Sleepers] ; EAX <- 1=any other sleeper
; ------------- Add sleepers (and undo my lock) into counter
inc eax ; EAX <- sleepers include my lock
%ifdef SMP
lock ; CPU instruction lock
%endif
add [ebx+SEM_Count],eax ; add other sleepers and test
setns al ; AL <- 1 sem is free, 0 not free
ret
; -----------------------------------------------------------------------------
; Try to semaphore down (lock semaphore)
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to semaphore SEM
; OUTPUT: CY = error, semaphore already locked
; NC = OK, semaphore successfully locked
; -----------------------------------------------------------------------------
; ------------- Try to lock semaphore
SemTryDown:
%ifdef SMP
lock ; CPU instruction lock
%endif
dec dword [ebx+SEM_Count] ; decrease counter
js SemTryDown2 ; semaphore is already locked
clc ; clear error flag, we caught the lock
ret
; ------------- Push registers
SemTryDown2: push ecx ; push ECX
; ------------- Undo semaphore lock
mov ecx,SemTryDownCB ; ECX <- try lock semaphore callback
call TaskWakeUpFnc ; unlock semaphore
; ------------- Pop registers
pop ecx ; pop ECX
stc ; set error flag, we have not semaphore
ret
; -----------------------------------------------------------------------------
; Semaphore up (unlock semaphore)
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to semaphore SEM
; -----------------------------------------------------------------------------
SemUp:
%ifdef SMP
lock ; CPU instruction lock
%endif
inc dword [ebx+SEM_Count] ; increase counter
jle near TaskWakeUp ; other task was waiting
ret ; nobody waits for semaphore
; -----------------------------------------------------------------------------
; Lock mutext callback function
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to mutex MUT
; EDX = 1: first passage, else 0
; OUTPUT: EAX = 0: sleep and wait for resource become free
; EAX = 1: resource successfully locked
; EDX = 0, not first passage
; -----------------------------------------------------------------------------
; ------------- Get sleepers and add undo of my lock (only in first passage)
MutexLockCB: mov eax,[ebx+SEM_Sleepers] ; 1=any sleeper, 0=no sleeper
add eax,edx ; EAX <- 0, 1, 2 (sleepers + my undo)
xor edx,edx ; EDX <- 0, no other undo
; ------------- Count other sleepers to lock counter
dec eax ; other sleepers without me
%ifdef SMP
lock ; CPU instruction lock
%endif
add [ebx+SEM_Count],eax ; add other sleepers and test
js MutexLockCB4 ; lock not caught
; ------------- We have caught the mutex, stop waiting
CURRENT eax ; EAX <- current task
mov [ebx+SEM_Owner],eax ; set owner of mutex
xor eax,eax ; EAX <- 0
mov [ebx+SEM_Sleepers],eax ; clear sleeper flag
inc eax ; EAX <- 1, continue flag
mov [ebx+SEM_Recursion],eax ; set recursion counter to 1
ret
; ------------- We continue waiting
MutexLockCB4: xor eax,eax ; EAX <- 0
inc eax ; EAX <- 1
mov [ebx+SEM_Sleepers],eax ; mark that any task is waiting
dec eax ; EAX <- 0, go to sleep flag
ret
; -----------------------------------------------------------------------------
; Lock mutex
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to mutex MUT
; -----------------------------------------------------------------------------
; ------------- Push registers
MutexLock: push eax ; push EAX
; ------------- Prepare current task (-> EAX)
CURRENT eax ; EAX <- current task
; ------------- Try to lock mutex
%ifdef SMP
lock ; CPU instruction lock
%endif
dec dword [ebx+SEM_Count] ; decrease counter
js MutexLock2 ; mutex is already locked
; ------------- Set owner of mutex
mov [ebx+SEM_Owner],eax ; set owner of mutex
xor eax,eax ; EAX <- 0
inc eax ; EAX <- 1
mov [ebx+SEM_Recursion],eax ; set recursion counter
; ------------- Pop registers
pop eax ; pop EAX
ret
; ------------- Check if mutex is locked by this task
MutexLock2: cmp eax,[ebx+SEM_Owner] ; we already own the lock?
jne MutexLock4 ; we have not the lock
; ------------- Increase recursion counter
inc dword [ebx+SEM_Recursion] ; increase recursion counter
; ------------- Pop registers
pop eax ; pop EAX
ret
; ------------- Push registers
MutexLock4: push ecx ; push ECX
push edx ; push EDX
; ------------- Prepare flag to undo my try to lock (-> EDX)
xor edx,edx ; EDX <- 0
inc edx ; EDX <- 1, flag of first passage
; ------------- Lock mutex
xor eax,eax ; EAX <- 0
dec eax ; EAX <- -1, infinitely wait
mov ecx,MutexLockCB ; ECX <- lock mutex callback
call TaskSleepFnc ; lock mutex
; ------------- Pop registers
pop edx ; pop EDX
pop ecx ; pop ECX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Try to lock mutex callback function
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to mutex MUT
; OUTPUT: AL = 0: mutex not cautch
; AL = 1: mutex can be locked, wake-up next task
; -----------------------------------------------------------------------------
; ------------- Clear sleepers
MutexTryLockCB: xor eax,eax ; EAX <- 0, no other sleeper
xchg eax,[ebx+SEM_Sleepers] ; EAX <- 1=any other sleeper
; ------------- Add sleepers (and undo my lock) into counter
inc eax ; EAX <- sleepers include my lock
%ifdef SMP
lock ; CPU instruction lock
%endif
add [ebx+SEM_Count],eax ; add other sleepers and test
setns al ; AL <- 1 sem is free, 0 not free
ret
; -----------------------------------------------------------------------------
; Try to lock mutex
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to mutex MUT
; OUTPUT: CY = error, mutex already locked
; NC = OK, mutex successfully locked
; -----------------------------------------------------------------------------
; ------------- Push registers
MutexTryLock: push ecx ; push ECX
; ------------- Prepare current task (-> ECX)
CURRENT ecx ; ECX <- current task
; ------------- Try to lock mutex
%ifdef SMP
lock ; CPU instruction lock
%endif
dec dword [ebx+SEM_Count] ; decrease counter
js MutexTryLock2 ; mutex is already locked
; ------------- Set owner of mutex
mov [ebx+SEM_Owner],ecx ; set owner of mutex
xor ecx,ecx ; ECX <- 0
inc ecx ; ECX <- 1
mov [ebx+SEM_Recursion],ecx ; set recursion counter
; ------------- Pop registers (here is NC)
pop ecx ; pop ECX
ret
; ------------- Check if mutex is locked by this task
MutexTryLock2: cmp ecx,[ebx+SEM_Owner] ; we already own the lock?
jne MutexTryLock4 ; we have not the lock
; ------------- Increase recursion counter
inc dword [ebx+SEM_Recursion] ; increase recursion counter
; ------------- Pop registers (here is NC)
pop ecx ; pop ECX
ret
; ------------- Undo mutex lock
MutexTryLock4: mov ecx,MutexTryLockCB ; ECX <- try lock mutex callback
call TaskWakeUpFnc ; unlock mutex
; ------------- Pop registers
pop ecx ; pop ECX
stc ; set error flag, we have not mutex
ret
; -----------------------------------------------------------------------------
; Unlock mutex
; -----------------------------------------------------------------------------
; INPUT: EBX = pointer to mutex MUT
; -----------------------------------------------------------------------------
; ------------- Decrease recursion counter
MutexUnlock: dec dword [ebx+SEM_Recursion] ; decrease recursion
jnz MutexUnlock4 ; it is not last recursion
; ------------- Reset owner of mutex
and dword [ebx+SEM_Owner],byte 0 ; clear owner
; ------------- Last recursion, unlock mutex and switch to another task
%ifdef SMP
lock ; CPU instruction lock
%endif
inc dword [ebx+SEM_Count] ; increase counter
jle near TaskWakeUp ; other task was waiting
ret ; nobody waits for mutex
; ------------- Not last recursion, only unlock mutex
MutexUnlock4:
%ifdef SMP
lock ; CPU instruction lock
%endif
inc dword [ebx+SEM_Count] ; increase counter
ret ; nobody waits for mutex
|