// genFloat.cc -- Float method prologue generation		-*- C++ -*-
// 
// Author: Ian.Piumarta@INRIA.Fr
//
// Last edited: Thu Jan 13 00:16:52 2000 by piumarta (Ian Piumarta) on pingu

#include "Object.h"

#include "archdep.h"
#include "machine.h"

#include ARCHDEP(emit.h)

#ifdef __i386__
/* word-based copy with swapping for non-PowerPC order */
inline double swapFloat(double fl) {
  double tmpf= 0.0;
  *((int *) (&tmpf) + 0) = *((int *) &fl + 1); 
  *((int *) (&tmpf) + 1) = *((int *) &fl + 0);
  return tmpf;
}
#else
#define swapFloat(f) f
#endif

class Float : public Object
{
  word	ieeeFloat[2];
public:
  // ultra-fast allocator for BlockClosures
  inline static Float *allocateEmpty(void)
    {
      register Float *f= (Float *)allocateChunk(sizeof(Float));
      // ASSMUE: header type short
      f->_bh= floatHeader | ((newHash() & 0xfff) << 17);
      return f;
    }

  inline double &value(void)
    {
      return *(double *)ieeeFloat;
    }

  static oop asFloat(oop rcvr)
    {
      assert(rcvr->isInteger());
      register Float *ans= allocateEmpty();
      ans->value()= swapFloat((double)rcvr->integerValue());
      return ans;
    }

# define defop(OP, NAME)			\
  static oop NAME(Float *rcv, Float *arg)	\
    {						\
      register double frcv= swapFloat(rcv->value());	\
      frcv OP##= swapFloat(arg->value());			\
      register Float *ans= allocateEmpty();	\
      ans->value()= swapFloat(frcv);    \
      return ans;				\
    }

  // BOGUELETTE : THESE COULD BE INLINED!!!
# define defrel(OP, NAME)			\
  static oop NAME(Float *rcv, Float *arg)	\
    {						\
      return (swapFloat(rcv->value()) OP swapFloat(arg->value()))	\
        ? trueObj				\
        : falseObj;				\
    }

  defop(  +, add);
  defop(  -, subtract);
  defrel( <, less);
  defrel( >, greater);
  defrel(<=, lessOrEqual);
  defrel(>=, greaterOrEqual);
  defrel(==, equal);
  defrel(!=, notEqual);
  defop(  *, multiply);
  defop(  /, divide);

# undef defop
# undef defrel
};


static void fpUnary(oop (*func)(oop), insn *fail)
{
  //emit_move_S_r(0, reg[0]);		// receiver // !!!
  emit_savepc();
  emit_extern();
  emit_gcprotect();
  emit_mkcargs1();
  emit_ccall(func);
  emit_killcargs1();
  emit_gcunprotect();
  emit_restorepc();
  emit_move_r_S(reg[0], 0);
  emit_resume();
}

static void fpBinary(oop (*func)(Float *, Float *), insn *fail)
{
  emit_move_S_r(0, reg[1]);		// argument
  //emit_move_S_r(1, reg[0]);		// receiver // !!!
  emit_notagck_r_t(reg[1], fail);	// SI arg => fail
  emit_get_i_r_r(0, reg[1], tmp[1]);	// base header
  emit_ccick_i_r(FloatCCI, tmp[1]);
  emit_bne(fail);			// non-Float arg => fail
  emit_savepc();
  emit_extern();
  emit_gcprotect();
  emit_mkcargs2();
  emit_ccall(func);
  emit_killcargs2();
  emit_gcunprotect();
  emit_restorepc();
  emit_move_r_popS(reg[0], 1);
  emit_resume();
}


void genAsFloat(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 0);
  fpUnary(Float::asFloat, fail);
}

void genFloatAdd(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 1);
  fpBinary(Float::add, fail);
}

void genFloatSubtract(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 1);
  fpBinary(Float::subtract, fail);
}

void genFloatLessThan(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 1);
  fpBinary(Float::less, fail);
}

void genFloatGreaterThan(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 1);
  fpBinary(Float::greater, fail);
}

void genFloatLessOrEqual(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 1);
  fpBinary(Float::lessOrEqual, fail);
}

void genFloatGreaterOrEqual(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 1);
  fpBinary(Float::greaterOrEqual, fail);
}

void genFloatEqual(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 1);
  fpBinary(Float::equal, fail);
}

void genFloatNotEqual(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 1);
  fpBinary(Float::notEqual, fail);
}

void genFloatMultiply(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 1);
  fpBinary(Float::multiply, fail);
}

void genFloatDivide(NativeMethod *nMeth, insn *fail)
{
  assert(nMeth->argumentCount == 1);
  fpBinary(Float::divide, fail);
}








