/*
 * COPYRIGHT:         See COPYING in the top level directory
 * PROJECT:           ReactOS kernel
 * PURPOSE:           Run-Time Library
 * FILE:              lib/rtl/i386/pow.S
 * PROGRAMER:         Alex Ionescu (alex@relsoft.net)
 *                    Eric Kohl (ekohl@rz-online.de)
 *
 * Copyright (C) 2002 Michael Ringgaard.
 * All rights reserved. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.  
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.  
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission. 

 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES// LOSS OF USE, DATA, OR PROFITS// OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE.
 */
 
.globl _pow
 
 /* DATA ********************************************************************/

fzero:
        .long   0                       // Floating point zero
        .long   0                       // Floating point zero

.intel_syntax noprefix

/* FUNCTIONS ***************************************************************/

_pow:
        push    ebp
        mov     ebp,esp
        sub     esp,12                  // Allocate temporary space
        push    edi                     // Save register edi
        push    eax                     // Save register eax
        mov     dword ptr [ebp-12],0    // Set negation flag to zero
        fld     qword ptr [ebp+16]      // Load real from stack
        fld     qword ptr [ebp+8]       // Load real from stack
        mov     edi,offset flat:fzero   // Point to real zero
        fcom    qword ptr [edi]         // Compare x with zero
        fstsw   ax                      // Get the FPU status word
        mov     al,ah                   // Move condition flags to AL
        lahf                            // Load Flags into AH
        and     al,    0b01000101       // Isolate  C0, C2 and C3
        and     ah,    0b10111010       // Turn off CF, PF and ZF
        or      ah,al                   // Set new  CF, PF and ZF
        sahf                            // Store AH into Flags
        jb      __fpow1                 // Re-direct if x < 0
        ja      __fpow3                 // Re-direct if x > 0
        fxch                            // Swap st, st(1)
        fcom    qword ptr [edi]         // Compare y with zero
        fxch                            // Restore x as top of stack
        fstsw   ax                      // Get the FPU status word
        mov     al,ah                   // Move condition flags to AL
        lahf                            // Load Flags into AH
        and     al,    0b01000101       // Isolate  C0, C2 and C3
        and     ah,    0b10111010       // Turn off CF, PF and ZF
        or      ah,al                   // Set new  CF, PF and ZF
        sahf                            // Store AH into Flags
        ja      __fpow3                 // Re-direct if y > 0
        fstp    st(1)                   // Set new stack top and pop
        mov     eax,1                   // Set domain error (EDOM)
        jmp     __fpow5                 // End of case
__fpow1:        fxch                            // Put y on top of stack
        fld    st                       // Duplicate y as st(1)
        frndint                         // Round to integer
        fxch                            // Put y on top of stack
        fcomp                           // y = int(y) ?
        fstsw   ax                      // Get the FPU status word
        mov     al,ah                   // Move condition flags to AL
        lahf                            // Load Flags into AH
        and     al,    0b01000101       // Isolate  C0, C2 and C3
        and     ah,    0b10111010       // Turn off CF, PF and ZF
        or      ah,al                   // Set new  CF, PF and ZF
        sahf                            // Store AH into Flags
        je      __fpow2                 // Proceed if y = int(y)
        fstp    st(1)                   // Set new stack top and pop
        fldz                            // Set result to zero
        fstp    st(1)                   // Set new stack top and pop
        mov     eax,1                   // Set domain error (EDOM)
        jmp     __fpow5                 // End of case
__fpow2:        fist    dword ptr [ebp-12]      // Store y as integer
        and     dword ptr [ebp-12],1    // Set bit if y is odd
        fxch                            // Put x on top of stack
        fabs                            // x = |x|
__fpow3:        fldln2                          // Load log base e of 2
        fxch    st(1)                   // Exchange st, st(1)
        fyl2x                           // Compute the natural log(x)
        fmulp                           // Compute y * ln(x)
        fldl2e                          // Load log base 2(e)
        fmulp   st(1),st                // Multiply x * log base 2(e)
        fst     st(1)                   // Push result
        frndint                         // Round to integer
        fsub    st(1),st                // Subtract
        fxch                            // Exchange st, st(1)
        f2xm1                           // Compute 2 to the (x - 1)
        fld1                            // Load real number 1
        faddp                           // 2 to the x
        fscale                          // Scale by power of 2
        fstp    st(1)                   // Set new stack top and pop
        test    dword ptr [ebp-12],1    // Negation required ?
        jz      __fpow4                 // No, re-direct
        fchs                            // Negate the result
__fpow4:        fstp    qword ptr [ebp-8]       // Save (double)pow(x, y)
        fld     qword ptr [ebp-8]       // Load (double)pow(x, y)
        fxam                            // Examine st
        fstsw   ax                      // Get the FPU status word
        cmp     ah,5                    // Infinity ?
        jne     __fpow6                 // No, end of case
        mov     eax,2                   // Set range error (ERANGE)
                                        // Get errno pointer offset
__fpow5:        int     3
        mov     edi,0                   // TODO: offset flat:__crt_errno
        mov     edi,[edi]               // Get C errno variable pointer
        mov     dword ptr [edi],eax     // Set errno
__fpow6:        pop     eax                     // Restore register eax
        pop     edi                     // Restore register edi
        mov     esp,ebp                 // Deallocate temporary space
        pop     ebp
        ret
