  TITLE   Ia32math.asm: Generic math routines for EBC interpreter running on IA32 processor

;------------------------------------------------------------------------------
;
; Copyright (c) 2006, Intel Corporation                                                         
; All rights reserved. This program and the accompanying materials                          
; are licensed and made available under the terms and conditions of the BSD License         
; which accompanies this distribution.  The full text of the license may be found at        
; http://opensource.org/licenses/bsd-license.php                                            
;                                                                                           
; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             
; 
; Module Name:
;
;   Ia32math.asm
; 
; Abstract:
; 
;   Generic math routines for EBC interpreter running on IA32 processor
;
;------------------------------------------------------------------------------

  .686P
  .XMM
  .MODEL SMALL
  .CODE

LeftShiftU64  PROTO C  Operand:  QWORD, CountIn: QWORD
RightShiftU64 PROTO C  Operand:  QWORD, CountIn: QWORD
ARightShift64 PROTO C  Operand:  QWORD, CountIn: QWORD
MulU64x64     PROTO C  Value1:   QWORD, Value2:  QWORD, ResultHigh: DWORD
MulS64x64     PROTO C  Value1:   QWORD, Value2:  QWORD, ResultHigh: DWORD
DivU64x64     PROTO C  Dividend: QWORD, Divisor: QWORD, Remainder:  DWORD, Error: DWORD
DivS64x64     PROTO C  Dividend: QWORD, Divisor: QWORD, Remainder:  DWORD, Error: DWORD

  
LeftShiftU64 PROC C Operand: QWORD, CountIn: QWORD

;------------------------------------------------------------------------------
; UINT64
; LeftShiftU64 (
;   IN UINT64   Operand,
;   IN UINT64   CountIn
;   )
;
; Routine Description:
;  
;   Left-shift a 64-bit value.
;
; Arguments:
;
;   Operand - the value to shift
;   Count   - shift count
;
; Returns:
;
;   Operand << Count
;------------------------------------------------------------------------------

  push   ecx
  ;
  ; if (CountIn > 63) return 0;
  ;
  cmp    dword ptr CountIn[4], 0
  jne    _LeftShiftU64_Overflow
  mov    ecx, dword ptr CountIn[0]
  cmp    ecx, 63
  jbe    _LeftShiftU64_Calc
  
_LeftShiftU64_Overflow:
  xor    eax, eax
  xor    edx, edx
  jmp    _LeftShiftU64_Done
  
_LeftShiftU64_Calc:
  mov    eax, dword ptr Operand[0]
  mov    edx, dword ptr Operand[4]
  
  shld   edx, eax, cl
  shl    eax, cl
  cmp    ecx, 32
  jc     short _LeftShiftU64_Done
  
  mov    edx, eax
  xor    eax, eax
  
_LeftShiftU64_Done:
  pop    ecx
  ret

LeftShiftU64 ENDP


RightShiftU64 PROC C Operand: QWORD, CountIn: QWORD

;------------------------------------------------------------------------------
; UINT64
; RightShiftU64 (
;   IN UINT64   Operand,
;   IN UINT64   CountIn
;   )
;
; Routine Description:
;  
;   Right-shift an unsigned 64-bit value.
;
; Arguments:
;
;   Operand - the value to shift
;   Count   - shift count
;
; Returns:
;
;   Operand >> Count
;------------------------------------------------------------------------------

  push   ecx
  ;
  ; if (CountIn > 63) return 0;
  ;
  cmp    dword ptr CountIn[4], 0
  jne    _RightShiftU64_Overflow
  mov    ecx, dword ptr CountIn[0]
  cmp    ecx, 63
  jbe    _RightShiftU64_Calc
  
_RightShiftU64_Overflow:
  xor    eax, eax
  xor    edx, edx
  jmp    _RightShiftU64_Done
  
_RightShiftU64_Calc:
  mov    eax, dword ptr Operand[0]
  mov    edx, dword ptr Operand[4]
  
  shrd   eax, edx, cl
  shr    edx, cl
  cmp    ecx, 32
  jc     short _RightShiftU64_Done
  
  mov    eax, edx
  xor    edx, edx
  
_RightShiftU64_Done:
  pop    ecx
  ret

RightShiftU64 ENDP


ARightShift64 PROC C Operand: QWORD, CountIn: QWORD

;------------------------------------------------------------------------------
; INT64
; ARightShift64 (
;   IN INT64  Operand,
;   IN UINT64 CountIn
;   )
;
; Routine Description:
;  
;   Arithmatic shift a 64 bit signed value.
;
; Arguments:
;
;   Operand - the value to shift
;   Count   - shift count
;
; Returns:
;
;  Operand >> Count
;------------------------------------------------------------------------------

  push   ecx
  ;
  ; If they exceeded the max shift count, then return either 0 or all F's
  ; depending on the sign bit.
  ;
  cmp    dword ptr CountIn[4], 0
  jne    _ARightShiftU64_Overflow
  mov    ecx, dword ptr CountIn[0]
  cmp    ecx, 63
  jbe    _ARightShiftU64_Calc
  
_ARightShiftU64_Overflow:
  ;
  ; Check the sign bit of Operand
  ;
  bt     dword ptr Operand[4], 31
  jnc    _ARightShiftU64_Return_Zero
  ;
  ; return -1
  ;
  or     eax, 0FFFFFFFFh
  or     edx, 0FFFFFFFFh
  jmp    _ARightShiftU64_Done

_ARightShiftU64_Return_Zero:
  xor    eax, eax
  xor    edx, edx
  jmp    _ARightShiftU64_Done
  
_ARightShiftU64_Calc:
  mov    eax, dword ptr Operand[0]
  mov    edx, dword ptr Operand[4]

  shrd   eax, edx, cl
  sar    edx, cl
  cmp    ecx, 32
  jc     short _ARightShiftU64_Done

  ;
  ; if ecx >= 32, then eax = edx, and edx = sign bit
  ;
  mov    eax, edx
  sar    edx, 31

_ARightShiftU64_Done:
  pop    ecx
  ret

ARightShift64 ENDP


MulU64x64 PROC C  Value1: QWORD, Value2: QWORD, ResultHigh: DWORD

;------------------------------------------------------------------------------
; UINT64 
; MulU64x64 (
;   UINT64 Value1, 
;   UINT64 Value2, 
;   UINT64 *ResultHigh
;   )
;
; Routine Description:
; 
;   Multiply two unsigned 64-bit values.
;
; Arguments:
;
;   Value1      - first value to multiply
;   Value2      - value to multiply by Value1
;   ResultHigh  - result to flag overflows
;
; Returns:
; 
;   Value1 * Value2
;   The 128-bit result is the concatenation of *ResultHigh and the return value 
;------------------------------------------------------------------------------

  push   ebx
  push   ecx
  mov    ebx, ResultHigh           ; ebx points to the high 4 words of result
  ;
  ; The result consists of four double-words.
  ; Here we assume their names from low to high: dw0, dw1, dw2, dw3
  ;
  mov    eax, dword ptr Value1[0]
  mul    dword ptr Value2[0]
  push   eax                       ; eax contains final result of dw0, push it
  mov    ecx, edx                  ; ecx contains partial result of dw1
  
  mov    eax, dword ptr Value1[4]
  mul    dword ptr Value2[0]
  add    ecx, eax                  ; add eax to partial result of dw1
  adc    edx, 0    
  mov    dword ptr [ebx], edx      ; lower double-word of ResultHigh contains partial result of dw2
    
  mov    eax, dword ptr Value1[0]
  mul    dword ptr Value2[4]
  add    ecx, eax                  ; add eax to partial result of dw1
  push   ecx                      ; ecx contains final result of dw1, push it
  adc    edx, 0
  mov    ecx, edx                  ; ecx contains partial result of dw2, together with ResultHigh

  mov    eax, dword ptr Value1[4]
  mul    dword ptr Value2[4]
  add    ecx, eax                  ; add eax to partial result of dw2
  adc    edx, 0
  add    dword ptr [ebx], ecx      ; lower double-word of ResultHigh contains final result of dw2
  adc    edx, 0
  mov    dword ptr [ebx + 4], edx  ; high double-word of ResultHigh contains final result of dw3
    
  pop    edx                       ; edx contains the final result of dw1
  pop    eax                       ; edx contains the final result of dw0
  pop    ecx
  pop    ebx
  ret

MulU64x64 ENDP


MulS64x64  PROC C  Value1: QWORD, Value2: QWORD, ResultHigh: DWORD

;------------------------------------------------------------------------------
; INT64
; MulS64x64 (
;   INT64 Value1,
;   INT64 Value2,
;   INT64 *ResultHigh
;   )
;
; Routine Description:
;  
;   Multiply two signed 64-bit values.
;
; Arguments:
;
;   Value1      - first value to multiply
;   Value2      - value to multiply by Value1
;   ResultHigh  - result to flag overflows
;
; Returns:
;
;   Value1 * Value2
;   The 128-bit result is the concatenation of *ResultHigh and the return value 
;------------------------------------------------------------------------------

  push   ebx
  push   ecx
  mov    ebx, ResultHigh           ; ebx points to the high 4 words of result
  xor    ecx, ecx                 ; the lowest bit of ecx flags the sign
    
  mov    edx, dword ptr Value1[4]
  bt     edx, 31
  jnc    short _MulS64x64_A_Positive
  ;
  ; a is negative
  ;
  mov    eax, dword ptr Value1[0]
  not    edx
  not    eax
  add    eax, 1
  adc    edx, 0
  mov    dword ptr Value1[0], eax
  mov    dword ptr Value1[4], edx
  btc    ecx, 0
    
_MulS64x64_A_Positive:
  mov    edx, dword ptr Value2[4]
  bt     edx, 31
  jnc    short _MulS64x64_B_Positive
  ;
  ; b is negative
  ;
  mov    eax, dword ptr Value2[0]
  not    edx
  not    eax
  add    eax, 1
  adc    edx, 0
  mov    dword ptr Value2[0], eax
  mov    dword ptr Value2[4], edx
  btc    ecx, 0
    
_MulS64x64_B_Positive:
  invoke MulU64x64, Value1, Value2, ResultHigh
  bt     ecx, 0
  jnc    short _MulS64x64_Done
  ;
  ;negate the result
  ;
  not    eax
  not    edx
  not    dword ptr [ebx]
  not    dword ptr [ebx + 4]
  add    eax, 1
  adc    edx, 0
  adc    dword ptr [ebx], 0
  adc    dword ptr [ebx + 4], 0
    
_MulS64x64_Done:
  pop    ecx
  pop    ebx
  ret

MulS64x64 ENDP


DivU64x64 PROC C  Dividend: QWORD, Divisor: QWORD, Remainder: DWORD, Error: DWORD, 

;------------------------------------------------------------------------------
; UINT64
; DivU64x64 (
;   IN  UINT64   Dividend,
;   IN  UINT64   Divisor,
;   OUT UINT64   *Remainder OPTIONAL,
;   OUT UINT32   *Error
;   )
;
; Routine Description:
; 
;   This routine allows a 64 bit value to be divided with a 64 bit value returns 
;   64bit result and the Remainder
;
; Arguments:
;
;   Dividend    - dividend
;   Divisor     - divisor
;   ResultHigh  - result to flag overflows
;   Error       - flag for error
;
; Returns:
; 
;   Dividend / Divisor
;   Remainder = Dividend mod Divisor
;------------------------------------------------------------------------------

  push   ecx
  
  mov    eax, Error
  mov    dword ptr [eax], 0 
  
  cmp    dword ptr Divisor[0], 0
  jne    _DivU64x64_Valid
  cmp    dword ptr Divisor[4], 0
  jne    _DivU64x64_Valid
  ;
  ; the divisor is zero
  ;
  mov    dword ptr [eax], 1 
  cmp    Remainder, 0
  je     _DivU64x64_Invalid_Return
  ;
  ; fill the remainder if the pointer is not null
  ;
  mov    eax, Remainder
  mov    dword ptr [eax], 0
  mov    dword ptr [eax + 4], 80000000h
  
_DivU64x64_Invalid_Return:
  xor    eax, eax
  mov    edx, 80000000h
  jmp    _DivU64x64_Done

_DivU64x64_Valid:
  ;
  ; let edx and eax contain the intermediate result of remainder
  ;
  xor    edx, edx
  xor    eax, eax
  mov    ecx, 64
  
_DivU64x64_Wend:
  ;
  ; shift dividend left one
  ;
  shl    dword ptr Dividend[0], 1
  rcl    dword ptr Dividend[4], 1    
  ;
  ; rotate intermediate result of remainder left one
  ;
  rcl    eax, 1
  rcl    edx, 1 
  
  cmp    edx, dword ptr Divisor[4]
  ja     _DivU64x64_Sub_Divisor
  jb     _DivU64x64_Cont
  cmp    eax, dword ptr Divisor[0]
  jb     _DivU64x64_Cont
  
_DivU64x64_Sub_Divisor:
  ;
  ; If intermediate result of remainder is larger than
  ; or equal to divisor, then set the lowest bit of dividend,
  ; and subtract divisor from intermediate remainder
  ;
  bts    dword ptr Dividend[0], 0
  sub    eax, dword ptr Divisor[0]
  sbb    edx, dword ptr Divisor[4]
  
_DivU64x64_Cont:
  loop   _DivU64x64_Wend
  
  cmp    Remainder, 0
  je     _DivU64x64_Assign
  mov    ecx, Remainder
  mov    dword ptr [ecx], eax
  mov    dword ptr [ecx + 4], edx
 
_DivU64x64_Assign:
  mov    eax, dword ptr Dividend[0]
  mov    edx, dword ptr Dividend[4]
  
_DivU64x64_Done:
  pop    ecx
  ret
  
DivU64x64 ENDP

DivS64x64 PROC C  Dividend: QWORD, Divisor: QWORD, Remainder: DWORD, Error: DWORD, 

;------------------------------------------------------------------------------
; INT64
; DivU64x64 (
;   IN  INT64   Dividend,
;   IN  INT64   Divisor,
;   OUT UINT64   *Remainder OPTIONAL,
;   OUT UINT32   *Error
;   )
;
; Routine Description:
; 
;   This routine allows a 64 bit signed value to be divided with a 64 bit 
;   signed value returns 64bit result and the Remainder.
;
; Arguments:
;
;   Dividend    - dividend
;   Divisor     - divisor
;   ResultHigh  - result to flag overflows
;   Error       - flag for error
;
; Returns:
; 
;   Dividend / Divisor
;   Remainder = Dividend mod Divisor
;------------------------------------------------------------------------------

  push   ecx
  
  mov    eax, Error
  mov    dword ptr [eax], 0 
  
  cmp    dword ptr Divisor[0], 0
  jne    _DivS64x64_Valid
  cmp    dword ptr Divisor[4], 0
  jne    _DivS64x64_Valid
  ;
  ; the divisor is zero
  ;
  mov    dword ptr [eax], 1 
  cmp    Remainder, 0
  je     _DivS64x64_Invalid_Return
  ;
  ; fill the remainder if the pointer is not null
  ;
  mov    eax, Remainder
  mov    dword ptr [eax], 0
  mov    dword ptr [eax + 4], 80000000h
  
_DivS64x64_Invalid_Return:
  xor    eax, eax
  mov    edx, 80000000h
  jmp    _DivS64x64_Done

_DivS64x64_Valid:
  ;
  ; The lowest bit of ecx flags the sign of quotient,
  ; The seconde lowest bit flags the sign of remainder
  ;
  xor    ecx, ecx                 
  
  mov    edx, dword ptr Dividend[4]
  bt     edx, 31
  jnc    short _DivS64x64_Dividend_Positive
  ;
  ; dividend is negative
  ;
  mov    eax, dword ptr Dividend[0]
  not    edx
  not    eax
  add    eax, 1
  adc    edx, 0
  mov    dword ptr Dividend[0], eax
  mov    dword ptr Dividend[4], edx
  ;
  ; set both the flags for signs of quotient and remainder
  ;
  btc    ecx, 0
  btc    ecx, 1
    
_DivS64x64_Dividend_Positive:
  mov    edx, dword ptr Divisor[4]
  bt     edx, 31
  jnc    short _DivS64x64_Divisor_Positive
  ;
  ; divisor is negative
  ;
  mov    eax, dword ptr Divisor[0]
  not    edx
  not    eax
  add    eax, 1
  adc    edx, 0
  mov    dword ptr Divisor[0], eax
  mov    dword ptr Divisor[4], edx
  ;
  ; just complement the flag for sign of quotient
  ;
  btc    ecx, 0
    
_DivS64x64_Divisor_Positive:
  invoke DivU64x64, Dividend, Divisor, Remainder, Error
  bt     ecx, 0
  jnc    short _DivS64x64_Remainder
  ;
  ; negate the quotient
  ;
  not    eax
  not    edx
  add    eax, 1
  adc    edx, 0
    
_DivS64x64_Remainder:
  bt     ecx, 1
  jnc    short _DivS64x64_Done
  ;
  ; negate the remainder
  ;
  mov    ecx, remainder
  not    dword ptr [ecx]
  not    dword ptr [ecx + 4]
  add    dword ptr [ecx], 1
  adc    dword ptr [ecx + 4], 0
  
_DivS64x64_Done:
  pop    ecx
  ret

DivS64x64 ENDP

END