Author Topic: How to properly use timings? (Milliseconds on Linux without GCC)  (Read 11341 times)

Offline 0xDEADBEEF

  • Jr. Member
  • *
  • Posts: 7
Hello,

Whats the best way to use timings (ms) on Linux without the use of gcc?
For example when I want to make something like a sleep:

Code: [Select]
; sleep 2.5s (2500ms)
mov eax, ms_since_start     ; set eax to initial time (in ms)
add eax, 2500               ; add 2500 to it so now it contains the time when the sleep ends

sleep_start:
mov ebx, ms_since_start     ; set ebx to current cycle time
cmp eax, ebx                ; compare current cycle with end time in eax
jl sleep_start              ; go to sleep_start when end time is not reached yet

; sleep is over here

Is something like this possible? I guess it must be a system call?

Thanks :)

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: How to properly use timings? (Milliseconds on Linux without GCC)
« Reply #1 on: March 24, 2017, 12:46:46 AM »
Correct. There are several system calls involving time. Mostly(?) they're in nanoseconds rather than milliseconds. The conversion should be obvious...

Code: [Select]
global _start

; nasm -f elf32 delay.asm
; ld -o delay delay.o -m elf_i386

section .data
    msg db "read me quick!", 10
    msg_len equ $ - msg

section .text
_start:
    mov eax, 4 ; sys_write
    mov ebx, 1 ; stdout
    mov ecx, msg
    mov edx, msg_len
    int 80h
   
; put a time structure on the stack
    push 0 ; nanoseconds
    push 2 ; seconds
   
    mov eax, 162 ; sys_nanosleep
    mov ebx, esp ;point to our time structure (requested)
    mov ecx, esp ; " (remaining)
    int 80h
    add esp, 8 ; "free" our time structure
   
    mov eax, 1 ; sys_exit
    mov ebx, 0 ; pretend no error
    int 80h

I've got one using sys_gettimeofday which defines the structure a little more clearly. It does not work properly. The late Chuck Crayne, former moderator of news:comp.lang.asm.x86 and a mentor to some of us, tossed out a challenge/example to display the time "like C". I had something that I thought worked, though rather naive. I happened to try it one New Year's Eve. It informed me that it was some time on January zeroth. Oops! Before I got it fixed it was the first. I think I left it in a screwed up state. Been meaning to set my clock to New Year's Eve, or work on it some New Year's Eve. I can post it if you want, but it is known to be incorrect.

Best,
Frank


Offline 0xDEADBEEF

  • Jr. Member
  • *
  • Posts: 7
Re: How to properly use timings? (Milliseconds on Linux without GCC)
« Reply #2 on: March 27, 2017, 07:59:05 PM »
Oh sorry Frank, I totally forgot about the question. :o

Calling sys_nanosleep gives me the expected result of "sleeping", but what I wanted to do is to get the current time in ms/ns to calculate the difference in time between two operations. Sorry, my english is not very good sometimes I dont know really how to explain what I really want :D

I looked into sys_gettimeofday but couldnt manage to make it work really. Do you have some documentations where all (most) of the system calls are listed with the structures they take and which registers they use for in- and outputs?

Thanks again Frank, I hope you have a great day =)

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: How to properly use timings? (Milliseconds on Linux without GCC)
« Reply #3 on: March 27, 2017, 09:06:43 PM »
Ahhh... system call numbers are in unistd.h (one or another version of it). Descriptions are in section 2 of the man pages (but in C terms). The best reference in asm format that I know of is Jeff Owens' AsmRef:
https://sourceforge.net/p/asmref/wiki/Home/

I've lost track of Jeff - there may be a better version. There's something on github.

I'll post what I've got using sys_gettimeofday.  As explained above, it is known to not work on New year's eve. I think it's okay at other times(?). If you fix it, post the corrected version (if you're so inclined).

Code: [Select]
;------------------------------   
; nasm -f elf time7.asm
; ld -o time7 time7.o

global _start                 ; inform the linker

;----------------------------------------------
; some structures for "time stuff"

struc tv
    tv_sec resd 1
    tv_usec resd 1
endstruc

struc tz
    tz_minuteswest resd 1
    tz_dsttime resd 1          ; obsolete! do not use!
endstruc       

struc tm
    tm_sec resd 1              ; Seconds. [0-60] (1 leap second)
    tm_min resd 1              ; Minutes. [0-59]
    tm_hour resd 1             ; Hours. [0-23]
    tm_mday resd 1             ; Day. [1-31]
    tm_mon resd 1              ; Month. [0-11]
    tm_year resd 1             ; Year - 1900.
    tm_wday resd 1             ; Day of week. [0-6]
    tm_yday resd 1             ; Days in year.[0-365]
    tm_isdst resd 1            ; DST. [-1/0/1]
endstruc

;---------------------------------
section .data
    months_table:     ; number of days in each month
        db 1Fh,1Ch,1Fh,1Eh,1Fh,1Eh,1Fh,1Fh,1Eh,1Fh,1Eh,1Fh, 1Fh; more?

    mon_name3:        ; three-letter month names
        db 'JanFebMarAprMayJunJulAugSepOctNovDecJan'

    day_name3 db 'SunMonTueWedThuFriSat'
;-----------------------------------------------------


section .bss

    timeval resb tv_size
    timezone resb tz_size
   
    ctime_buffer resb 80

section .text

_start:

    mov eax, 78         ; __NR_gettimeofday
    mov ebx, timeval    ; fills in these structures
    mov ecx, timezone
    int 80h
    cmp eax, -4096
    jbe okay_time
    mov ebx, eax
    jmp error_exit

okay_time:   
    mov ecx, 60         ; minutes! that's so C!
    mov eax, [timezone + tz_minuteswest]
    mul ecx
    neg eax
    add eax, [timeval + tv_sec]

    mov edi, ctime_buffer
    call time2string

    mov edx, edi
    mov ecx, ctime_buffer
    sub edx, ecx
    mov ebx, 1
    mov eax, 4
    int 80h
   
exit:   
    xor ebx, ebx
error_exit:
    mov eax, 1        ; sys_exit
    int 80h
;---------------


;-------------------
time2string:
    sub esp, tm_size
    mov esi, esp

    mov ecx, 60
    xor edx, edx
    div ecx
    mov [esi + tm_sec], edx

    xor edx, edx
    div ecx
    mov [esi + tm_min], edx

    mov ecx, 24
    xor edx, edx
    div ecx
    mov [esi + tm_hour], edx

    push eax
    mov ecx, 7
    xor edx, edx
    div ecx
    add edx, byte 4
    cmp edx, ecx
    jb gooddow
    sub edx, ecx
gooddow:   
    mov [esi + tm_wday], edx
    pop eax

    mov ecx, 365
    xor edx, edx
    div ecx
    mov [esi + tm_year], eax
    add dword [esi + tm_year], 1970

                         ; leap year... kinda brain-dead
         ; should be okay for "current" dates
    test dword [esi + tm_year], 3
    jnz notly
    mov byte [months_table + 1], 1Dh  ; 29 days in february
notly:   
    shr eax, 2           ; how many leapyears in that?
    sub edx, eax
    mov [esi + tm_yday], edx

    xor ebx, ebx
findmonth:
    movzx eax, byte [months_table + ebx]
    sub edx, eax
;    js foundmonth
    jbe foundmonth
;jbe? this fails on new years eve!!!

    inc ebx
    jmp short findmonth
   
foundmonth:
    add edx, eax
    inc edx                           ; C is one-based
    mov [esi + tm_mday], edx   
    mov [esi + tm_mon], ebx           ; zero-based
    mov byte [months_table + 1], 1Ch  ; 28 days in february again
                                      ; (for repeat calls...)

    mov eax, [esi + tm_wday]

    mov eax, [day_name3 + eax + eax * 2]
    ; got four characters - only want 3, and a space
    and eax, 0FFFFFFh
    or eax, 20000000h
    stosd

    mov eax, [esi + tm_mon]

    mov eax, [mon_name3 + eax + eax * 2]
    and eax, 0FFFFFFh
    or eax, 20000000h
    stosd

    mov eax, [esi + tm_mday]
    call twodigits
    mov al, ' '
    stosb

    mov eax, [esi + tm_hour]
    call twodigits
    mov al, ':'
    stosb
    mov eax, [esi + tm_min]
    call twodigits
    mov al, ':'
    stosb
    mov eax, [esi + tm_sec]
    call twodigits

    mov al, ' '
    stosb

    mov eax, [esi + tm_year]
    mov ecx, 100             ; kludge to use "twodigits"
    xor edx, edx
    div ecx
    call twodigits
    mov al, dl
    call twodigits
   
    mov al, 10               ; newline - ctime() does one
    stosb           
    xor al, al
    stosb

    add esp, tm_size
    ret
;-------------------------


;-----------
; convert al to two ascii digits
; and stuff 'em in es:edi
;-----------
twodigits:
    aam
    add ax, 3030h
    xchg ah, al
    stosw
    ret
;--------------

Best,
Frank