unit time;

{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}

interface

const
 CLOCK_REALTIME         =0;
 CLOCK_VIRTUAL          =1;
 CLOCK_PROF             =2;
 CLOCK_MONOTONIC        =4;
 CLOCK_UPTIME           =5;  // FreeBSD-specific.
 CLOCK_UPTIME_PRECISE   =7;  // FreeBSD-specific.
 CLOCK_UPTIME_FAST      =8;  // FreeBSD-specific.
 CLOCK_REALTIME_PRECISE =9;  // FreeBSD-specific.
 CLOCK_REALTIME_FAST    =10; // FreeBSD-specific.
 CLOCK_MONOTONIC_PRECISE=11; // FreeBSD-specific.
 CLOCK_MONOTONIC_FAST   =12; // FreeBSD-specific.
 CLOCK_SECOND           =13; // FreeBSD-specific.
 CLOCK_THREAD_CPUTIME_ID=14;
 CLOCK_PROCTIME         =15; // ORBIS only
 CLOCK_EXT_NETWORK      =16; // ORBIS only
 CLOCK_EXT_DEBUG_NETWORK=17; // ORBIS only
 CLOCK_EXT_AD_NETWORK   =18; // ORBIS only
 CLOCK_EXT_RAW_NETWORK  =19; // ORBIS only

type
 Ptimespec=^timespec;
 timespec=packed record
  tv_sec :Int64;       /// seconds
  tv_nsec:Int64;       /// nanoseconds
 end;

 Ptimeval=^timeval;
 timeval=packed record
  tv_sec :Int64;
  tv_usec:Int64;   //microsecond
 end;

 Ptimezone=^timezone;
 timezone=packed record
  tz_minuteswest:Integer;
  tz_dsttime    :Integer;
 end;

 time_t=QWORD;
 ptime_t=^time_t;

 ptimesec=^timesec;
 timesec=packed record
  tz_time   :time_t;
  tz_secwest:DWORD;
  tz_dstsec :DWORD;
 end;

const
 tick=100;
 hz=10000000;

function _usec2msec(usec:QWORD):QWORD;  //Microsecond to Milisecond
function _msec2usec(msec:QWORD):QWORD;  //Milisecond  to Microsecond
function _usec2nsec(usec:QWORD):QWORD;  //Microsecond to Nanosecond
function _nsec2usec(nsec:QWORD):QWORD;  //Nanosecond  to Microsecond
function _msec2nsec(msec:QWORD):QWORD;  //Milisecond  to Nanosecond
function _nsec2msec(nsec:QWORD):QWORD;  //Nanosecond  to Milisecond

function  timespeccmp_lt(tvp,uvp:ptimespec):Integer; inline;

procedure TIMEVAL_TO_TIMESPEC(tv:ptimeval;ts:ptimespec); inline;
procedure TIMESPEC_TO_TIMEVAL(tv:ptimeval;ts:ptimespec); inline;

function  TIMESPEC_TO_UNIT(ts:ptimespec):Int64; inline; //Unit
function  TIMEVAL_TO_UNIT (tv:ptimeval ):Int64; inline; //Unit
function  USEC_TO_UNIT    (usec:QWORD  ):Int64; inline; //Unit

function  tvtohz(time:Int64):Int64; inline;
procedure usec2timespec(ts:ptimespec;timeo:DWORD);

procedure TIMESPEC_ADD(dst,src,val:ptimespec);
procedure TIMESPEC_SUB(dst,src,val:ptimespec);

function  itimerfix(tv:ptimeval):Integer;

function  clock_gettime(clock_id:Integer;tp:Ptimespec):Integer;
function  clock_getres(clock_id:Integer;tp:Ptimespec):Integer;

implementation

uses
 errno,
 trap,
 thr_error,
 kern_time;

function _usec2msec(usec:QWORD):QWORD;  //Microsecond to Milisecond
begin
 Result:=(usec+999) div 1000;
end;

function _msec2usec(msec:QWORD):QWORD;  //Milisecond to Microsecond
begin
 Result:=msec*1000;
end;

function _usec2nsec(usec:QWORD):QWORD;  //Microsecond to Nanosecond
begin
 Result:=usec*1000;
end;

function _nsec2usec(nsec:QWORD):QWORD;  //Nanosecond to Microsecond
begin
 Result:=(nsec+999) div 1000;
end;

function _msec2nsec(msec:QWORD):QWORD;  //Milisecond to Nanosecond
begin
 Result:=msec*1000000;
end;

function _nsec2msec(nsec:QWORD):QWORD;  //Nanosecond to Milisecond
begin
 Result:=(nsec+999999) div 1000000;
end;

function timespeccmp_lt(tvp,uvp:ptimespec):Integer; inline;
begin
 if (tvp^.tv_sec=uvp^.tv_sec) then
 begin
  Result:=ord(tvp^.tv_nsec < uvp^.tv_nsec);
 end else
 begin
  Result:=ord(tvp^.tv_sec < uvp^.tv_sec);
 end;
end;

procedure TIMEVAL_TO_TIMESPEC(tv:ptimeval;ts:ptimespec); inline;
begin
 ts^.tv_sec :=tv^.tv_sec;
 ts^.tv_nsec:=tv^.tv_usec * 1000;
end;

procedure TIMESPEC_TO_TIMEVAL(tv:ptimeval;ts:ptimespec); inline;
begin
 tv^.tv_sec :=ts^.tv_sec;
 tv^.tv_usec:=ts^.tv_nsec div 1000;
end;

function TIMESPEC_TO_UNIT(ts:ptimespec):Int64; inline; //Unit
begin
 Result:=(QWORD(ts^.tv_sec)*10000000)+(QWORD(ts^.tv_nsec) div 100);
end;

function TIMEVAL_TO_UNIT(tv:ptimeval):Int64; inline; //Unit
begin
 Result:=(QWORD(tv^.tv_sec)*10000000)+(QWORD(tv^.tv_usec)*10);
end;

function USEC_TO_UNIT(usec:QWORD):Int64; inline; //Unit
begin
 Result:=(usec*10);
end;

function tvtohz(time:Int64):Int64; inline;
begin
 Result:=time;
end;

procedure usec2timespec(ts:ptimespec;timeo:DWORD);
begin
 ts^.tv_sec :=(timeo div 1000000);
 ts^.tv_nsec:=(timeo mod 1000000)*1000;
end;

procedure TIMESPEC_ADD(dst,src,val:ptimespec);
begin
 dst^.tv_sec :=src^.tv_sec +val^.tv_sec;
 dst^.tv_nsec:=src^.tv_nsec+val^.tv_nsec;
 if (dst^.tv_nsec>=1000000000) then
 begin
  Inc(dst^.tv_sec);
  Dec(dst^.tv_nsec,1000000000);
 end;
end;

procedure TIMESPEC_SUB(dst,src,val:ptimespec);
begin
 dst^.tv_sec :=src^.tv_sec -val^.tv_sec;
 dst^.tv_nsec:=src^.tv_nsec-val^.tv_nsec;
 if (dst^.tv_nsec<0) then
 begin
  Dec(dst^.tv_sec);
  Inc(dst^.tv_nsec,1000000000);
 end;
end;

{
 * Check that a proposed value to load into the .it_value or
 * .it_interval part of an interval timer is acceptable, and
 * fix it to have at least minimal value (i.e. if it is less
 * than the resolution of the clock, round it up.)
 }
function itimerfix(tv:ptimeval):Integer;
begin
 if (tv^.tv_sec < 0) or (tv^.tv_usec < 0) or (tv^.tv_usec >= 1000000) then
  Exit(EINVAL);
 if (tv^.tv_sec=0) and (tv^.tv_usec<>0) and (tv^.tv_usec < tick) then
  tv^.tv_usec:=tick;
 Exit(0);
end;

function clock_gettime(clock_id:Integer;tp:Ptimespec):Integer; assembler; nostackframe;
asm
 movq  sys_clock_gettime,%rax
 call  fast_syscall
 jmp   cerror
end;

function clock_getres(clock_id:Integer;tp:Ptimespec):Integer; assembler; nostackframe;
asm
 movq  sys_clock_getres,%rax
 call  fast_syscall
 jmp   cerror
end;

end.

