//
//  FloatingPointUtilities.h
//  AppUtility
//
//  Created by Bob Stuller on 9/18/11.
//  Copyright 2011 Nuance Communications, Inc. All rights reserved.
//

#ifndef AppUtility_FloatingPointUtilities_h
#define AppUtility_FloatingPointUtilities_h

#ifdef __cplusplus

#import <Carbon/Carbon.h>

typedef UInt16 uns16;
typedef UInt32 uns32;

typedef uns16 FpuControlStateType;
typedef uns32 FpuSSE2ControlStateType;


#define FPU_IEEE    0x037f

///////////////////////////////////////////////////////////////////////////
// Functions to get/set FPU control state.  Don't use directly outside this
// module.

// Irritating subtlety when setting control state:  the Pentium line has a
// very strange way of dealing with FP exceptions.  The offending
// instruction does not raise an exception, it merely sets a flag in the HW
// status word recording that an exception *should* be raised.  This flag
// isn't looked at until an attempt is made to execute *another* FP
// instruction.  In addition, the FLDCW instruction (which loads a new
// control word value) looks at all the exception bits (which are sticky),
// and sets the "should raise an exception" flag if the new control word
// value unmasks any of the exceptions whose sticky bits happen to be set.
//
// So, if (say) a divide-by-0 (DBZ) occurs in user code when DBZ is masked,
// nothing happens other than that the DBZ sticky flag gets set.  If the
// user, some billions of cycles later, happens to call an SDAPI function:
// 1. We load a new control word value that enables DBZ.
// 2. As a side-effect, the "should raise an exception" flag gets set.
// 3. The *next* time an FP instruction is executed, the DBZ exception
//    finally gets raised.
// Now most SDAPI functions don't execute any FP code, so "the next FP
// instruction" is likely in this file, namely the FLDCW that restores the
// user's original control word at the end of the SDAPI call.  This makes it
// look like the DBZ happened inside the SDAPI.
// To avoid this, whenever we load a new control word value, we force any
// newly-enabled pending exceptions to get raised (the Pentium FWAIT
// instruction suffices for this).  Then at least the DBZ gets raised upon
// entering the SDAPI.
//
// The best solution is for the user to enable FP exceptions in their own
// code.  Our alternative approach is to issue an fwait with the old
// exception bits, so that any pending exception gets reaised, then to clear
// all pending FP exceptions (zero out the sticky bits) before loading the
// new control word.
//
// A pretty good reference for the x87 control bits is:
// http://webster.cs.ucr.edu/AoA/Windows/HTML/RealArithmetic.html
//
// A pretty good reference for the SSE control bits is:
// http://softpixel.com/~cwright/programming/simd/sse.php

inline FpuControlStateType fpuGetControlState();
inline FpuSSE2ControlStateType fpuGetSSE2ControlState();
inline void fpuSetControlStateBoth(const FpuControlStateType newState,
				   const FpuSSE2ControlStateType newSSE2State);
inline void fpuSetControlState(const FpuControlStateType newState);

///////////////////////////////////////////////////////////////////////////
// Class-based interfaces.  Use these to control FPU state in a safe
// (scoped save/restore) manner.

// FpuSaveRestore is a base class for the FpuUseXXX classes, supplying
// save/restore semantics at construction/destruction time.  It's probably
// not useful except as a base class for this modulue's other classes.

class FpuSaveRestore
{
public:
	// constructor remembers the current FPU state in a member
    FpuSaveRestore();

    // destructor restores the FPU state remembered by the
    // constructor
    ~FpuSaveRestore();

protected:
    FpuControlStateType mOrigFpuControlState;
    FpuSSE2ControlStateType mOrigSSE2FpuControlState;
	
private:
    // prevent copying
    FpuSaveRestore(const FpuSaveRestore&);
    FpuSaveRestore& operator=(const FpuSaveRestore&);
};

// Constructing an FpuUseIEEE object sets the FPU to use
// the hardware default FPU state.  The
// FPU state in effect immediately before construction is restored
// when the object is destructed.
class FpuUseIEEE: public FpuSaveRestore
{
public:
    FpuUseIEEE();

private:
    // prevent copying
    FpuUseIEEE(const FpuUseIEEE&);
    FpuUseIEEE& operator=(const FpuUseIEEE&);
};

#endif	// __cplusplus
#endif // AppUtility_FloatingPointUtilities_h
