root/src/system/libroot/os/arch/x86/system_time_asm.S
/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 *
 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
 * Distributed under the terms of the NewOS License.
 */

#include <asm_defs.h>


/* saves the conversion factor needed for system_time */
.lcomm cv_factor                                4
.lcomm cv_factor_nsecs                  4
.lcomm cv_factor_nsecs_shift    1


.text


FUNCTION(__x86_setup_system_time):
        movl    4(%esp), %eax
        movl    %eax, cv_factor
        movl    8(%esp), %eax
        movl    %eax, cv_factor_nsecs
        movb    12(%esp), %al
        movb    %al, cv_factor_nsecs_shift
        ret
FUNCTION_END(__x86_setup_system_time)


/* int64 system_time(); */
FUNCTION(system_time):
        pushl   %ebx
        pushl   %ecx
        movl    cv_factor, %ebx

        /* load 64-bit factor into %eax (low), %edx (high) */
        rdtsc           /* time in %edx,%eax */

        movl    %edx, %ecx      /* save high half */
        mull    %ebx            /* truncate %eax, but keep %edx */
        movl    %ecx, %eax
        movl    %edx, %ecx      /* save high half of low */
        mull    %ebx /*, %eax*/
        /* now compute  [%edx, %eax] + [%ecx], propagating carry */
        subl    %ebx, %ebx      /* need zero to propagate carry */
        addl    %ecx, %eax
        adc             %ebx, %edx
        popl    %ecx
        popl    %ebx
        ret
FUNCTION_END(system_time)


/* int64 system_time_nsecs(); */
FUNCTION(system_time_nsecs):
        testb   $0, cv_factor_nsecs_shift
        jne             1f

        /* same algorithm as system_time(), just with a different factor */

        pushl   %ebx
        pushl   %ecx
        movl    cv_factor_nsecs, %ebx

        /* load 64-bit factor into %eax (low), %edx (high) */
        rdtsc           /* time in %edx,%eax */

        movl    %edx, %ecx      /* save high half */
        mull    %ebx            /* truncate %eax, but keep %edx */
        movl    %ecx, %eax
        movl    %edx, %ecx      /* save high half of low */
        mull    %ebx /*, %eax*/
        /* now compute  [%edx, %eax] + [%ecx], propagating carry */
        subl    %ebx, %ebx      /* need zero to propagate carry */
        addl    %ecx, %eax
        adc             %ebx, %edx
        popl    %ecx
        popl    %ebx
        ret

1:
        /* TSC frequency is less than 1 GHz -- we shift everything up 16 bit */

        pushl   %ebx
        pushl   %ecx
        pushl   %esi
        movl    cv_factor_nsecs, %ebx

        /* load 64-bit factor into %eax (low), %edx (high) */
        rdtsc           /* time in %edx,%eax */

        /* save high half */
        movl    %edx, %ecx

        /* multiply low half by conversion factor */
        mull    %ebx

        /* save result */
        movl    %eax, %esi      /* low half -> %esi */
        movl    %ecx, %eax
        movl    %edx, %ecx      /* high half -> %ecx */

        /* multiply high half by conversion factor */
        mull    %ebx

        /* now compute  [%edx, %eax] + [%ecx], propagating carry */
        xorl    %ebx, %ebx      /* need zero to propagate carry */
        addl    %ecx, %eax
        adc             %ebx, %edx

        /* shift the result left 16 bit */
        shl             $16, %edx
        movl    %eax, %ebx
        shr             $16, %ebx
        orw             %bx, %dx
        shl             $16, %eax

        /* add the high 16 bit of the low half of the low product */
        shr             $16, %esi
        orw             %si, %ax

        popl    %esi
        popl    %ecx
        popl    %ebx
        ret
FUNCTION_END(system_time_nsecs)