Karol Czaradzki
Karol Czaradzki

Reputation: 389

How to get system time with NASM assembly in Linux without using functions exported from C?

I am working on a project for my university. The task is to print current date and time. I success fully managed to create a subroutine that prints numbers, all I need now is to get date. I tried this approach:

%define RTCaddress  0x70
%define RTCdata     0x71

;Get time and date from RTC
.l1:    mov al,10           ;Get RTC register A
        out RTCaddress,al
        in al,RTCdata
        test al,0x80            ;Is update in progress?
        jne .l1             ; yes, wait

        mov al,0            ;Get seconds (00 to 59)
        out RTCaddress,al
        in al,RTCdata
        mov [RTCtimeSecond],al

But just calling:

.l1:    mov al,10           ;Get RTC register A
        out RTCaddress,al

Is enought to get a crash. Do you have any idea how to fix this approach or is there any different I could use. I am working with Nasm on Linux 64bit.

Upvotes: 2

Views: 5679

Answers (1)

Michael Petch
Michael Petch

Reputation: 47573

As Jester pointed out Jpowel answered a similar question. JPowel provided an answer that works for 32-bit and 64-bit. The compatibility (on 64-bit Linux) of calling through int 0x80 has a small amount of additional overhead associated with it. The 64-bit specific method is to use the 64-bit instruction SYSCALL to the sys_time kernel routine. Linux sys_time manpage is here. It defines sys_time as:

SYNOPSIS

  #include <time.h>

  time_t time(time_t *t);

DESCRIPTION

  time() returns the time as the number of seconds since the Epoch,
  1970-01-01 00:00:00 +0000 (UTC).

  If t is non-NULL, the return value is also stored in the memory
  pointed to by t.

A good resource for the 64-bit Linux syscallcan be found on Ryan Chapmans blog. This information can be useful for setting up the sys_time call using 64-Bit System V ABI kernel calling convention. His table has:

%rax  System call    %rdi
---------------------------------
201   sys_time       time_t *tloc

This code provides an example of calling sys_time with a pointer to a time_t type (long long) and shows the second method of passing 0 as the first parameter which simply returns the time in rax.

        section .data
time:   dq      0

        global  _start
        section .text

_start:
        ; Method 1: pass a pointer to a time_t (long long) to return time in
        ; sys_time = syscall(201, *long long)

        mov     rax, 201         ; system call 0xC9(201) = sys_time
        mov     rdi, time        ; address of long long (qword) to return
                                 ;     a time_t val
        syscall                  ; make Linux system call

        ; Method 2: pass zero as the time_t parameter. sys_time returns
        ; value in rax .
        ; sys_time = syscall(201, *long long = 0)

        mov     rax, 201         ; system call 0xC9(201) = sys_time
        xor     rdi, rdi         ; address of long long (qword) set to zero
        syscall                  ; make Linux system call

        ; Do something useful with time
        ; Seconds since 1970-01-01 00:00:00 +0000 (UTC)

        ; Exit with the time
        mov     rdi, rax         ; return least sig byte of time as return code
        mov     rax, 60          ; system call 0x3C(60) is exit
        syscall                  ; make Linux system call

This Stackoverflow answer suggests a reasonable implementation in C based on the source code for a C library gmtimefunction. It wouldn't be difficult to convert it to X86_64 assembler. The code is pretty straight forward. With such a function breaking up the time into individual components for formatting wouldn't be difficult at all.

Upvotes: 5

Related Questions