; =============================================================================
;
; Litos - Task
;
; =============================================================================
CODE_SECTION 32
; ------------- Create task flags
TASKCREATE_ADMIN EQU B0 ; allow admin reserve
; -----------------------------------------------------------------------------
; Clone current task
; -----------------------------------------------------------------------------
; OUTPUT: EAX = 0 for child, new task ID for parent
; CY = error, no child task created
; -----------------------------------------------------------------------------
; ------------- Push registers
TaskClone: push ebx ; push EBX
push ecx ; push ECX
push edx ; push EDX
push esi ; push ESI
push edi ; push EDI
push ebp ; push EBP
push ds ; push DS
push es ; push ES
push fs ; push FS
push gs ; push GS
pushf ; push flags
cli ; disable interrupts
; ------------- Get current (parent) task (-> EDX)
CURRENT edx ; get current task
lea ecx,[edx+TASK_Stack] ; ECX <- end of stack
sub ecx,esp ; ECX <- stack size
neg ecx ; ECX <- -stack size
fnsave [edx+TASK_FPU] ; save FPU state
; ------------- Create new task (-> EBX)
xor eax,eax ; EAX <- flags
call TaskCreate ; create new task
jnc TaskClone2 ; task created OK
popf ; pop flags
stc ; set error flag
jmp short TaskClone9 ; error
; ------------- Initialize task
TaskClone2: mov dword [byte ebx+TASK_EIP],ScheduleRet; scheduler
lea eax,[ebx+TASK_Stack-7*4+ecx] ; EAX <- task stack
mov dword [eax],0 ; flags
mov dword [byte eax+6*4],TaskCloneCont ; return address
mov dword [ebx+TASK_ESP],eax ; stack address
; ------------- Store new task into run-queue
and dword [ebx+TASK_Alarm+ALARM_Expire],byte 0
and dword [ebx+TASK_Alarm+ALARM_Expire+4],byte 0
call TaskGoWait ; store task into wait-queue
call WakeUpTask ; wake up task
xchg eax,ebx ; EAX <- new task
jmp short TaskClone8
; ------------- Child
TaskCloneCont: xor eax,eax ; EAX <- 0
; ------------- Pop registers - parent
TaskClone8: popf ; pop flags
clc ; operation OK
TaskClone9: pop gs ; pop GS
pop fs ; pop FS
pop es ; pop ES
pop ds ; pop DS
pop ebp ; pop EBP
pop edi ; pop EDI
pop esi ; pop ESI
pop edx ; pop EDX
pop ecx ; pop ECX
pop ebx ; pop EBX
ret
; -----------------------------------------------------------------------------
; Set TSS descriptor
; -----------------------------------------------------------------------------
; INPUT: ECX = TSS descriptor
; EBX = task
; -----------------------------------------------------------------------------
SetTSSDescr: push ebx ; push EBX
add ebx,byte TASK_TSS ; EBX <- address of TSS
mov word [ecx+GLDT_Limit],TASK_IOTerm-TASK_TSS+2 ; limit
mov [ecx+GLDT_Base3-3],ebx ; base address HIGH
mov [ecx+GLDT_Base],ebx ; base address LOW
mov word [ecx+GLDT_Access],B7+9 ; TSS type, present
pop ebx ; pop EBX
ret
; -----------------------------------------------------------------------------
; Set LDT descriptor
; -----------------------------------------------------------------------------
; INPUT: ECX = LDT descriptor
; EBX = task
; -----------------------------------------------------------------------------
SetLDTDescr: push ebx ; push EBX
or word [ecx+GLDT_Limit],byte -1 ; limit
and dword [ecx+GLDT_Base3-3],byte 0 ; base address HIGH
and dword [ecx+GLDT_Base],byte 0 ; base address LOW
mov word [ecx+GLDT_Access],B7+2 ; LDT type, present
pop ebx ; pop EBX
ret
; -----------------------------------------------------------------------------
; Create new task
; -----------------------------------------------------------------------------
; INPUT: AL = flags
; B0: 1=admin reserve allowed, 0=only user tasks
; EDX = address of parent task
; OUTPUT: CY = memory error (EBX = NULL)
; EBX = address of new task (NULL=memory error)
; LOCKS: SysMemLock
; -----------------------------------------------------------------------------
; ------------- Create task memory block (-> EDX)
TaskCreate: xchg eax,ebx ; EBX <- push EAX
mov eax,TASK_size ; EAX <- size of task
call SysMemAlloc ; get new task memory block
xchg eax,ebx ; EBX <- address of task memory block
jnc TaskCreate2 ; task created OK
ret
; ------------- Push registers
TaskCreate2: push eax ; push EAX
push ecx ; push ECX
push edx ; push EDX
push esi ; push ESI
push edi ; push EDI
; ------------- Copy parent task
mov esi,edx ; ESI <- source task
mov edi,ebx ; EDI <- destination task
mov ecx,TASK_size/4 ; ECX <- size of task / 4
rep movsd ; copy parent task
; ------------- Get TSS index (-> EAX)
call GetTSS ; get TSS index
jnc TaskCreate3 ; TSS index OK
xchg eax,ebx ; EAX <- task memory block
call SysMemFree ; free task memory block
xchg eax,ebx ; EBX <- 0
stc ; set error flag
jmp TaskCreate9
; ------------- Prepare TSS selector
TaskCreate3: lea ecx,[TASK_FIRST_TSS+eax*8] ; EDX <- TSS selector
mov [ebx+TASK_TR],ecx ; TSS selector
mov [ebx+TASK_TSSIndex],eax ; save TSS index
; ------------- Initialize TSS descriptor
add ecx,TaskTSSTab-TASK_FIRST_TSS ; ECX <- TSS descriptor
call SetTSSDescr ; set TSS descriptor
; ------------- Prepare LDT selector
sub ecx,TaskTSSTab-TASK_FIRST_LDT ; ECX <- LDT selector
mov [ebx+TASK_LDT],ecx ; LDT selector
; ------------- Initialize LDT descriptor
add ecx,TaskLDTTab-TASK_FIRST_LDT ; ECX <- LDT descriptor
call SetLDTDescr ; set LDT descriptor
; ------------- Initialize object head
mov eax,OBJECT_TASK ; EAX <- object type, task
call ObjectInit ; initialize object head
; ------------- Split remaining time quantum (between task and its parent)
shr dword [ebx+TASK_Remaining],1; task remaining time/2
inc dword [edx+TASK_Remaining] ; parent remaining time+1
shr dword [edx+TASK_Remaining],1; parent remaining time/2
; ------------- Initialize some parameters
lea eax,[ebx+TASK_Stack] ; initialize stack address
mov [ebx+TASK_ESP0],eax
mov [ebx+TASK_ESP1],eax
mov [ebx+TASK_ESP2],eax
mov byte [ebx+TASK_AbsPrio],PRIORITY_NORMAL ; priority
mov byte [ebx+TASK_RelPrio],0
mov byte [ebx+TASK_AddPrio],6
mov byte [ebx+TASK_MaxPrio],PRIORITY_MAXUSR
call TaskCalcPrio ; recalc new priority
mov [ebx+TASK_Priority],al ; store new priority
; ------------- Initialize task link
lea eax,[ebx+TASK_Link] ; EAX <- task link
TREEINIT eax ; initialize task link
; ------------- Initialize link to task queue
lea eax,[ebx+TASK_Queue] ; EAX <- link to task queue
LISTINIT eax ; initialize link to task queue
; ------------- Initialize signals
xor eax,eax ; EAX <- 0
mov [ebx+TASK_Signal],eax ; reset signal flags LOW
mov [ebx+TASK_Signal+4],eax ; reset signal flags HIGH
mov [ebx+TASK_SigNum],eax ; reset number of signals
mov [ebx+TASK_TaskLock],eax ; no task lock
lea edi,[ebx+TASK_SigRTNum] ; EDI <- number of RT signals
xor ecx,ecx ; ECX <- 0
mov cl,32/4 ; ECX <- number of RT signals in DW
rep stosw ; reset real-time counters
mov al,SIGNAL_size ; EAX <- size of signal queue item
call SysMemAlloc ; get signal queue item
mov [ebx+TASK_SigRes],eax ; reserve signal entry
%ifdef SMP
LOCK_Init (ebx+TASK_SigLock) ; initialize signal queue lock
%endif
lea eax,[ebx+TASK_SigQueue] ; EAX <- signal queue
LISTINIT eax ; initialize signal queue
mov eax,[ebx+TASK_SigAction] ; EAX <- signal actions
%ifdef SMP
lock ; CPU instruction lock
%endif
inc dword [eax+ST_Ref] ; increase shared sounter
; ------------- Link with parent
; mov [ebx+TASK_Link+TREE_Parent],edx ; link to parent
; mov eax,[edx+TASK_Link+TREE_Child] ; EAX <- first child
; or eax,eax ; any child?
; jz TaskCreate4 ; no child
; lea esi,[ebx+TASK_Link] ; ESI <- sibling list
; LISTADD eax,esi,ecx ; link to sibling list
; jmp short TaskCreate5
;
;TaskCreate4: mov [edx+TASK_Link+TREE_Child],ebx ; it is first child
; ------------- Start time
TaskCreate5: call GetSystemTime ; get system time
mov [ebx+TASK_StartTime],eax ; store start time LOW
mov [ebx+TASK_StartTime+4],edx ; store start time HIGH
; ------------- Clear running time
xor eax,eax ; EAX <- 0
mov [ebx+TASK_RunTime],eax ; clear running time LOW
mov [ebx+TASK_RunTime+4],eax ; clear running time HIGH
; ------------- Initialize alarm
lea eax,[ebx+TASK_Alarm] ; EAX <- alarm
LISTINIT eax ; initialize list
xor eax,eax ; EAX <- 0
mov [ebx+TASK_Alarm+ALARM_Repeat],eax ; repeat time LOW
mov [ebx+TASK_Alarm+ALARM_Repeat+4],eax ; repeat time HIGH
mov dword [ebx+TASK_Alarm+ALARM_Func],SleepWakeUp; function
mov [ebx+TASK_Alarm+ALARM_Data],ebx ; user data
; ------------- Mask of consoles
mov eax,[ConActMask] ; EAX <- mask of active consoles
mov [ebx+TASK_ConOutMask],eax ; mask of output consoles
mov [ebx+TASK_ConInMask],eax ; mask of input consoles
; ------------- Pop registers
clc ; flag - operation OK
TaskCreate9: pop edi ; pop EDI
pop esi ; pop ESI
pop edx ; pop EDX
pop ecx ; pop ECX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Create idle task
; -----------------------------------------------------------------------------
; INPUT: EDX = index of CPU
; OUTPUT: EBX = idle task
; DESTROYS: EAX, ECX, EDX, ESI, EDI
; LOCKS: SysMemLock
; -----------------------------------------------------------------------------
; ------------- Create idle task structure (it cannot fail, memory is OK now)
IdleInit: mov eax,TASK_size ; EAX <- size of task
call SysMemAlloc ; get new task memory block
xchg eax,ebx ; EBX <- address of task memory block
; ------------- Prepare run-queue (-> ESI)
mov esi,[RunQueueAddr+edx*4] ; ESI <- run-queue
; ------------- Clear task structure
mov edi,ebx ; EDI <- task structure
xor eax,eax ; EAX <- 0
mov ecx,TASK_size/4 ; ECX <- size of task structure
rep stosd ; clear task structure
; ------------- Initialize object head
mov eax,OBJECT_TASK ; EAX <- object type, task
; call ObjectInit ; initialize object head
; ------------- Initialize basic registers
lea eax,[ebx+TASK_Stack] ; EAX <- task stack
mov [ebx+TASK_ESP0],eax ; ESP for level 0
mov [ebx+TASK_SS0],ss ; SS for level 0
mov [ebx+TASK_ESP1],eax ; ESP for level 1
mov [ebx+TASK_SS1],ss ; SS for level 1
mov [ebx+TASK_ESP2],eax ; ESP for level 2
mov [ebx+TASK_SS2],ss ; SS for level 2
mov [ebx+TASK_ESP],eax ; ESP for task
mov eax,cr3 ; EAX <- cr3
mov [ebx+TASK_CR3],eax ; set CR3
mov dword [ebx+TASK_EIP],Idle; idle loop
mov [ebx+TASK_ES],es ; ES
mov [ebx+TASK_CS],cs ; CS
mov [ebx+TASK_SS],ss ; SS
mov [ebx+TASK_DS],ds ; DS
mov eax,USER_DS ; AX <- user data segment
mov [ebx+TASK_FS],eax ; FS
mov [ebx+TASK_GS],eax ; GS
mov word [ebx+TASK_IOBase],TASK_IOBitmap ; perm. bit map
or dword [ebx+TASK_IOTerm],byte -1 ; set I/O terminator
pushf ; push flags
pop eax ; EAX <- flags
and eax,~(B12+B13) ; set I/O privilege level to 0
mov [ebx+TASK_Flags],eax ; flags
push eax ; push EAX
popf ; pop flags
; ------------- Initialize FPU
fninit ; initialize FPU
fnsave [ebx+TASK_FPU] ; save FPU state
; ------------- Prepare TSS selector
mov al,1 ; admin reserve allowed
call GetTSS ; get TSS index
lea ecx,[TASK_FIRST_TSS+eax*8] ; EDX <- TSS selector
mov [ebx+TASK_TR],ecx ; TSS selector
mov [ebx+TASK_TSSIndex],eax ; save TSS index
; ------------- Initialize TSS descriptor
add ecx,TaskTSSTab-TASK_FIRST_TSS ; ECX <- TSS descriptor
call SetTSSDescr ; set TSS descriptor
; ------------- Prepare LDT selector
sub ecx,TaskTSSTab-TASK_FIRST_LDT ; ECX <- LDT selector
mov [ebx+TASK_LDT],ecx ; LDT selector
; ------------- Initialize LDT descriptor
add ecx,TaskLDTTab-TASK_FIRST_LDT ; ECX <- LDT descriptor
call SetLDTDescr ; set LDT descriptor
; ------------- Initialize task link
lea eax,[ebx+TASK_Link] ; EAX <- task link
TREEINIT eax ; initialize task link
; ------------- Set some other variables
mov byte [ebx+TASK_State],TASK_RUNNING ; running state
mov byte [ebx+TASK_Type],TASK_IDLE ; idle task type
mov [ebx+TASK_CPU],dl ; current CPU
mov eax,[CPUMask] ; EAX <- CPU mask
mov [ebx+TASK_CPUMask],eax ; mask of allowed CPUs
; ------------- Initialize link to task queue
lea eax,[ebx+TASK_Queue] ; EAX <- link to task queue
LISTINIT eax ; initialize link to task queue
; ------------- Initialize signals
dec dword [ebx+TASK_SigMask] ; enable all signals LOW
dec dword [ebx+TASK_SigMask+4] ; enable all signals HIGH
%ifdef SMP
LOCK_Init (ebx+TASK_SigLock) ; initialize signal queue lock
%endif
lea eax,[ebx+TASK_SigQueue] ; EAX <- signal queue
LISTINIT eax ; initialize signal queue
mov dword [ebx+TASK_SigMax],200 ; maximal number of signals
; ------------- Create default table of signal actions
mov eax,SIGTAB ; size of table of signal actions
call SysMemAlloc ; get table of signal actions
mov [ebx+TASK_SigAction],eax ; table of signal actions
xchg eax,edi ; EDI <- start of table
xor eax,eax ; EAX <- 0
inc eax ; EAX <- 1
stosd ; initialize table lock
stosd ; initialize number of shared count
dec eax ; EAX <- 0
mov ecx,SIGTAB_size/4-2 ; ECX <- size of table
rep stosd ; clear signal action table
; ------------- Create reserve signal entry for SIGKILL
xor eax,eax ; EAX <- 0
mov al,SIGNAL_size ; EAX <- size of signal queue item
call SysMemAlloc ; get signal queue item
mov [ebx+TASK_SigRes],eax ; reserve signal entry
; ------------- Store task into run-queue
mov [ebx+TASK_RunQueue],esi ; set run-queue
mov edx,[esi+RUNQ_Current] ; EDX <- current task queue
xor ecx,ecx ; ECX <- 0, level
call TaskEnqueue ; store task into run-queue
inc dword [esi+RUNQ_Total] ; increase number of tasks
mov [esi+RUNQ_Idle],ebx ; idle task of this run-queue
; ------------- Start time
call GetSystemTime ; get system time
mov [ebx+TASK_StartTime],eax ; store start time LOW
mov [ebx+TASK_StartTime+4],edx ; store start time HIGH
; ------------- Initialize alarm
lea eax,[ebx+TASK_Alarm] ; EAX <- alarm
LISTINIT eax ; initialize list
xor eax,eax ; EAX <- 0
mov [ebx+TASK_Alarm+ALARM_Repeat],eax ; repeat time LOW
mov [ebx+TASK_Alarm+ALARM_Repeat+4],eax ; repeat time HIGH
mov dword [ebx+TASK_Alarm+ALARM_Func],SleepWakeUp; function
mov [ebx+TASK_Alarm+ALARM_Data],ebx ; user data
; ------------- Mask of consoles
mov eax,[ConActMask] ; EAX <- mask of active consoles
mov [ebx+TASK_ConOutMask],eax ; mask of output consoles
mov [ebx+TASK_ConInMask],eax ; mask of input consoles
ret
; -----------------------------------------------------------------------------
; Destroy task
; -----------------------------------------------------------------------------
; INPUT: EBX = task to destroy
; -----------------------------------------------------------------------------
TaskDestroy:
; !!!!!!!! TODO
ret
; -----------------------------------------------------------------------------
; Kill task
; -----------------------------------------------------------------------------
; INPUT: EBX = task to kill
; -----------------------------------------------------------------------------
TaskKill:
; !!!!!!!! TODO
ret
; -----------------------------------------------------------------------------
; Initialize TSS list
; -----------------------------------------------------------------------------
InitTSS: mov eax,TaskTSSTab ; EDX <- task TSS table
mov ebx,TaskFreeList ; EDX <- list of free TSS
mov ecx,TASK_MAX ; ECX <- number of tasks
InitTSS2: call ListLast ; add entry into list
add eax,byte GLDT_size ; EAX <- next entry
loop InitTSS2 ; next entry
ret
; -----------------------------------------------------------------------------
; Get TSS index
; -----------------------------------------------------------------------------
; INPUT: AL = flag, 0=only user tasks, 1=admin reserve allowed
; OUTPUT: EAX = index of TSS
; CY = error, no free TSS
; -----------------------------------------------------------------------------
; ------------- Push registers
GetTSS: push ebx ; push EBX
; ------------- Lock free TSS table
pushf ; push flags
cli ; disable interrupts
%ifdef SMP
mov ebx,TaskFreeLock ; EBX <- lock to free TSS table
call SpinLock ; lock free TSS table
%endif
; ------------- Check if any free TSS left
cmp dword [TaskFree],byte 0 ; any free TSS left?
jg GetTSS2 ; any free TSS left
cmp al,0 ; admin reserve allowed?
je GetTSS9 ; no admin reserve
cmp dword [TaskFree],byte -TASK_ADMIN ; admin reserve?
jle GetTSS9 ; no free tasks left
; ------------- Decrease number of free tasks
GetTSS2: dec dword [TaskFree] ; decrease number of free tasks
inc dword [TaskUsedNum] ; increase number of used tasks
; ------------- Get index of next free task
mov eax,[TaskFreeList+LIST_Next] ; EBX <- next free task
call ListDel ; remove task from the list
sub eax,TaskTSSTab ; EAX <- offset in TSS table
shr eax,3 ; EAX <- relative index of task
; ------------- Set flag in bit map of used tasks
push eax ; push EAX
push ecx ; push ECX
mov ebx,eax ; EBX <- index of task
shr ebx,3 ; EBX <- offset od BYTE
and al,7 ; AL <- index of bit in BYTE
xchg eax,ecx ; CL <- index of bit in BYTE
xor eax,eax ; EAX <- 0
inc eax ; EAX <- 1
shl eax,cl ; EAX <- bit mask
or [TaskUsedMap+ebx],al ; set flag in bit map
pop ecx ; pop ECX
pop eax ; pop EAX
; ------------- OK, unlock free TSS table
%ifdef SMP
LOCK_Unlock TaskFreeLock ; unlock free TSS table
%endif
popf ; pop flags and enable interrupts
clc ; clear error flag
; ------------- Pop registers
pop ebx ; pop EBX
ret
; ------------- Error, unlock free TSS table
GetTSS9:
%ifdef SMP
LOCK_Unlock TaskFreeLock ; unlock free TSS table
%endif
popf ; pop flags and enable interrupts
stc ; set error flag
; ------------- Pop registers
pop ebx ; pop EBX
ret
; -----------------------------------------------------------------------------
; Check if TSS index is valid
; -----------------------------------------------------------------------------
; INPUT: EAX = TSS index
; OUTPUT: CY = error, TSS index is not valid (task does not exist)
; NOTES: It does not lock TSS table.
; -----------------------------------------------------------------------------
; ------------- Push registers
CheckTSS: push eax ; push EAX
push ebx ; push EBX
push ecx ; push ECX
; ------------- Check maximal TSS value
cmp eax,TASK_MAX ; is TSS index valid?
jae CheckTSS6 ; TSS index is not valid
; ------------- Check if task is valid
mov ebx,eax ; EBX <- index of task
shr ebx,3 ; EBX <- offset od BYTE
and al,7 ; AL <- index of bit in BYTE
xchg eax,ecx ; CL <- index of bit in BYTE
xor eax,eax ; EAX <- 0
inc eax ; EAX <- 1
shl eax,cl ; EAX <- bit mask
test [TaskUsedMap+ebx],al ; test flag in bit map
jnz CheckTSS8 ; task is valid
CheckTSS6: stc ; set error flag
; ------------- Pop registers
CheckTSS8: pop ecx ; pop ECX
pop ebx ; pop EBX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Free TSS index
; -----------------------------------------------------------------------------
; INPUT: EAX = index of TSS
; OUTPUT: CY = error, TSS index is not valid (task does not exist)
; -----------------------------------------------------------------------------
; ------------- Push registers
FreeTSS: push ebx ; push EBX
; ------------- Lock free TSS table
pushf ; push flags
cli ; disable interrupts
%ifdef SMP
mov ebx,TaskFreeLock ; EBX <- lock to free TSS table
call SpinLock ; lock free TSS table
%endif
; ------------- Check if index of TSS is valid
call CheckTSS ; check if TSS index is valid
jc FreeTSS9 ; error, TSS index is not valid
; ------------- Push registers 2
push eax ; push EAX
push ecx ; push ECX
; ------------- Increase number of free tasks
inc dword [TaskFree] ; increase number of free tasks
dec dword [TaskUsedNum] ; decrease number of used tasks
; ------------- Add task to the list of free tasks
push eax ; push EAX
shl eax,3 ; EAX <- offset of task
add eax,TaskTSSTab ; EAX <- address of task
mov ebx,TaskFreeList ; EBX <- free task list
call ListLast ; add task into end of list
pop eax ; pop EAX
; ------------- Reset flag in bit map of used tasks
mov ebx,eax ; EBX <- index of task
shr ebx,3 ; EBX <- offset od BYTE
and al,7 ; AL <- index of bit in BYTE
xchg eax,ecx ; CL <- index of bit in BYTE
xor eax,eax ; EAX <- 0
inc eax ; EAX <- 1
shl eax,cl ; EAX <- bit mask
xor [TaskUsedMap+ebx],al ; reset flag in bit map
; ------------- Pop registers 2
pop ecx ; pop ECX
pop eax ; pop EAX
; ------------- OK, unlock free TSS table
%ifdef SMP
LOCK_Unlock TaskFreeLock ; unlock free TSS table
%endif
popf ; pop flags and enable interrupts
clc ; clear error flag
; ------------- Pop registers
pop ebx ; pop EBX
ret
; ------------- Error, unlock free TSS table
FreeTSS9:
%ifdef SMP
LOCK_Unlock TaskFreeLock ; unlock free TSS table
%endif
popf ; pop flags and enable interrupts
stc ; set error flag
; ------------- Pop registers
pop ebx ; pop EBX
ret
; -----------------------------------------------------------------------------
; Data
; -----------------------------------------------------------------------------
DATA_SECTION
; ------------- Free TSS table
align 4, db 0
%ifdef SMP
TaskFreeLock: SPINLOCK ; lock to free TSS table
%endif
align 4, db 0
TaskUsedNum: dd 0 ; number of used tasks
TaskFree: dd TASK_MAX-TASK_ADMIN ; number of free tasks (-reserve)
TaskFreeList: LISTHEAD ; list of free TSS
; -----------------------------------------------------------------------------
; Uninitialized data
; -----------------------------------------------------------------------------
BSS_SECTION
; ------------- Bit map of used tasks (32 or 512 bytes)
align 4, resb 1
TaskUsedMap: resb (TASK_MAX+7)/8
|