;****************************************************************************
;*
;*                  SciTech OS Portability Manager Library
;*
;*  ========================================================================
;*
;*    The contents of this file are subject to the SciTech MGL Public
;*    License Version 1.0 (the "License"); you may not use this file
;*    except in compliance with the License. You may obtain a copy of
;*    the License at http://www.scitechsoft.com/mgl-license.txt
;*
;*    Software distributed under the License is distributed on an
;*    "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
;*    implied. See the License for the specific language governing
;*    rights and limitations under the License.
;*
;*    The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
;*
;*    The Initial Developer of the Original Code is SciTech Software, Inc.
;*    All Rights Reserved.
;*
;*  ========================================================================
;*
;* Language:    NASM
;* Environment: Intel 32 bit Protected Mode.
;*
;* Description: Code for 64-bit arhithmetic
;*
;****************************************************************************

include "scitech.mac"

header      _int64

begcodeseg  _int64                  ; Start of code segment

a_low       EQU 04h                 ; Access a_low directly on stack
a_high      EQU 08h                 ; Access a_high directly on stack
b_low       EQU 0Ch                 ; Access b_low directly on stack
shift       EQU 0Ch                 ; Access shift directly on stack
result_2    EQU 0Ch                 ; Access result directly on stack
b_high      EQU 10h                 ; Access b_high directly on stack
result_3    EQU 10h                 ; Access result directly on stack
result_4    EQU 14h                 ; Access result directly on stack

;----------------------------------------------------------------------------
; void _PM_add64(u32 a_low,u32 a_high,u32 b_low,u32 b_high,__u64 *result);
;----------------------------------------------------------------------------
; Adds two 64-bit numbers.
;----------------------------------------------------------------------------
cprocstart  _PM_add64

        mov     eax,[esp+a_low]
        add     eax,[esp+b_low]
        mov     edx,[esp+a_high]
        adc     edx,[esp+b_high]
        mov     ecx,[esp+result_4]
        mov     [ecx],eax
        mov     [ecx+4],edx
        ret

cprocend

;----------------------------------------------------------------------------
; void _PM_sub64(u32 a_low,u32 a_high,u32 b_low,u32 b_high,__u64 *result);
;----------------------------------------------------------------------------
; Subtracts two 64-bit numbers.
;----------------------------------------------------------------------------
cprocstart  _PM_sub64

        mov     eax,[esp+a_low]
        sub     eax,[esp+b_low]
        mov     edx,[esp+a_high]
        sbb     edx,[esp+b_high]
        mov     ecx,[esp+result_4]
        mov     [ecx],eax
        mov     [ecx+4],edx
        ret

cprocend

;----------------------------------------------------------------------------
; void _PM_mul64(u32 a_high,u32 a_low,u32 b_high,u32 b_low,__u64 *result);
;----------------------------------------------------------------------------
; Multiples two 64-bit numbers.
;----------------------------------------------------------------------------
cprocstart  _PM_mul64

        mov     eax,[esp+a_high]
        mov     ecx,[esp+b_high]
        or      ecx,eax
        mov     ecx,[esp+b_low]
        jnz     @@FullMultiply
        mov     eax,[esp+a_low]         ; EDX:EAX = b.low * a.low
        mul     ecx
        mov     ecx,[esp+result_4]
        mov     [ecx],eax
        mov     [ecx+4],edx
        ret

@@FullMultiply:
        push    ebx
        mul     ecx                     ; EDX:EAX = a.high * b.low
        mov     ebx,eax
        mov     eax,[esp+a_low+4]
        mul     [DWORD esp+b_high+4]    ; EDX:EAX = b.high * a.low
        add     ebx,eax
        mov     eax,[esp+a_low+4]
        mul     ecx                     ; EDX:EAX = a.low * b.low
        add     edx,ebx
        pop     ebx
        mov     ecx,[esp+result_4]
        mov     [ecx],eax
        mov     [ecx+4],edx
        ret

cprocend

;----------------------------------------------------------------------------
; void _PM_div64(u32 a_low,u32 a_high,u32 b_low,u32 b_high,__u64 *result);
;----------------------------------------------------------------------------
; Divides two 64-bit numbers.
;----------------------------------------------------------------------------
cprocstart  _PM_div64

        push    edi
        push    esi
        push    ebx
        xor     edi,edi
        mov     eax,[esp+a_high+0Ch]
        or      eax,eax
        jns     @@ANotNeg

; Dividend is negative, so negate it and save result for later

        inc     edi
        mov     edx,[esp+a_low+0Ch]
        neg     eax
        neg     edx
        sbb     eax,0
        mov     [esp+a_high+0Ch],eax
        mov     [esp+a_low+0Ch],edx

@@ANotNeg:
        mov     eax,[esp+b_high+0Ch]
        or      eax,eax
        jns     @@BNotNeg

; Divisor is negative, so negate it and save result for later

        inc     edi
        mov     edx,[esp+b_low+0Ch]
        neg     eax
        neg     edx
        sbb     eax,0
        mov     [esp+b_high+0Ch],eax
        mov     [esp+b_low+0Ch],edx

@@BNotNeg:
        or      eax,eax
        jnz     @@BHighNotZero

; b.high is zero, so handle this faster

        mov     ecx,[esp+b_low+0Ch]
        mov     eax,[esp+a_high+0Ch]
        xor     edx,edx
        div     ecx
        mov     ebx,eax
        mov     eax,[esp+a_low+0Ch]
        div     ecx
        mov     edx,ebx
        jmp     @@BHighZero

@@BHighNotZero:
        mov     ebx,eax
        mov     ecx,[esp+b_low+0Ch]
        mov     edx,[esp+a_high+0Ch]
        mov     eax,[esp+a_low+0Ch]

; Shift values right until b.high becomes zero

@@ShiftLoop:
        shr     ebx,1
        rcr     ecx,1
        shr     edx,1
        rcr     eax,1
        or      ebx,ebx
        jnz     @@ShiftLoop

; Now complete the divide process

        div     ecx
        mov     esi,eax
        mul     [DWORD esp+b_high+0Ch]
        mov     ecx,eax
        mov     eax,[esp+b_low+0Ch]
        mul     esi
        add     edx,ecx
        jb      @@8
        cmp     edx,[esp+a_high+0Ch]
        ja      @@8
        jb      @@9
        cmp     eax,[esp+a_low+0Ch]
        jbe     @@9
@@8:    dec     esi
@@9:    xor     edx,edx
        mov     eax,esi

@@BHighZero:
        dec     edi
        jnz     @@Done

; The result needs to be negated as either a or b was negative

        neg     edx
        neg     eax
        sbb     edx,0

@@Done: pop     ebx
        pop     esi
        pop     edi
        mov     ecx,[esp+result_4]
        mov     [ecx],eax
        mov     [ecx+4],edx
        ret

cprocend

;----------------------------------------------------------------------------
; __i64 _PM_shr64(u32 a_low,s32 a_high,s32 shift,__u64 *result);
;----------------------------------------------------------------------------
; Shift a 64-bit number right
;----------------------------------------------------------------------------
cprocstart  _PM_shr64

        mov     eax,[esp+a_low]
        mov     edx,[esp+a_high]
        mov     cl,[esp+shift]
        shrd    edx,eax,cl
        mov     ecx,[esp+result_3]
        mov     [ecx],eax
        mov     [ecx+4],edx
        ret

cprocend

;----------------------------------------------------------------------------
; __i64 _PM_sar64(u32 a_low,s32 a_high,s32 shift,__u64 *result);
;----------------------------------------------------------------------------
; Shift a 64-bit number right (signed)
;----------------------------------------------------------------------------
cprocstart  _PM_sar64

        mov     eax,[esp+a_low]
        mov     edx,[esp+a_high]
        mov     cl,[esp+shift]
        sar     edx,cl
        rcr     eax,cl
        mov     ecx,[esp+result_3]
        mov     [ecx],eax
        mov     [ecx+4],edx
        ret

cprocend

;----------------------------------------------------------------------------
; __i64 _PM_shl64(u32 a_low,s32 a_high,s32 shift,__u64 *result);
;----------------------------------------------------------------------------
; Shift a 64-bit number left
;----------------------------------------------------------------------------
cprocstart  _PM_shl64

        mov     eax,[esp+a_low]
        mov     edx,[esp+a_high]
        mov     cl,[esp+shift]
        shld    edx,eax,cl
        mov     ecx,[esp+result_3]
        mov     [ecx],eax
        mov     [ecx+4],edx
        ret

cprocend

;----------------------------------------------------------------------------
; __i64 _PM_neg64(u32 a_low,s32 a_high,__u64 *result);
;----------------------------------------------------------------------------
; Shift a 64-bit number left
;----------------------------------------------------------------------------
cprocstart  _PM_neg64

        mov     eax,[esp+a_low]
        mov     edx,[esp+a_high]
        neg     eax
        neg     edx
        sbb     eax,0
        mov     ecx,[esp+result_2]
        mov     [ecx],eax
        mov     [ecx+4],edx
        ret

cprocend


endcodeseg  _int64

        END
