// genPrimitive.cc -- primitive method prologue generation		-*- C++ -*-
// 
// Author: Ian.Piumarta@INRIA.Fr
//
// Last edited: 2/28/01 by marcus@ira.uka.de

#include "archdep.h"
#include "machine.h"
#include "primitive.h"
		   
#include "Frame.h" 
		   
#include ARCHDEP(emit.h)


#ifdef __macintosh__
  extern "C" {
    int ioMSecs();
	   int ioLowResMSecs();
  };
 #include <time.h>
 int ioLowResMSecs() {
    return (1000 * clock()) / CLOCKS_PER_SEC;
 }
#else
  extern "C" {
    int ioLowResMSecs();
    int ioMSecs();
  };
#endif

extern "C" { void checkForInterrupts(void); }
static int startTime= 0;

typedef void (*pvfv)(void);


void genPrimitive(unsigned index, NativeMethod *nMeth, insn *fail)
{
  byte flags= primitiveFlags[index];

  if (flags & PrimitiveFailBit)	// primitive always fails
    return;

  pvfv prim= (pvfv)primitiveTable[index];

  if (flags & IntrinsicPrimBit)
    {
      assert(prim != (pvfv)primitiveFail);

#    ifdef NEW_PRIMITIVES

      emit_savepc();
      emit_extern();

      emit_move_i_v((int)&primitiveArguments[nMeth->argumentCount], stackPointer);
      // receiver + arguments
      //printf("%d ARG LOAD AT %p INTRINSIC\n", nMeth->argumentCount, asm_pc);
      emit_move_i_r((int)&primitiveArguments[nMeth->argumentCount], tmp[0]);
      for (size_t idx= 0; idx <= nMeth->argumentCount; ++idx)
	{
#        ifdef STACK_GROWS_DOWN
	  emit_get_i_r_r(idx*4,SP, reg[0]);		// STACK DIRECTION!!!
	  emit_put_r_i_r(reg[0],    -idx*4,tmp[0]);
#        else
	  emit_get_i_r_r(-idx*4,SP, reg[0]);		// STACK DIRECTION!!!
	  emit_put_r_i_r(reg[0],    -idx*4,tmp[0]);
#        endif
	}
      emit_gcprotect();
      emit_initccall(prim);
      emit_move_i_v(nMeth->argumentCount, argumentCount);
      emit_move_i_v(index, primitiveIndex);
      emit_move_i_v(1, successFlag);
      emit_initccall(prim);
      emit_move_F_r(reg[1]);			// arg2 is frame
      emit_move_i_r((int)nMeth, reg[0]);	// arg1 is method
      emit_mkcargs2();
      emit_execccall(prim);	// regs/tmps trashed!
      emit_killcargs2();
      emit_restorepc();
#    ifndef NDEBUG
      emit_gcunprotect();
#    endif
      emit_intern();
      emit_test_v(successFlag);
      emit_beq(fail);
      emit_move_v_r(primitiveArguments[0], tmp[0]);	// get result
      emit_move_r_popS(tmp[0], nMeth->argumentCount);	// push it
      emit_resume();

#    else // !NEW_PRIMITIVES

      emit_savepc();
      emit_extern();
      emit_externp();
      emit_gcprotect();
      emit_initccall(prim);
      emit_move_i_v(nMeth->argumentCount, argumentCount);
      emit_move_i_v(index, primitiveIndex);
      emit_move_i_v(1, successFlag);
      emit_move_F_r(reg[1]);			// arg2 is frame
      emit_move_i_r((int)nMeth, reg[0]);	// arg1 is method
      emit_mkcargs2();
      emit_execccall(prim);	// regs/tmps trashed!
      emit_killcargs2();
      emit_restorepc();
#    ifndef NDEBUG
      emit_gcunprotect();
#    endif
      emit_internp();
      emit_test_v(successFlag);
      emit_beq(fail);
      emit_resume();

#    endif // !NEW_PRIMITIVES

      return;
    }

  // NON-INTRINSIC PRIMITIVES

  insn *asm_org= asm_pc, *notimer= asm_pc, *checkok= asm_pc, *nocheck= asm_pc;
  for (asm_pass= 1; asm_pass < 3; ++asm_pass)
    {
      asm_pc= asm_org;
      // must protect pc against all primitives
      emit_savepc();

#    if 0
      {
	static int saveFP;
	emit_move_r_v(FP, saveFP);
	emit_initccall(&printf);
	emit_move_i_r(index, reg[1]);
	emit_move_i_r((int)"PRIM %d\n", reg[0]);
	emit_mkcargs2();
	emit_execccall(&printf);
	emit_killcargs2();
	emit_restorepc();
      }
#    endif

      if (!(flags & FastPrimBit))
	{
	  emit_move_v_r(nextWakeupTick, reg[0]);
	  emit_test_r(reg[0]);
	  emit_beq(notimer);
	  {
	    emit_ccall(ioLowResMSecs);
	    emit_move_r_v(reg[0], startTime);
	  }
	  notimer= asm_pc;
	}

      emit_initccall(prim);

      if (flags & StackPointerBit)
	{
#        ifdef NEW_PRIMITIVES
	  emit_move_i_v((int)&primitiveArguments[nMeth->argumentCount], stackPointer);
	  // receiver + arguments
	  //printf("%d ARG LOAD AT %p\n", nMeth->argumentCount, asm_pc);
	  emit_move_i_r((int)&primitiveArguments[nMeth->argumentCount], tmp[0]);
	  for (size_t idx= 0; idx <= nMeth->argumentCount; ++idx)
	    {
#            ifdef STACK_GROWS_DOWN
	      emit_get_i_r_r(idx*4,SP, reg[0]);		// STACK DIRECTION!!!
	      emit_put_r_i_r(reg[0],    -idx*4,tmp[0]);
#            else
	      emit_get_i_r_r(-idx*4,SP, reg[0]);		// STACK DIRECTION!!!
	      emit_put_r_i_r(reg[0],    -idx*4,tmp[0]);
#            endif
	    }
#	 else
	  emit_externp();
#	 endif
	}
      if (flags & ActiveFrameBit)
	{
	  emit_extern();
	  emit_gcprotect();
	}
      if (flags & ArgumentCountBit)
	{
	  emit_move_i_v(nMeth->argumentCount, argumentCount);
	}
      // THIS SHOULD BE AUTOMATIC
      if ((index == 147) || (index == 96))	// warpBits, copyBits
	{
	  emit_move_i_v(index, primitiveIndex);
	}
      if (flags & SuccessFlagBit)
	{
	  emit_move_i_v(1, successFlag);
	}

      emit_mkcargs2();
      emit_execccall(prim);	// regs/tmps trashed!
      emit_killcargs2();

      emit_restorepc();

#    ifndef NDEBUG
      if (flags & ActiveFrameBit)
	{
	  emit_gcunprotect();
	}
#    endif
#    ifdef NEW_PRIMITIVES
      if (flags & SuccessFlagBit)
	{
	  emit_test_v(successFlag);
	  emit_beq(fail);
	}
      if (flags & StackPointerBit)
	{
	  emit_move_v_r(primitiveArguments[0], tmp[0]);		// get result
	  emit_move_r_popS(tmp[0], nMeth->argumentCount);	// push it
	}
#      ifndef NDEBUG
      emit_move_i_v(0, stackPointer);	// force explosion if debugging
#      endif
#    else
      if (flags & StackPointerBit)
	{
	  emit_internp();
	  emit_extern();
	}
      if (flags & SuccessFlagBit)
	{
	  emit_test_v(successFlag);
	  emit_beq(fail);
	}
#    endif

      if (!(flags & FastPrimBit))
	{
	  emit_move_v_r(nextWakeupTick, tmp[0]);
	  emit_test_r(tmp[0]);
	  emit_beq(nocheck);
	  {
	    emit_savepc();
	    {
	      emit_ccall(ioLowResMSecs);
	      emit_move_v_r(startTime, reg[1]);
	      emit_cmp_r_r(reg[0], reg[1]);
	      emit_beq(checkok);
	      {
		emit_ccall(ioMSecs);
		emit_move_v_r(nextWakeupTick, reg[1]);
		emit_and_i_r(0x1fffffff, reg[0]);
		emit_cmp_r_r(reg[0], reg[1]);
		emit_blt(checkok);
		{
		  emit_move_i_v(0, interruptCheckCounter);
		}
	      }
	      checkok= asm_pc;
	    }
	    emit_restorepc();
	  }
	  nocheck= asm_pc;
	}

      emit_resume();
    }
  asm_pass= 0;
}


void genControlPrimitive(unsigned index, NativeMethod *nMeth, insn *fail)
{
  void *prim= (insn *)primitiveTable[index];

  PRINTF(("%p genControlPrimitive %d\n", asm_pc, index));

  emit_savepc();
  emit_extern();
  emit_initccall(prim);
  emit_gcprotect();
  emit_move_F_r(reg[1]);
  emit_move_i_r((int)nMeth, reg[0]);
  emit_mkcargs2();
  emit_execccall(prim);
  emit_killcargs2();
# ifndef NDEBUG
  emit_gcunprotect();
# endif

  if ((index == 81) || (index == 82))	// #value
    {
      emit_move_r_r(reg[0], tmp[0]);
      emit_initjmp_r(tmp[0]);
    }
  else
    {
      emit_initjmp_r(reg[0]);
    }

  emit_restorepc();
  emit_intern();
  emit_cmp_r_i(reg[0], 0);
  emit_beq(fail);

  if ((index == 81) || (index == 82))	// #value
    {
      emit_move_S_r(nMeth->argumentCount, reg[0]);	// reload receiver
      emit_execjmp_r(tmp[0]);
    }
  else
    {
      emit_execjmp_r(reg[0]);
    }
}
