Tvùrce webu je i pro tebe! Postav tøeba web. Bez grafika. Bez kodéra. Hned.
wz

ALARM.ASM

Alarm


; =============================================================================
;
;                              Litos - Alarm
;
; =============================================================================
; Alarms are stored in 256-level list of alarms with 13 ms difference of levels
; (i.e. 2^17 * 100-ns). Levels are designed as doubly linked lists with hashed
; index, using 256-bit mask indicating which level of lists is not empty. Time
; counting is realized only as shifting level pointer which points to the base
; level of the lists. Last level has range from 3.342 second to infinity.
; =============================================================================

		CODE_SECTION	32

; ------------- Alarm list constants

ALARMLIST_SHIFT	EQU	17		; bits of one alarm list level
ALARMLIST_MAX	EQU	256		; number of levels of alarm list
ALARMLIST_DIF	EQU	1<<ALARMLIST_SHIFT ; alarm list difference (2^17=13 ms)

; ------------- Macro - initialized ALARM structure (%1=function, %2=user data)

%macro		ALARMTIMER 2
		LISTHEAD		; link to list (empty = expired)
		dd	0,0		; system time of alarm expiration
		dd	0,0		; repeat interval and current level
		dd	%1		; callback function
		dd	%2		; user data
%endmacro

; -----------------------------------------------------------------------------
;                          Lock/unlock alarm list
; -----------------------------------------------------------------------------
; NOTES:	Use macro ALARMLOCK to lock, ALARMUNLOCK to unlock.
; -----------------------------------------------------------------------------

; ------------- Alarm lock function

%ifdef	SMP
		LOCK_LockFnc AlarmLock,AlarmListLock ; declare alarm lock
%endif

; ------------- Macro - call alarm lock function

%macro		ALARMLOCK 0
%ifdef	SMP
		call	AlarmLock	; call alarm lock function
%endif
%endmacro

; ------------- Macro - alarm unlock

%macro		ALARMUNLOCK 0
%ifdef	SMP
		LOCK_Unlock AlarmListLock ; unlock alarm
%endif
%endmacro

; -----------------------------------------------------------------------------
;                      Initialize alarm list
; -----------------------------------------------------------------------------

; ------------- Initialize alarm list

AlarmListInit:	mov	ebx,AlarmListList ; EBX <- first list of alarms
		mov	ecx,ALARMLIST_MAX ; ECX <- number of list of alarms
AlarmListInit2:	call	ListInit	; initialize list of alarms
		add	ebx,byte LIST_size ; EBX <- next list of alarms
		loop	AlarmListInit2	; initialize next list of alarms

; ------------- Re-Initialize alam list start time

AlarmReInit:	call	GetSystemTime	; get system time
		mov	[AlarmListStart],eax ; alarm list start time LOW
		mov	[AlarmListStart+4],edx ; alarm list start time HIGH
		ret

; -----------------------------------------------------------------------------
;                          Initialize alarm entry
; -----------------------------------------------------------------------------
; INPUT:	EBX = alarm structure (or 0 on error)
;		ECX = user data
;		EDX = callback function (INPUT:	EBX = alarm structure
;						ECX = user data
;						EDI:ESI = system time)
;					 DESTROYS: EAX..EBP
;					 NOTES:	Can enable interrupts.
;					 It can safely delete alarm entry.
; -----------------------------------------------------------------------------

AlarmInit:	push	eax		; push EAX
		jmp	short AlarmCreate2 ; initialize alarm entry

; -----------------------------------------------------------------------------
;                           Create new alarm
; -----------------------------------------------------------------------------
; INPUT:	ECX = user data
;		EDX = callback function (INPUT:	EBX = alarm structure
;						ECX = user data
;						EDI:ESI = system time)
;					 DESTROYS: EAX..EBP
;					 NOTES:	Can enable interrupts.
;					 It can safely delete alarm entry.
; OUTPUT:	CY = error (EBX = 0)
;		EBX = alarm structure (or 0 on error)
; NOTES: If alarm is created with other method (static) then initialize link.
; -----------------------------------------------------------------------------

; ------------- Push registers

AlarmCreate:	push	eax		; push EAX

; ------------- Create alarm structure

		mov	eax,ALARM_size	; EAX <- size of alarm structure
		call	SysMemAlloc	; allocate alarm structure
		xchg	eax,ebx		; EBX <- address of alarm structure
		jc	short AlarmCreate8 ; memory error (EBX = 0)

; ------------- Fill up entries (and clear error flag NC)

AlarmCreate2:	LISTINIT ebx		; initialize link to list
		xor	eax,eax		; EAX <- 0
		mov	[ebx+ALARM_Expire],eax ; expiration time LOW
		mov	[ebx+ALARM_Expire+4],eax ; expiration time HIGH
		mov	[ebx+ALARM_Repeat],eax ; repeat interval LOW
		mov	[ebx+ALARM_Repeat+4],eax ; repeat interval HIGH + level
		mov	[ebx+ALARM_Func],edx ; callback function
		mov	[ebx+ALARM_Data],ecx ; user data

; ------------- Pop registers

AlarmCreate8:	pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                            Delete alarm
; -----------------------------------------------------------------------------
; INPUT:	EBX = alarm structure
; OUTPUT:	EBX = NULL
; NOTES: If alarm is running it stops it first.
; -----------------------------------------------------------------------------

; ------------- Stop alarm if it is running

AlarmDelete:	call	AlarmStop	; stop alarm

; ------------- Delete alarm structure

		xchg	eax,ebx		; EAX <- alarm structure
		call	SysMemFree	; free alarm structure
		xchg	eax,ebx		; EBX <- NULL
		ret

; -----------------------------------------------------------------------------
;                    Set alarm at given absolute date and time
; -----------------------------------------------------------------------------
; INPUT:	EDX:EAX = alarm absolute date and time (in 100-ns)
;		EBX = alarm structure
; NOTES: - It recalculates absolute time to system time.
;	 - Repeat interval and other entries are not affected.
;	 - Alarm most not be running.
; -----------------------------------------------------------------------------

; ------------- Check if alarm is not running

AlarmSetAt:	LISTTEST ebx		; is alarm running?
		jnz	short AlarmSetAt8 ; alarm is running

; ------------- Push registers

		push	eax		; push EAX
		push	edx		; push EDX
		push	esi		; push ESI
		push	edi		; push EDI

; ------------- Get start time (-> EDX:EAX)

		xchg	eax,esi		; ESI <- alarm LOW
		mov	edi,edx		; EDI <- alarm HIGH
		call	GetStartTime	; get start time (-> EDX:EAX)

; ------------- Recalculate absolute time to system time

		sub	esi,eax		; recalc to system time LOW
		sbb	edi,edx		; recalc to system time HIGH

; ------------- Set time of expiration

		mov	[ebx+ALARM_Expire],esi ; expiration time LOW
		mov	[ebx+ALARM_Expire+4],edi ; expiration time HIGH

; ------------- Pop registers

		pop	edi		; pop EDI
		pop	esi		; pop ESI
		pop	edx		; pop EDX
		pop	eax		; pop EAX
AlarmSetAt8:	ret

; -----------------------------------------------------------------------------
;                            Set alarm interval 
; -----------------------------------------------------------------------------
; INPUT:	EAX = alarm start interval (in milliseconds, 0=now)
;		EBX = alarm structure
;		ECX = alarm repeat interval (in milliseconds, 0=no repeat) 
; NOTES: Alarm most not be running.
; -----------------------------------------------------------------------------

; ------------- Check if alarm is not running

AlarmSetInt:	LISTTEST ebx		; is alarm running?
		jnz	short AlarmSetInt8 ; alarm is running

; ------------- Push registers

		push	eax		; push EAX
		push	ecx		; push ECX
		push	edx		; push EDX
		push	esi		; push ESI

; ------------- Set repeat interval

		xchg	eax,ecx		; EAX <- repeat, ECX <- start
		mov	edx,TIME_1MS	; EDX <- time of 1 ms
		mul	edx		; recalc repeat interval to 100-ns
		mov	[ebx+ALARM_Repeat],eax ; repeat interval LOW
		mov	[ebx+ALARM_Repeat+4],edx ; repeat interval HIGH

; ------------- Get current system time (-> ESI:ECX)

		call	GetSystemTime	; get system time (-> EDX:EAX)
		xchg	eax,ecx		; ECX <- system time LOW, EAX <- start
		mov	esi,edx		; ESI <- system time HIGH

; ------------- Set start interval

		mov	edx,TIME_1MS	; EDX <- time of 1 ms
		mul	edx		; recalc start interval to 100-ns
		add	eax,ecx		; EAX <- start time LOW
		adc	edx,esi		; ESI <- start time HIGH
		mov	[ebx+ALARM_Expire],eax ; start time LOW
		mov	[ebx+ALARM_Expire+4],edx ; start time HIGH

; ------------- Pop registers

		pop	esi		; pop ESI
		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	eax		; pop EAX
AlarmSetInt8:	ret

; -----------------------------------------------------------------------------
;                         Stop alarm (without lock)
; -----------------------------------------------------------------------------
; INPUT:	EBX = alarm structure
; NOTES: Alarm must be running (it does not check if it is).
; -----------------------------------------------------------------------------

; ------------- Push registers

AlarmStop0:	push	eax		; push EAX
		push	ecx		; push ECX

; ------------- Decrease number of alarms in the list

		dec	dword [AlarmListTotal] ; decrease number of alarms

; ------------- Remove alarm from the list

		LISTDEL	ebx,eax,ecx	; remove alarm from the list

; ------------- Clear alarm list mask if it was last alarm in the list

		cmp	eax,ecx		; was it last entry in the list?
		jne	short AlarmStop02 ; it was not last entry in the list
		movzx	eax,byte [ebx+ALARM_Level] ; EAX <- level in alarm list
		sub	al,[AlarmListBase] ; EAX <- relative level
		btr	[AlarmListMask],eax ; clear alarm list mask

; ------------- Mark alarm as not running

AlarmStop02:	LISTINIT ebx		; mark alarm as not running

; ------------- Pop registers

		pop	ecx		; pop ECX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                               Stop alarm
; -----------------------------------------------------------------------------
; INPUT:	EBX = alarm structure
; NOTES: It tests if alarm is running.
; -----------------------------------------------------------------------------

; ------------- Disable interrupts

AlarmStop:	pushf			; push flags
		cli			; disable interrupts

; ------------- Lock alarm list

		ALARMLOCK		; lock alarm list

; ------------- Check if alarm is running

		LISTTEST ebx		; is alarm running?
		jz	short AlarmStop2 ; alarm is not running

; ------------- Stop alarm

		call	AlarmStop0	; stop alarm

; ------------- Unlock alarm list

AlarmStop2:	ALARMUNLOCK		; unlock alarm list

; ------------- Enable interrupts

		popf			; pop flags
		ret

; -----------------------------------------------------------------------------
;                           Start alarm (without lock)
; -----------------------------------------------------------------------------
; INPUT:	EBX = alarm structure
; NOTES: Alarm must not be running (it does not check if it is).
;	 Minimal start time is limited to the current time (it affects
;	 next repeat time).
; -----------------------------------------------------------------------------

; ------------- Push registers

AlarmStart0:	push	eax		; push EAX
		push	ecx		; push ECX
		push	edx		; push EDX
		push	esi		; push ESI

; ------------- Offset of expiration time (-> EDX:EAX)

		mov	eax,[ebx+ALARM_Expire] ; EAX <- expiration time LOW
		mov	edx,[ebx+ALARM_Expire+4] ; EDX <- expiration time HIGH
		sub	eax,[AlarmListStart] ; EAX <- relative alarm time LOW
		sbb	edx,[AlarmListStart+4]; EDX <- relative alarm time HIGH
		jns	short AlarmStart02 ; offset is OK (non-negative)
		sub	[ebx+ALARM_Expire],eax ; limit expiration time LOW
		sbb	[ebx+ALARM_Expire+4],edx ; limit expiration time HIGH
		xor	eax,eax		; EAX <- 0, limit underflow LOW
		xor	edx,edx		; EDX <- 0, limit underflow HIGH

; ------------- Correct time to next alarm service

AlarmStart02:	or	edx,edx		; offset HIGH too big?
		jnz	short AlarmStart03 ; offset HIGH too big
		or	eax,eax		; offset LOW too big?
		js	short AlarmStart03 ; offset LOW too big
		cmp	eax,[AlarmListNext] ; check time to next service
		jge	AlarmStart03	; time is OK
		mov	[AlarmListNext],eax ; correct time to next service

; ------------- Alarm list level (-> EAX)

AlarmStart03:	shrd	eax,edx,ALARMLIST_SHIFT ; EAX <- level in alarm list
		shr	edx,ALARMLIST_SHIFT ; EDX <- level HIGH
		jnz	short AlarmStart04 ; level overflow
		cmp	eax,ALARMLIST_MAX ; valid alarm list level?
		jb	short AlarmStart06 ; level is OK
AlarmStart04:	xor	eax,eax		; EAX <- 0
		mov	al,ALARMLIST_MAX-1 ; AL <- limit level

; ------------- Set alarm list mask

AlarmStart06:	bts	[AlarmListMask],eax ; set alarm list mask

; ------------- Current level in alarm list

		add	al,[AlarmListBase] ; add base level
		mov	[ebx+ALARM_Level],al ; store alarm list level

; ------------- Find place where to store alarm to

		lea	ecx,[AlarmListList+eax*LIST_size]; ECX<-first list head
		mov	esi,ecx		; ESI <- list head
		mov	eax,[ebx+ALARM_Expire] ; EAX <- alarm time LOW
		mov	edx,[ebx+ALARM_Expire+4] ; EDX <- alarm time HIGH
AlarmStart07:	mov	esi,[esi+LIST_Prev] ; ESI <- previous alarm
		cmp	esi,ecx		; last alarm?
		je	short AlarmStart09 ; last alarm
		cmp	edx,[esi+ALARM_Expire+4] ; expiration time HIGH
		jne	AlarmStart08	; not equal HIGH	
		cmp	eax,[esi+ALARM_Expire] ; expiration time LOW
AlarmStart08:	jb	short AlarmStart07 ; not before, check next alarm

; ------------- Store alarm into the list

AlarmStart09:	LISTADD	esi, ebx, eax	; add alarm after the found entry

; ------------- Increase number of alarms in the list

		inc	dword [AlarmListTotal] ; increase number of alarms

; ------------- Pop registers

		pop	esi		; pop ESI
		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                               Start alarm
; -----------------------------------------------------------------------------
; INPUT:	EBX = alarm structure
; NOTES: If alarm is running it stops it first.
; -----------------------------------------------------------------------------

; ------------- Disable interrupts

AlarmStart:	pushf			; push flags
		cli			; disable interrupts

; ------------- Lock alarm list

		ALARMLOCK		; lock alarm list

; ------------- Check if alarm is running

		LISTTEST ebx		; is alarm running?
		jz	short AlarmStart2 ; alarm is not running

; ------------- Stop alarm

		call	AlarmStop0	; stop alarm

; ------------- Start alarm

AlarmStart2:	call	AlarmStart0	; start alarm

; ------------- Unlock alarm list

		ALARMUNLOCK		; unlock alarm list

; ------------- Enable interrupts

		popf			; pop flags
		ret

; -----------------------------------------------------------------------------
;                              Alarm time service
; -----------------------------------------------------------------------------
; NOTES:	This function must be called with interrupts disabled and only
;		from CPU 0.
; -----------------------------------------------------------------------------

; ------------- Push registers

AlarmTime:	pusha			; push all registers

; ------------- Get system time with round correction 0.5 ms (-> EDI:ESI)

AlarmTime1:	call	GetSystemTime	; get system time
		add	eax,TIME_1MS/2	; round correction 0.5 ms
		adc	edx,byte 0	; carry
		xchg	eax,esi		; ESI <- system time LOW
		mov	edi,edx		; EDI <- system time HIGH

; ------------- Lock alarm list

		ALARMLOCK		; lock alarm list

; ------------- Prepare next alarm request

		mov	eax,[AlarmListStart] ; EAX <- current start time
		add	eax,ALARMLIST_DIF ; EAX <- next level
		sub	eax,esi		; EAX <- interval to next level
		mov	[AlarmListNext],eax ; next service

; ------------- Get first alarm from current level of alarm list (-> EBX)

		movzx	ecx,byte [AlarmListBase] ; ECX <- base level
		lea	ecx,[AlarmListList+ecx*LIST_size] ; ECX <- list head
		mov	ebx,[ecx+LIST_Next] ; EBX <- first alarm from the list
		cmp	ebx,ecx		; is it valid alarm?
		je	short AlarmTime4 ; there is no alarm

; ------------- Check if this alarm has expired

		cmp	edi,[ebx+ALARM_Expire+4] ; check expiration time HIGH
		jne	short AlarmTime2 ; check alarm
		cmp	esi,[ebx+ALARM_Expire] ; check expiration time LOW
AlarmTime2:	jb	near AlarmTime8 ; alarm is not expired

; ------------- Correct next alarm request

		mov	eax,[ebx+LIST_Next] ; EAX <- next alarm service
		cmp	eax,ecx		; next alarm?
		je	AlarmTime3	; no next alarm
		mov	eax,[eax+ALARM_Expire] ; EAX <- expiration time LOW
		sub	eax,esi		; EAX <- offset LOW
		mov	[AlarmListNext],eax ; correct time to next service

; ------------- Stop alarm

AlarmTime3:	call	AlarmStop0	; stop alarm

; ------------- Add alarm with repeating to the list again

		mov	ecx,[ebx+ALARM_Repeat] ; ECX <- repeat time LOW
		mov	edx,[ebx+ALARM_Repeat+4] ; EDX <- repeat time HIGH
		and	edx,0ffffffh	; mask out level byte
		jnz	AlarmTime32	; repeat interval should be set
		jecxz	AlarmTime38	; repeat interval should not be set
AlarmTime32:	add	[ebx+ALARM_Expire],ecx ; new expiration time LOW
		adc	[ebx+ALARM_Expire+4],edx ; new expiration time HIGH
		call	AlarmStart0	; start alarm again

; ------------- Prepare data for callback function

AlarmTime38:	mov	ecx,[ebx+ALARM_Data] ; ECX <- user data
		mov	edx,[ebx+ALARM_Func] ; EDX <- callback function
		sub	esi,TIME_1MS/2	; return round correction 0.5 ms
		sbb	edi,byte 0	; carry

; ------------- Unlock alarm list

		ALARMUNLOCK		; unlock alarm list

; ------------- Call alarm callback function

		call	edx		; call callback function
		cli			; disable interrupts

; ------------- Pop registers

		jmp	near AlarmTime1	; next alarm service

; ------------- Check if base level of alarm list should be shifted

AlarmTime4:	mov	eax,[AlarmListStart] ; EAX <- start time LOW
		mov	edx,[AlarmListStart+4] ; EDX <- start time HIGH
		add	eax,ALARMLIST_DIF ; EAX <- new start time LOW
		adc	edx,byte 0	; EDX <- new start time HIGH
		cmp	edi,edx		; compare next level HIGH
		jne	short AlarmTime5 ; HIGH not equal
		cmp	esi,eax		; compare next level LOW
AlarmTime5:	jb	near AlarmTime9	; next level not reached

; ------------- New start time of base level

		mov	[AlarmListStart],eax ; new start time LOW
		mov	[AlarmListStart+4],edx ; new start time HIGH

; ------------- Increase alarm list base level

		movzx	ecx,byte [AlarmListBase] ; ECX <- alarm list base level
		inc	cl		; increase alarm list base level
		mov	[AlarmListBase],cl ; store new alarm list base level

; ------------- Correct next alarm request

		lea	ebp,[AlarmListList+ecx*LIST_size] ; EBP <- list head
		mov	ebx,[ebp+LIST_Next] ; EBX <- first alarm from the list
		cmp	ebx,ebp		; is it valid alarm?
		je	short AlarmTime54 ; there is no alarm
		mov	ebx,[ebx+ALARM_Expire] ; EBX <- expiration time LOW
		sub	ebx,esi		; EBX <- offset LOW
		mov	[AlarmListNext],ebx ; correct time to next service

; ------------- Rotate alarm list mask

AlarmTime54:	mov	ebx,AlarmListMask ; EBX <- alarm list mask
		bt	dword [ebx+0*4],0 ; test bit 0 (set CY if bit 0 is set)
		rcr	dword [ebx+7*4],1 ; rotate mask 7
		rcr	dword [ebx+6*4],1 ; rotate mask 6
		rcr	dword [ebx+5*4],1 ; rotate mask 5
		rcr	dword [ebx+4*4],1 ; rotate mask 4
		rcr	dword [ebx+3*4],1 ; rotate mask 3
		rcr	dword [ebx+2*4],1 ; rotate mask 2
		rcr	dword [ebx+1*4],1 ; rotate mask 1
		rcr	dword [ebx+0*4],1 ; rotate mask 0

; ------------- Prepare time of new last level (-> EDX:EAX)

AlarmTime6:	add	eax,ALARMLIST_DIF*(ALARMLIST_MAX-1) ; time of last list
		adc	edx,byte 0	; time of last list HIGH

; ------------- Split alarms in old last level

		add	cl,ALARMLIST_MAX-2 ; ECX <- old last level
AlarmTime7:	lea	esi,[AlarmListList+ecx*LIST_size] ; ESI<-last list head
		mov	edi,[esi+LIST_Prev] ; EDI <- previous alarm
		cmp	edi,esi		; is it valid alarm?
		je	short AlarmTime9 ; there is no other alarm

; ------------- Check if this alarm has valid time

		cmp	edx,[edi+ALARM_Expire+4] ; check alarm time HIGH
		jne	AlarmTime72	; HIGH not equal
		cmp	eax,[edi+ALARM_Expire] ; check alarm time LOW
AlarmTime72:	ja	short AlarmTime9 ; alarm time is OK

; ------------- Remove alarm from old list

		LISTDEL	edi,esi,ebx	; remove alarm from the list

; ------------- Clear alarm list mask if it was last alarm in the list

		cmp	esi,ebx		; was it last entry in the list?
		jne	short AlarmTime74 ; it was not last entry in the list
		xor	byte [AlarmListMask+7*4+3],B6 ; clear mask

; ------------- Add alarm into begin of new last list

AlarmTime74:	mov	ebx,ecx		; EBX <- current level
		inc	bl		; EBX <- next last level
		lea	ebx,[AlarmListList+ebx*LIST_size] ; EBX<-last list head
		LISTADD	ebx,edi,esi	; add alarm into last list
		or	byte [AlarmListMask+7*4+3],B7 ; set new mask
		jmp	short AlarmTime7 ; get next alarm

; ------------- Alarm not expired, set new next request

AlarmTime8:	mov	eax,[ebx+ALARM_Expire] ; EAX <- expiration time LOW
		sub	eax,esi		; EAX <- remaining time
		mov	[AlarmListNext],eax ; set remainging time

; ------------- Unlock alarm list

AlarmTime9:	ALARMUNLOCK		; unlock alarm list

; ------------- Pop registers

		popa			; pop all registers
		ret

; -----------------------------------------------------------------------------
;                                   Data
; -----------------------------------------------------------------------------

		DATA_SECTION

; ------------- Alarm list lock

		align	4, db 0
AlarmListLock:	SPINLOCK		; alarm list lock

; -----------------------------------------------------------------------------
;                            Uninitialized data
; -----------------------------------------------------------------------------

		BSS_SECTION

; ------------- Alarm list

		align	4, resb 1
AlarmListList:	resb	LIST_size*ALARMLIST_MAX ; lists of alarms
AlarmListBase:	resb	1		; base level of alarm list
		align	4, resb 1
AlarmListNext:	resd	1		; time to next service of alarm list
AlarmListStart:	resd	2		; time of current start of alarm list
AlarmListMask:	resd	ALARMLIST_MAX/32 ; mask of non-empty lists
AlarmListTotal:	resd	1		; total number of alarms

Back to source browser