How can I access the system time using NASM, on Linux?
(Editor's note: the accepted answer is for 16-bit DOS with direct hardware access; it would work inside DOSBox. The other answers are actually for Linux.)
How can I access the system time using NASM, on Linux?
(Editor's note: the accepted answer is for 16-bit DOS with direct hardware access; it would work inside DOSBox. The other answers are actually for Linux.)
Using 32-bit code under Linux:
mov eax, 13 ; call number = __NR_time
xor ebx, ebx ; tloc = NULL
int 0x80
; 32-bit time_t in EAX
This is a system call to time(2)
(system call number 13), and it returns the signed 32-bit time_t
in EAX.
(Unlike other system calls, return values >= -4095U (MAX_ERRNO) are still successes, and simply small negative numbers that represent times just before Jan 1, 1970. With a NULL pointer arg, time(2)
can't fail. See the man page for details.)
On bare metal (in a custom OS), or in a DOS program:
%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
mov al,0x02 ;Get minutes (00 to 59)
out RTCaddress,al
in al,RTCdata
mov [RTCtimeMinute],al
mov al,0x04 ;Get hours (see notes)
out RTCaddress,al
in al,RTCdata
mov [RTCtimeHour],al
mov al,0x07 ;Get day of month (01 to 31)
out RTCaddress,al
in al,RTCdata
mov [RTCtimeDay],al
mov al,0x08 ;Get month (01 to 12)
out RTCaddress,al
in al,RTCdata
mov [RTCtimeMonth],al
mov al,0x09 ;Get year (00 to 99)
out RTCaddress,al
in al,RTCdata
mov [RTCtimeYear],al
ret
This uses NASM, and is from here.
This will not work under a normal OS like Linux that stops user-space processes from directly accessing hardware. You could maybe get this to work as root, with an ioperm(2)
system call to allow access to that I/O port. Linux only updates the BIOS/hardware RTC to match the current system time during shutdown, not continuously, so don't expect it to be perfectly in sync, especially if the motherboard battery is dead.
With NASM, if you are targeting Linux x86-64, you can simply do the following:
mov rax, 201
xor rdi, rdi
syscall
201
corresponds to the 64-bit system call number for sys_time
(as listed here). Register rdi
is set to 0 so the return value after performing the system call is stored in rax
, but you could also make it point to a memory location of your choosing. The result is expressed in seconds since the Epoch.
More information about this system call can be found in the time man page.
As an addendum to the answer by Pyves above (using x86-64/NASM/Linux), if you want to get better clock resolution than a second, you can syscall with 228 instead of 201 to get seconds in one 64-bit variable and additional nanoseconds (beyond the seconds) in another 64-bit variable.
default rel
section .bss
time: resq 2 ; 2 qwords for seconds and nanoseconds
section .text
mov rax, 228 ; 228 is system call for sys_clock_gettime
xor edi, edi ; 0 for system clock (preferred over "mov rdi, 0")
lea rsi, [time]
syscall ; [time] contains number of seconds
; [time + 8] contains number of nanoseconds
From the man page, the system call is
int clock_gettime(clockid_t clock_id, struct timespec *tp);
struct timespec
on x86-64 is a pair of unsigned 64-bit integers, with seconds at the low address, nanos at the higher address.
I'd say, depending on what platform you're on, you'll have to use an OS function.
On windows, try GetSystemTime. On linux, try gettimeofday - see related question here.