ia32math.c

来自「Next BIOS Source code : Extensible Firmw」· C语言 代码 · 共 470 行

C
470
字号
/*++

Copyright (c)  1999 - 2002 Intel Corporation. All rights reserved
This software and associated documentation (if any) is furnished
under a license and may only be used or copied in accordance
with the terms of the license. Except as permitted by such
license, no part of this software or documentation may be
reproduced, stored in a retrieval system, or transmitted in any
form or by any means without the express written consent of
Intel Corporation.


Module Name:  

	Ia32math.c
  
Abstract:

  Generic math routines for EBC interpreter running on IA32 processor.
  
--*/  

#include "Efi.h"

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

--*/
{
  UINT64      Result;
  UINT32      Count;
  
  if (CountIn > 63) {
    return 0;
  }
  Count = (UINT32)CountIn;
  _asm {
      mov     eax, dword ptr Operand[0]
      mov     edx, dword ptr Operand[4]
      mov     ecx, Count
      shld    edx, eax, cl
      shl     eax, cl
      cmp     ecx, 32
      jc      short ls10
      mov     edx, eax
      xor     eax, eax
ls10:
      mov     dword ptr Result[0], eax
      mov     dword ptr Result[4], edx
  }
  return Result;
}

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


--*/
{
  UINT64      Result;
  UINT32      Count;
  
  if (CountIn > 63) {
    return 0;
  }
  Count = (UINT32)CountIn;
  
  _asm {
      mov     eax, dword ptr Operand[0]
      mov     edx, dword ptr Operand[4]
      mov     ecx, Count
      shrd    eax, edx, cl
      shr     edx, cl
      cmp     ecx, 32
      jc      short rs10
      mov     eax, edx
      xor     edx, edx
rs10:
      mov     dword ptr Result[0], eax
      mov     dword ptr Result[4], edx
  }

  return Result;
}


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:

  (INT64)Operand >> Count

--*/
{
  INT64   Result;
  UINT32  Count;
  //
  // If they exceeded the max shift count, then return either 0 or all F's
  // depending on the sign bit. Assume the compiler simplifies the << 63.
  //
  if (CountIn > 63) {
    if (Operand & (0x01 << 63)) {
      return (INT64)~0;
    }
    return 0;
  }
  Count = (UINT32)CountIn;
  _asm {
    mov  eax, dword ptr Operand[0]
    mov  edx, dword ptr Operand[4]
    mov  ecx, Count
//    and  ecx, 63 // ecx in {0, 1, ..., 63}

    shrd eax, edx, cl // edx undefined if cl > 31
    sar  edx, cl // shift right by cl & 0x1f, in {0, 1, ..., 31}

    cmp  ecx, 32
    jc   short ARShift64_done

    mov  eax, edx // if ecx >= 32, then eax = edx, and edx = sign bit
    sar  edx, 31 // fill in with sign bit; 31 - (cl & 0x1f) would be ebnough

ARShift64_done:
    mov     dword ptr Result[0], eax
    mov     dword ptr Result[4], edx
  }

  return (Result);
}


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

Note:

  The 128-bit result is the concatenation of *ResultHigh and the return value 
  The product fits in 64 bits if *ResultHigh == 0x0000000000000000

  Method 1. Use four 32-bit multiplications:
      a * b = 2^64 * a_h * b_h + 2^32 * (a_h * b_l + b_h * a_l) + a_l * b_l
  Method 2. An alternative using only three multiplications is:
      a * b = (2^64 + 2^32) * a_h * b_h + 
               2^32 * (a_h - a_l) * (b_h - b_l) + (2^32 + 1) * a_l * b_l
  The first method was implemented, because of the overhead in the second one

--*/
{
  UINT64  Result;
  UINT64  ResHi;
  UINT32  SavedEbx;
  
  _asm {
    mov dword ptr SavedEbx, ebx     // save across function calls
    mov eax, dword ptr Value1[0] // a_l -> eax
    mov ebx, dword ptr Value2[0] // b_l -> ebx
    mul ebx // a_lxb_l -> edx:eax
    mov dword ptr Result[0], eax // (a_lxb_l)_LOW -> LL; done LL
    mov ecx, edx // (a_lxb_l)_HIGH -> ecx
    mov eax, dword ptr Value1[4] // a_h -> eax (b_l is in ebx)
    mul ebx // a_hxb_l -> edx:eax
    add eax, ecx // (a_lxb_l)_HIGH + (a_hxb_l)_LOW -> CF:eax
    adc edx, 0x0 // (a_lxb_l)_HIGH + CF -> edx (no carry over, so CF = 0)
    mov dword ptr ResHi[0], edx // (a_hxb_l)_HIGH + CF -> HL; partial result
    mov ecx, eax // (a_lxb_l)_HIGH + (a_hxb_l)_LOW -> ecx
    mov eax, dword ptr Value1[0] // a_l -> eax
    mov ebx, dword ptr Value2[4] // b_h -> ebx
    mul ebx // b_hxa_l -> edx:eax
    add eax, ecx // (a_lxb_l)_HIGH + (a_hxb_l)_LOW + (b_hxa_l)_LOW -> CF:eax
    mov dword ptr Result[4], eax // eax -> LH; done LH
    adc edx, 0x0 // add CF to (b_hxa_l)_HIGH (no carry over, so CF = 0)
    mov ecx, edx // (b_hxa_l)_HIGH -> ecx
    mov eax, dword ptr Value1[4] // a_h -> eax (b_h is in ebx)
    mul ebx // a_hxb_h -> edx:eax
    add eax, ecx // (a_hxb_h)_LOW + (b_hxa_l)_HIGH -> CF:eax
    adc edx, 0x0 // (a_hxb_h)_HIGH + CF -> edx (no carry over, so CF = 0)
    add dword ptr ResHi[0], eax // HL; done HL
    adc edx, 0x0
    mov dword ptr ResHi[4], edx // HH; done HH
    mov ebx, dword ptr SavedEbx
  }

  *ResultHigh = ResHi;
  return (Result);

}


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

Note:

  The 128-bit result is the concatenation of *ResultHigh and the return value.
  The product fits in 64 bits if 
     (*ResultHigh == 0x0000000000000000 AND *ResultLow_bit63 == 0)
                                     OR
     (*ResultHigh == 0xffffffffffffffff AND *ResultLow_bit63 == 1)

  Method 1. Use four 32-bit multiplications:
      a * b = 2^64 * a_h * b_h + 2^32 * (a_h * b_l + b_h * a_l) + a_l * b_l
  Method 2. An alternative using only three multiplications is:
      a * b = (2^64 + 2^32) * a_h * b_h +
               2^32 * (a_h - a_l) * (b_h - b_l) + (2^32 + 1) * a_l * b_l
  The first method was implemented, because of the overhead in the second one

--*/
{
  INT64   Result;
  INT64   ResHi;
  UINT32  SavedEbx;
  UINT32  Sign = 0;

  _asm {
    mov dword ptr SavedEbx, ebx     // save across function calls
    mov ebx, dword ptr Value1[4] // a_h -> ebx
    bt  ebx, 0x1f // CF = 1 if a < 0
    jnc short MulS64xU64_a_positive
    //
    // a is negative
    //
    mov eax, dword ptr Value1[0] // a_l -> eax
    not ebx
    not eax
    add eax, 0x1
    adc ebx, 0x0
    mov dword ptr Value1[0], eax // eax -> a_l
    mov dword ptr Value1[4], ebx // ebx -> a_h
    btc dword ptr Sign[0], 0x0 // complement sign
MulS64xU64_a_positive:
    mov ebx, dword ptr Value2[4] // b_h -> ebx
    bt  ebx, 0x1f // CF = 1 if b < 0
    jnc short MulS64xU64_b_positive
    //
    // b is negative
    //
    mov eax, dword ptr Value2[0] // b_l -> eax
    not ebx
    not eax
    add eax, 0x1
    adc ebx, 0x0
    mov dword ptr Value2[0], eax // eax -> b_l
    mov dword ptr Value2[4], ebx // ebx -> b_h
    btc dword ptr Sign[0], 0x0 // complement sign
MulS64xU64_b_positive:
    //
    // multiply |a| and |b|
    //
    mov eax, dword ptr Value1[0] // a_l -> eax
    mov ebx, dword ptr Value2[0] // b_l -> ebx
    mul ebx // a_lxb_l -> edx:eax
    mov dword ptr Result[0], eax // (a_lxb_l)_LOW -> LL; done LL
    mov ecx, edx // (a_lxb_l)_HIGH -> ecx
    mov eax, dword ptr Value1[4] // a_h -> eax (b_l is in ebx)
    mul ebx // a_hxb_l -> edx:eax
    add eax, ecx // (a_lxb_l)_HIGH + (a_hxb_l)_LOW -> CF:eax
    adc edx, 0x0 // (a_lxb_l)_HIGH + CF -> edx (no carry over, so CF = 0)
    mov dword ptr ResHi[0], edx // (a_hxb_l)_HIGH + CF -> HL; partial result
    mov ecx, eax // (a_lxb_l)_HIGH + (a_hxb_l)_LOW -> ecx
    mov eax, dword ptr Value1[0] // a_l -> eax
    mov ebx, dword ptr Value2[4] // b_h -> ebx
    mul ebx // b_hxa_l -> edx:eax
    add eax, ecx // (a_lxb_l)_HIGH + (a_hxb_l)_LOW + (b_hxa_l)_LOW -> CF:eax
    mov dword ptr Result[4], eax // eax -> LH; done LH
    adc edx, 0x0 // add CF to (b_hxa_l)_HIGH (no carry over, so CF = 0)
    mov ecx, edx // (b_hxa_l)_HIGH -> ecx
    mov eax, dword ptr Value1[4] // a_h -> eax (b_h is in ebx)
    mul ebx // a_hxb_h -> edx:eax
    add eax, ecx // (a_hxb_h)_LOW + (b_hxa_l)_HIGH -> CF:eax
    adc edx, 0x0 // (a_hxb_h)_HIGH + CF -> edx (no carry over, so CF = 0)
    add dword ptr ResHi[0], eax // HL; done HL
    adc edx, 0x0
    mov dword ptr ResHi[4], edx // HH; done HH
    bt  dword ptr Sign[0], 0x0 // sign -> CF
    jnc short MulS64xU64_done
    mov eax, dword ptr Result[0] // LL
    mov ebx, dword ptr Result[4] // LH
    mov ecx, dword ptr ResHi[0] // HL
    mov edx, dword ptr ResHi[4] // HH
    not edx
    not ecx
    not ebx
    not eax
    add eax, 0x1
    adc ebx, 0x0
    adc ecx, 0x0
    adc edx, 0x0
    mov dword ptr Result[0], eax // LL
    mov dword ptr Result[4], ebx // LH
    mov dword ptr ResHi[0], ecx // HL
    mov dword ptr ResHi[4], edx // HH
MulS64xU64_done:
    mov ebx, dword ptr SavedEbx
  }

  *ResultHigh = ResHi;
  return (Result);

}

UINT64
DivU64x64 (
  IN  UINT64   Dividend,
  IN  UINT64   Divisor,
  OUT UINT64   *Remainder OPTIONAL,
  OUT UINT32   *Error
  )
{
  UINT64      Rem;
  UINT64      Bit;        

  *Error = 0;
  if (Divisor == 0) {
    *Error = 1;
    if (Remainder) {
      *Remainder = 0x8000000000000000;
    }
    return 0x8000000000000000;
  }

  //
  // For each bit in the dividend
  //

  Rem = 0;
  for (Bit=0; Bit < 64; Bit++) {
      _asm {
      shl     dword ptr Dividend[0], 1    ; shift dividend left one
      rcl     dword ptr Dividend[4], 1    
      rcl     dword ptr Rem[0], 1         ; shift rem left one
      rcl     dword ptr Rem[4], 1    
      }
      if (Rem >= Divisor) {
        Dividend |= 1;
        Rem -= Divisor;
      }
  }

  if (Remainder) {
    *Remainder = Rem;
  }

  return Dividend;
}


INT64
DivS64x64 (
  IN  INT64   Dividend,
  IN  INT64   Divisor,
  OUT INT64   *Remainder OPTIONAL,
  OUT UINT32  *Error
  )
{
  UINT64   Quotient;
  BOOLEAN  Negative;
  BOOLEAN  RemainderNegative;

  Negative = FALSE;
  RemainderNegative = FALSE;
  if (Dividend < 0) {
    Dividend = -Dividend;
    Negative = (BOOLEAN)!Negative;
    RemainderNegative = TRUE;
  }
  if (Divisor < 0) {
    Divisor = -Divisor;
    Negative = (BOOLEAN)!Negative;
  }
  Quotient = DivU64x64 ((UINT64)Dividend, (UINT64)Divisor, (INT64 *)Remainder, Error);
  if (*Error) {
    return (INT64)Quotient;
  }
  if (Negative) {
    Quotient = -((INT64)Quotient);
  }
  if (RemainderNegative) {
    if (Remainder) {
      *Remainder = -*Remainder;
    }
  }
  return (INT64)Quotient;
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?