__sys_time_slice = 10 ;[ms]


uglobal
  if used TimerCreate
    __InternalTimerID     dd ?
    __InternalTimerThread dd ?
  end if
endg



if used TimerCreate

initialize InitLinuxTimers
  .action lnx_sigaction
  .timer lnx_sigevent
  .time  lnx_itimerspec
begin
; set the signal handler.
        mov     eax, sys_sigaction
        mov     ebx, SIGALRM

        lea     ecx, [.action]
        mov     [ecx+lnx_sigaction.sa_handler], __TimerProc
        mov     [ecx+lnx_sigaction.sa_mask], 0
        mov     [ecx+lnx_sigaction.sa_flags], 0
        xor     edx, edx

        int $80

; create the interval timer
        mov     eax, sys_timer_create
        mov     ebx, CLOCK_REALTIME

        lea     ecx, [.timer]
        mov     [ecx+lnx_sigevent.notify], SIGEV_SIGNAL
        mov     [ecx+lnx_sigevent.signo], SIGALRM
        mov     [ecx+lnx_sigevent.value], 0             ; not used but must be set - the function fails without it...
        mov     edx, __InternalTimerID

        int $80
;        stdcall OutputRegister, regEAX, 16
; then start a thread that will process timer expiration events.

        stdcall ThreadCreate, __TimerHandler, 0
        mov     [__InternalTimerThread], eax

; start the timer
        mov     ebx, [__InternalTimerID]
        mov     eax, sys_timer_settime
        lea     edx, [.time]
        mov     [edx+lnx_itimerspec.it_interval.tv_sec], 0
        mov     [edx+lnx_itimerspec.it_interval.tv_nsec], __sys_time_slice*1000000
        mov     [edx+lnx_itimerspec.it_value.tv_sec], 0
        mov     [edx+lnx_itimerspec.it_value.tv_nsec], __sys_time_slice*1000000
        xor     esi, esi

        int $80
;        stdcall OutputRegister, regEAX, 16
        return
endp



finalize FreeLinuxTimers
begin
; stop the timer
        mov     eax, sys_timer_delete
        mov     ebx, [__InternalTimerID]
        int $80

; then stop the handling thread
        mov     eax, sys_kill
        mov     ebx, [__InternalTimerThread]
        mov     ecx, SIGTERM
        int $80

        return
endp

end if


; This procedure is called by the system on every time quantum.
;

proc __TimerProc, .signal
begin
        push    ebx esi edi

        mov     eax, sys_signal


        lea     eax, [__ptrFirstTimer]
        xor     edi, edi

.loop:
        mov     eax, [eax+TTimer.next]
        test    eax, eax
        jz      .end_timers

        test    [eax+TTimer.flags], tmfRunning
        jz      .loop

        mov     ecx, [eax+TTimer.value]
        add     ecx, __sys_time_slice
        mov     [eax+TTimer.value], ecx
        cmp     ecx, [eax+TTimer.interval]
        jl     .loop

; call event handler
        cmp     [eax+TTimer.Callback], 0
        je      .handlerok

        push    eax

        mov     ecx, [eax+TTimer.flags]
        and     ecx, $0f
        cmp     eax, tmfDoNothing
        je      .end_event

; run the timer event in separate thread:
        inc     [eax+TTimer.Expired]
        inc     edi

.end_event:
        pop     eax
        test    [eax+TTimer.flags], tmfSyncDestroy
        jz      .handlerok

        stdcall TimerDestroy, eax

.handlerok:
; jump to loop
        mov     ecx, [eax+TTimer.interval]
        sub     [eax+TTimer.value], ecx
        jmp     .loop

.end_timers:

        test    edi, edi
        jz      @f

        mov     eax, sys_kill
        mov     ebx, [__InternalTimerThread]
        mov     ecx, SIGCONT
        int $80

@@:
        pop     edi esi ebx
        xor     eax, eax
        cret
endp




proc __TimerHandler, .dummy
.remain dd ?
.action lnx_sigaction
begin

        mov     eax, sys_sigaction
        mov     ebx, SIGCONT
        lea     ecx, [.action]
        mov     [ecx+lnx_sigaction.sa_handler], __dummy_proc
        mov     [ecx+lnx_sigaction.sa_mask], 0
        mov     [ecx+lnx_sigaction.sa_flags], SA_NODEFER
        xor     edx, edx
        int $80

.from_begin:

        mov     [.remain], 0
        lea     eax, [__ptrFirstTimer]

.listloop:
        mov     eax, [eax+TTimer.next]

.listloop2:
        test    eax, eax
        jz      .end_list

        mov     ecx, [eax+TTimer.Expired]
        jecxz   .listloop

        dec     ecx
        mov     [eax+TTimer.Expired], ecx
        add     [.remain], ecx

        cmp     [eax+TTimer.Callback], 0
        je      .listloop

        mov     ecx, [eax+TTimer.flags]
        and     ecx, $0f
        cmp     ecx, tmfDoNothing
        je      .end_event

        cmp     ecx, tmfFireEvent
        je      .end_event            ;.fire_event ; still not supported

; call the callback procedure.
        pushad
        stdcall [eax+TTimer.Callback], eax
        popad

.end_event:
        test    [eax+TTimer.flags], tmfSyncDestroy
        jz      .listloop

        push    eax
        mov     eax, [eax+TTimer.next]  ; after the destruction, this pointer will be lost.
        stdcall TimerDestroy ; pointer from the stack.
        jmp     .listloop2


.end_list:
        cmp     [.remain], 0
        jne     .from_begin

        mov     eax, sys_pause
        int $80

        jmp     .from_begin
endp

proc __dummy_proc, .dummy
begin
        cret
endp