/*
  * UAE - The Un*x Amiga Emulator
  *
  * MC68881 emulation
  *
  * Copyright 1996 Herman ten Brugge
  * Adapted for JIT compilation (c) Bernd Meyer, 2000
  * Modified 2005 Peter Keunecke
 */

#include <cmath>

#include "sysdeps.h"

#include "options.h"
#include "memory.h"
#include "newcpu.h"
#include "compemu_arm.h"
#include "flags_arm.h"

#if defined(USE_JIT_FPU)

extern void fp_to_exten(fpdata *fpd, uae_u32 wrd1, uae_u32 wrd2, uae_u32 wrd3);

static const int sz1[8] = { 4, 4, 12, 12, 2, 8, 1, 0 };
static const int sz2[8] = { 4, 4, 12, 12, 2, 8, 2, 0 };

/* return the required floating point precision or -1 for failure, 0=E, 1=S, 2=D */
STATIC_INLINE int comp_fp_get (uae_u32 opcode, uae_u16 extra, int treg)
{
	int reg = opcode & 7;
	int mode = (opcode >> 3) & 7;
	int size = (extra >> 10) & 7;

	if (size == 3 || size == 7) /* 3 = packed decimal, 7 is not defined */
		return -1;
	switch (mode) {
		case 0: /* Dn */
  		switch (size) {
  			case 0: /* Long */
    			fmov_l_rr (treg, reg);
    			return 2;
  			case 1: /* Single */
    			fmov_s_rr (treg, reg);
    			return 1;
  			case 4: /* Word */
    			fmov_w_rr (treg, reg);
    			return 1;
  			case 6: /* Byte */
  			  fmov_b_rr (treg, reg);
    			return 1;
  			default:
    			return -1;
  		}
		case 1: /* An,  invalid mode */
  		return -1;
		case 2: /* (An) */
  		mov_l_rr (S1, reg + 8);
  		break;
		case 3: /* (An)+ */
  		mov_l_rr (S1, reg + 8);
      arm_ADD_l_ri8(reg + 8, (reg == 7 ? sz2[size] : sz1[size]));
  		break;
		case 4: /* -(An) */
		  arm_SUB_l_ri8(reg + 8, (reg == 7 ? sz2[size] : sz1[size]));
  		mov_l_rr (S1, reg + 8);
  		break;
		case 5: /* (d16,An)  */
		{
			uae_u32 off = (uae_s32) (uae_s16) comp_get_iword ((m68k_pc_offset += 2) - 2);
			mov_l_rr (S1, reg + 8);
			lea_l_brr (S1, S1, off);
			break;
		}
		case 6: /* (d8,An,Xn) or (bd,An,Xn) or ([bd,An,Xn],od) or ([bd,An],Xn,od) */
		{
			uae_u32 dp = comp_get_iword ((m68k_pc_offset += 2) - 2);
			calc_disp_ea_020 (reg + 8, dp, S1);
			break;
		}
		case 7:
  		switch (reg) {
  			case 0: /* (xxx).W */
  			{
  				uae_u32 off = (uae_s32) (uae_s16) comp_get_iword ((m68k_pc_offset += 2) - 2);
  				mov_l_ri (S1, off);
  				break;
  			}
  			case 1: /* (xxx).L */
  			{
  				uae_u32 off = comp_get_ilong ((m68k_pc_offset += 4) - 4);
  				mov_l_ri (S1, off);
  				break;
  			}
  			case 2: /* (d16,PC) */
  			{
  				uae_u32 address = start_pc + ((uae_char*) comp_pc_p - (uae_char*) start_pc_p) +	m68k_pc_offset;
  				uae_s32 PC16off = (uae_s32) (uae_s16) comp_get_iword ((m68k_pc_offset += 2) - 2);
  				mov_l_ri (S1, address + PC16off);
  				break;
  			}
  			case 3: /* (d8,PC,Xn) or (bd,PC,Xn) or ([bd,PC,Xn],od) or ([bd,PC],Xn,od) */
  			  return -1; /* rarely used, fallback to non-JIT */
  			case 4: /* # < data >; Constants should be converted just once by the JIT */
    			m68k_pc_offset += sz2[size];
    			switch (size) {
    				case 0:
    				{
    					uae_s32 li = comp_get_ilong(m68k_pc_offset - 4);
    					float si = (float)li;
    
    					if (li == (int)si) {
						    //write_log ("converted immediate LONG constant to SINGLE\n");
    						fmov_s_ri(treg, *(uae_u32 *)&si);
    						return 1;
    					}
					    //write_log ("immediate LONG constant\n");
    					fmov_l_ri(treg, *(uae_u32 *)&li);
    					return 2;
    				}
    				case 1:
				      //write_log (_T("immediate SINGLE constant\n"));
      				fmov_s_ri(treg, comp_get_ilong(m68k_pc_offset - 4));
      				return 1;
    				case 2:
            {
				      //write_log (_T("immediate LONG DOUBLE constant\n"));
              uae_u32 wrd1, wrd2, wrd3;
              fpdata tmp;
              wrd3 = comp_get_ilong(m68k_pc_offset - 4);
              wrd2 = comp_get_ilong(m68k_pc_offset - 8);
              wrd1 = comp_get_iword(m68k_pc_offset - 12) << 16;
			  fp_to_exten(&tmp, wrd1, wrd2, wrd3);
              mov_l_ri(S1, ((uae_u32*)&tmp)[0]);
              mov_l_ri(S2, ((uae_u32*)&tmp)[1]);
              fmov_d_rrr (treg, S1, S2);
      				return 0;
            }
    				case 4:
    				{
    					float si = (float)(uae_s16)comp_get_iword(m68k_pc_offset-2);
    
					    //write_log (_T("converted immediate WORD constant %f to SINGLE\n"), si);
    					fmov_s_ri(treg, *(uae_u32 *)&si);
    					return 1;
    				}
    				case 5:
    				{
					    //write_log (_T("immediate DOUBLE constant\n"));
              mov_l_ri(S1, comp_get_ilong(m68k_pc_offset - 4));
              mov_l_ri(S2, comp_get_ilong(m68k_pc_offset - 8));
              fmov_d_rrr (treg, S1, S2);
    					return 2;
    				}
    				case 6:
    				{
    					float si = (float)(uae_s8)comp_get_ibyte(m68k_pc_offset - 2);
    
    					//write_log (_T("converted immediate BYTE constant to SINGLE\n"));
    					fmov_s_ri(treg, *(uae_u32 *)&si);
    					return 1;
    				}
    				default: /* never reached */
      				return -1;
          }
   			default: /* never reached */
  	  		return -1;
     }
  }
  
	switch (size) {
		case 0: /* Long */
  		readlong (S1, S2);
  		fmov_l_rr (treg, S2);
  		return 2;
		case 1: /* Single */
  		readlong (S1, S2);
			fmov_s_rr (treg, S2);
  		return 1;
		case 2: /* Long Double */
		  fp_to_exten_rm (treg, S1);
		  return 0;
		case 4: /* Word */
  		readword (S1, S2);
			fmov_w_rr (treg, S2);
  		return 1;
		case 5: /* Double */
		  fp_to_double_rm (treg, S1);
  		return 2;
		case 6: /* Byte */
  		readbyte (S1, S2);
		  fmov_b_rr (treg, S2);
  		return 1;
		default:
  		return -1;
  }
	return -1;
}

/* return of -1 means failure, >=0 means OK */
STATIC_INLINE int comp_fp_put (uae_u32 opcode, uae_u16 extra)
{
	int reg = opcode & 7;
	int sreg = (extra >> 7) & 7;
	int mode = (opcode >> 3) & 7;
	int size = (extra >> 10) & 7;

	if (size == 3 || size == 7) /* 3 = packed decimal, 7 is not defined */
		return -1;
	switch (mode) {
		case 0: /* Dn */
  		switch (size) {
  			case 0: /* FMOVE.L FPx, Dn */
  			  fmov_to_l_rr(reg, sreg);
    			return 0;
  			case 1: /* FMOVE.S FPx, Dn */
  			  fmov_to_s_rr(reg, sreg);
    			return 0;
  			case 4: /* FMOVE.W FPx, Dn */
  			  fmov_to_w_rr(reg, sreg);
    			return 0;
  			case 6: /* FMOVE.B FPx, Dn */
  			  fmov_to_b_rr(reg, sreg);
			    return 0;
			  default:
			    return -1;
      }
		case 1: /* An, invalid mode */
  		return -1;
		case 2: /* (An) */
		  mov_l_rr (S1, reg + 8);
		  break;
		case 3: /* (An)+ */
		  mov_l_rr (S1, reg + 8);
      arm_ADD_l_ri8(reg + 8, (reg == 7 ? sz2[size] : sz1[size]));
		  break;
		case 4: /* -(An) */
	    arm_SUB_l_ri8(reg + 8, (reg == 7 ? sz2[size] : sz1[size]));
		  mov_l_rr (S1, reg + 8);
		  break;
		case 5: /* (d16,An) */
		{
			uae_u32 off = (uae_s32) (uae_s16) comp_get_iword ((m68k_pc_offset += 2) - 2);
			mov_l_rr (S1, reg + 8);
			arm_ADD_l_ri (S1, off);
			break;
		}
		case 6: /* (d8,An,Xn) or (bd,An,Xn) or ([bd,An,Xn],od) or ([bd,An],Xn,od) */
		{
			uae_u32 dp = comp_get_iword ((m68k_pc_offset += 2) - 2);
			calc_disp_ea_020 (reg + 8, dp, S1);
			break;
		}
		case 7:
		  switch (reg) {
			  case 0: /* (xxx).W */
			  {
				  uae_u32 off = (uae_s32) (uae_s16) comp_get_iword ((m68k_pc_offset += 2) - 2);
				  mov_l_ri (S1, off);
				  break;
			  }
			  case 1: /* (xxx).L */
			  {
				  uae_u32 off = comp_get_ilong ((m68k_pc_offset += 4) - 4);
				  mov_l_ri (S1, off);
				  break;
			  }
			  default: /* All other modes are not allowed for FPx to <EA> */
			    write_log (_T ("JIT FMOVE FPx,<EA> Mode is not allowed %04x %04x\n"), opcode, extra);
		    return -1;
		  }
  }
	switch (size) {
		case 0: /* Long */
    	fmov_to_l_rr(S2, sreg);
		  writelong_clobber (S1, S2);
		  return 0;
		case 1: /* Single */
      fmov_to_s_rr(S2, sreg);
	    writelong_clobber (S1, S2);
	    return 0;
		case 2:/* Long Double */
		  fp_from_exten_mr (S1, sreg);
		  return 0;
		case 4: /* Word */
		  fmov_to_w_rr(S2, sreg);
		  writeword_clobber (S1, S2);
		  return 0;
		case 5: /* Double */
			fp_from_double_mr(S1, sreg);
		  return 0;
		case 6: /* Byte */
      fmov_to_b_rr(S2, sreg);
		  writebyte (S1, S2);
		  return 0;
		default:
  		return -1;
  }
	return -1;
}

/* return -1 for failure, or register number for success */
STATIC_INLINE int comp_fp_adr (uae_u32 opcode)
{
	uae_s32 off;
	int mode = (opcode >> 3) & 7;
	int reg = opcode & 7;

	switch (mode) {
		case 2:
		case 3:
		case 4:
		  mov_l_rr (S1, 8 + reg);
		  return S1;
		case 5:
		  off = (uae_s32) (uae_s16) comp_get_iword ((m68k_pc_offset += 2) - 2);
      mov_l_rr (S1, 8 + reg);
		  arm_ADD_l_ri (S1, off);
		  return S1;
		case 7:
		  switch (reg) {
			  case 0:
			    off = (uae_s32) (uae_s16) comp_get_iword ((m68k_pc_offset += 2) - 2);
			    mov_l_ri (S1, off);
			    return S1;
			  case 1:
			    off = comp_get_ilong ((m68k_pc_offset += 4) - 4);
			    mov_l_ri (S1, off);
			    return S1;
		  }
		default:
  		return -1;
	}
}

void comp_fscc_opp (uae_u32 opcode, uae_u16 extra)
{
	int reg;

	if (!currprefs.compfpu) {
		FAIL (1);
		return;
	}

	if (extra & 0x20) {  /* only cc from 00 to 1f are defined */
	  FAIL (1);
	  return;
	}
	if ((opcode & 0x38) != 0) { /* We can only do to integer register */
		FAIL (1);
		return;
	}

	fflags_into_flags ();
	reg = (opcode & 7);

	if (!(opcode & 0x38)) {
	  switch (extra & 0x0f) { /* according to fpp.c, the 0x10 bit is ignored */
		  case 0: fp_fscc_ri(reg, NATIVE_CC_F_NEVER); break;
		  case 1: fp_fscc_ri(reg, NATIVE_CC_EQ); break;
		  case 2: fp_fscc_ri(reg, NATIVE_CC_F_OGT); break;
		  case 3: fp_fscc_ri(reg, NATIVE_CC_F_OGE); break;
		  case 4: fp_fscc_ri(reg, NATIVE_CC_F_OLT); break;
		  case 5: fp_fscc_ri(reg, NATIVE_CC_F_OLE); break;
		  case 6: fp_fscc_ri(reg, NATIVE_CC_F_OGL); break;
		  case 7: fp_fscc_ri(reg, NATIVE_CC_F_OR); break;
		  case 8: fp_fscc_ri(reg, NATIVE_CC_F_UN); break;
		  case 9: fp_fscc_ri(reg, NATIVE_CC_F_UEQ); break;
		  case 10: fp_fscc_ri(reg, NATIVE_CC_F_UGT); break;
		  case 11: fp_fscc_ri(reg, NATIVE_CC_F_UGE); break;
		  case 12: fp_fscc_ri(reg, NATIVE_CC_F_ULT); break;
		  case 13: fp_fscc_ri(reg, NATIVE_CC_F_ULE); break;
		  case 14: fp_fscc_ri(reg, NATIVE_CC_NE); break;
		  case 15: fp_fscc_ri(reg, NATIVE_CC_AL); break;
	  }
  }
}

void comp_fbcc_opp (uae_u32 opcode)
{
	uae_u32 start_68k_offset = m68k_pc_offset;
	uae_u32 off, v1, v2;
	int cc;

	if (!currprefs.compfpu) {
		FAIL (1);
		return;
	}

	if (opcode & 0x20) {  /* only cc from 00 to 1f are defined */
		FAIL (1);
		return;
	}
	if (!(opcode & 0x40)) {
		off = (uae_s32) (uae_s16) comp_get_iword ((m68k_pc_offset += 2) - 2);
	}
	else {
		off = comp_get_ilong ((m68k_pc_offset += 4) - 4);
	}

	/* according to fpp.c, the 0x10 bit is ignored
	   (it handles exception handling, which we don't
	   do, anyway ;-) */
	cc = opcode & 0x0f;
  if(cc == 0)
    return; /* jump never */

	/* Note, "off" will sometimes be (unsigned) "negative", so the following
         * uintptr can be > 0xffffffff, but the result will be correct due to
         * wraparound when truncated to 32 bit in the call to mov_l_ri. */
	mov_l_ri(S1, (uintptr)
		(comp_pc_p + off - (m68k_pc_offset - start_68k_offset)));
	mov_l_ri(PC_P, (uintptr) comp_pc_p);

	/* Now they are both constant. Might as well fold in m68k_pc_offset */
	arm_ADD_l_ri (S1, m68k_pc_offset);
	arm_ADD_l_ri (PC_P, m68k_pc_offset);
	m68k_pc_offset = 0;

	v1 = get_const (PC_P);
	v2 = get_const (S1);
	fflags_into_flags ();

	switch (cc) {
		case 1: register_branch (v1, v2, NATIVE_CC_EQ); break;
		case 2: register_branch (v1, v2, NATIVE_CC_F_OGT); break;
		case 3: register_branch (v1, v2, NATIVE_CC_F_OGE); break;
		case 4: register_branch (v1, v2, NATIVE_CC_F_OLT); break;
		case 5: register_branch (v1, v2, NATIVE_CC_F_OLE); break;
		case 6: register_branch (v1, v2, NATIVE_CC_F_OGL); break;
		case 7: register_branch (v1, v2, NATIVE_CC_F_OR); break;
		case 8: register_branch (v1, v2, NATIVE_CC_F_UN); break;
		case 9: register_branch (v1, v2, NATIVE_CC_F_UEQ); break;
		case 10: register_branch (v1, v2, NATIVE_CC_F_UGT); break;
		case 11: register_branch (v1, v2, NATIVE_CC_F_UGE); break;
		case 12: register_branch (v1, v2, NATIVE_CC_F_ULT); break;
		case 13: register_branch (v1, v2, NATIVE_CC_F_ULE); break;
		case 14: register_branch (v1, v2, NATIVE_CC_NE); break;
		case 15: register_branch (v2, v2, NATIVE_CC_AL); break;
	}
}

static uae_u32 dhex_pi[]    ={0x54442D18, 0x400921FB};
static uae_u32 dhex_exp_1[] ={0x8B145769, 0x4005BF0A};
static uae_u32 dhex_l2_e[]  ={0x652B82FE, 0x3FF71547};
static uae_u32 dhex_ln_2[]  ={0xFEFA39EF, 0x3FE62E42};
static uae_u32 dhex_ln_10[] ={0xBBB55516, 0x40026BB1};
static uae_u32 dhex_l10_2[] ={0x509F79FF, 0x3FD34413};
static uae_u32 dhex_l10_e[] ={0x1526E50E, 0x3FDBCB7B};
static uae_u32 dhex_1e16[]  ={0x37E08000, 0x4341C379};
static uae_u32 dhex_1e32[]  ={0xB5056E17, 0x4693B8B5};
static uae_u32 dhex_1e64[]  ={0xE93FF9F5, 0x4D384F03};
static uae_u32 dhex_1e128[] ={0xF9301D32, 0x5A827748};
static uae_u32 dhex_1e256[] ={0x7F73BF3C, 0x75154FDD};
static double fp_1e8;

void comp_fpp_opp (uae_u32 opcode, uae_u16 extra)
{
	int reg;
	int sreg, prec = 0;
	int	dreg = (extra >> 7) & 7;
	int source = (extra >> 13) & 7;
	int	opmode = extra & 0x7f;

  if (special_mem) {
    FAIL(1);
    return;
  } 

	if (!currprefs.compfpu) {
		FAIL (1);
		return;
	}
	switch (source) {
		case 3: /* FMOVE FPx, <EA> */
		  if (comp_fp_put (opcode, extra) < 0)
			  FAIL (1);
		  return;
		case 4: /* FMOVE.L  <EA>, ControlReg */
			if ((opcode & 0x38) == 0) {
				// Dn
				// Only single selected control register is allowed
				// All control register bits unset = FPIAR
				uae_u16 bits = extra & (0x1000 | 0x0800 | 0x0400);
				if (bits && bits != 0x1000 && bits != 0x0800 && bits != 0x400) {
					FAIL(1);
					return;
				}
				if (extra & 0x1000) { /* FPCR */
				  FAIL (1);
				}
				if (extra & 0x0800) { /* FPSR */
				  FAIL (1);
				}
				if ((extra & 0x0400) || !bits) /* FPIAR */
				  mov_l_mr ((uintptr)&regs.fpiar, opcode & 15);
				return;
			} else if ((opcode & 0x38) == 0x08) {
				// An
				// Only FPIAR can be moved to/from address register
				// All bits unset = FPIAR
				uae_u16 bits = extra & (0x1000 | 0x0800 | 0x0400);
				if (bits && bits != 0x0400) {
					FAIL(1);
					return;
				}
			  mov_l_mr ((uintptr)&regs.fpiar, opcode & 15);
				return;
      }
		  else if ((opcode & 0x3f) == 0x3c) {
			  if (extra & 0x1000) { /* FPCR */
          FAIL(1);
				  return;
			  }
			  if (extra & 0x0800) { /* FPSR */
				  FAIL (1);
				  return;
			  }
			  if (extra & 0x0400) { /* FPIAR */
				  uae_u32 val = comp_get_ilong ((m68k_pc_offset += 4) - 4);
				  mov_l_mi ((uintptr)&regs.fpiar, val);
				  return;
			  }
		  }
		  FAIL (1);
		  return;
		case 5: /* FMOVE.L  ControlReg, <EA> */
			if ((opcode & 0x38) == 0) {
				// Dn
				// Only single selected control register is allowed
				// All control register bits unset = FPIAR
				uae_u16 bits = extra & (0x1000 | 0x0800 | 0x0400);
				if (bits && bits != 0x1000 && bits != 0x0800 && bits != 0x400) {
					FAIL(1);
					return;
				}
				if (extra & 0x1000) /* FPCR */
				  mov_l_rm (opcode & 15, (uintptr)&regs.fpcr);
				if (extra & 0x0800) { /* FPSR */
				  FAIL (1);
				}
				if ((extra & 0x0400) || !bits) /* FPIAR */
				  mov_l_rm (opcode & 15, (uintptr)&regs.fpiar);
				return;
			} else if ((opcode & 0x38) == 0x08) {
				// An
				// Only FPIAR can be moved to/from address register
				// All bits unset = FPIAR
				uae_u16 bits = extra & (0x1000 | 0x0800 | 0x0400);
				if (bits && bits != 0x0400) {
					FAIL(1);
					return;
				}
			  mov_l_rm (opcode & 15, (uintptr)&regs.fpiar);
				return;
      }
		  FAIL (1);
		  return;
		case 6:
		case 7:
		{
			uae_u32 list = 0;
			int incr = 0;
			if (extra & 0x2000) {
				int ad;

				/* FMOVEM FPP->memory */
				switch ((extra >> 11) & 3) { /* Get out early if failure */
					case 0:
					case 2:
					  break;
					case 1:
					case 3:
					default:
					  FAIL (1); 
            return;
				}
				ad = comp_fp_adr (opcode);
				if (ad < 0) {
					m68k_setpc (m68k_getpc () - 4);
					op_illg (opcode);
					return;
				}
				switch ((extra >> 11) & 3) {
					case 0:	/* static pred */
					  list = extra & 0xff;
					  incr = -1;
					  break;
					case 2:	/* static postinc */
					  list = extra & 0xff;
					  incr = 1;
					  break;
					case 1:	/* dynamic pred */
					case 3:	/* dynamic postinc */
  					abort ();
				}
				if (incr < 0) { /* Predecrement */
					for (reg = 7; reg >= 0; reg--) {
						if (list & 0x80) {
							sub_l_ri (ad, 12);
							fp_from_exten_mr (ad, reg);
						}
						list <<= 1;
					}
				} else { /* Postincrement */
					for (reg = 0; reg <= 7; reg++) {
						if (list & 0x80) {
							fp_from_exten_mr (ad, reg);
							arm_ADD_l_ri (ad, 12);
						}
						list <<= 1;
					}
				}
				if ((opcode & 0x38) == 0x18)
					mov_l_rr ((opcode & 7) + 8, ad);
				if ((opcode & 0x38) == 0x20)
					mov_l_rr ((opcode & 7) + 8, ad);
			} else {
				/* FMOVEM memory->FPP */
				int ad;
				switch ((extra >> 11) & 3) { /* Get out early if failure */
					case 0:
					case 2:
					  break;
					case 1:
					case 3:
					default:
					  FAIL (1); 
            return;
				}
				ad = comp_fp_adr (opcode);
				if (ad < 0) {
					m68k_setpc (m68k_getpc () - 4);
					op_illg (opcode);
					return;
				}
				switch ((extra >> 11) & 3) {
					case 0:	/* static pred */
					  list = extra & 0xff;
					  incr = -1;
					  break;
					case 2:	/* static postinc */
					  list = extra & 0xff;
					  incr = 1;
					  break;
					case 1:	/* dynamic pred */
					case 3:	/* dynamic postinc */
  					abort ();
				}

				if (incr < 0) {
					// not reached
					for (reg = 7; reg >= 0; reg--) {
						if (list & 0x80) {
							sub_l_ri (ad, 12);
              fp_to_exten_rm(reg, ad);
						}
						list <<= 1;
					}
				} else {
					for (reg = 0; reg <= 7; reg++) {
						if (list & 0x80) {
              fp_to_exten_rm(reg, ad);
							arm_ADD_l_ri (ad, 12);
						}
						list <<= 1;
					}
				}
				if ((opcode & 0x38) == 0x18)
					mov_l_rr ((opcode & 7) + 8, ad);
				if ((opcode & 0x38) == 0x20)
					mov_l_rr ((opcode & 7) + 8, ad);
			}
		}
    return;
		case 2: /* from <EA> to FPx */
		  dont_care_fflags ();
		  if ((extra & 0xfc00) == 0x5c00) { /* FMOVECR */
			  //write_log (_T("JIT FMOVECR %x\n"), opmode);
			  switch (opmode) {
				  case 0x00:
				    fmov_d_rm (dreg, (uintptr)&dhex_pi);
				    break;
				  case 0x0b:
				    fmov_d_rm (dreg, (uintptr)&dhex_l10_2);
				    break;
				  case 0x0c:
				    fmov_d_rm (dreg, (uintptr)&dhex_exp_1);
				    break;
				  case 0x0d:
				    fmov_d_rm (dreg, (uintptr)&dhex_l2_e);
				    break;
				  case 0x0e:
				    fmov_d_rm (dreg, (uintptr)&dhex_l10_e);
				    break;
				  case 0x0f:
            fmov_d_ri_0 (dreg);
				    break;
				  case 0x30:
				    fmov_d_rm (dreg, (uintptr)&dhex_ln_2);
				    break; 
				  case 0x31:
				    fmov_d_rm (dreg, (uintptr)&dhex_ln_10);
				    break;
				  case 0x32:
            fmov_d_ri_1 (dreg);
				    break;
				  case 0x33:
            fmov_d_ri_10 (dreg);
				    break;
				  case 0x34:
				    fmov_d_ri_100 (dreg);
				    break;
				  case 0x35:
				    fmov_l_ri (dreg, 10000);
				    break;
				  case 0x36:
				    fmov_rm (dreg, (uintptr)&fp_1e8);
				    break;
				  case 0x37:
				    fmov_d_rm (dreg, (uintptr)&dhex_1e16);
				    break;
				  case 0x38:
				    fmov_d_rm (dreg, (uintptr)&dhex_1e32);
				    break;
				  case 0x39:
				    fmov_d_rm (dreg, (uintptr)&dhex_1e64);
				    break;
				  case 0x3a:
				    fmov_d_rm (dreg, (uintptr)&dhex_1e128);
				    break;
				  case 0x3b:
				    fmov_d_rm (dreg, (uintptr)&dhex_1e256);
				    break;
				  default:
				    FAIL (1);
				    return;
			  }
			  fmov_rr (FP_RESULT, dreg);
			  return;
      }
		  if (opmode & 0x20) /* two operands, so we need a scratch reg */
			  sreg = FS1;
		  else /* one operand only, thus we can load the argument into dreg */
			  sreg = dreg;
		  if(opmode >= 0x30 && opmode <= 0x37) {
				// get out early for unsupported ops
			  FAIL (1);
			  return;
		  }
		  if ((prec = comp_fp_get (opcode, extra, sreg)) < 0) {
			  FAIL (1);
			  return;
		  }
		  if (!opmode) { /* FMOVE  <EA>,FPx */
			  fmov_rr (FP_RESULT, dreg);
			  return;
		  }
		  /* no break here for <EA> to dreg */
		case 0: /* directly from sreg to dreg */
		  if (!source) { /* no <EA> */
			  dont_care_fflags ();
			  sreg = (extra >> 10) & 7;
		  }
		  switch (opmode) {
			  case 0x00: /* FMOVE */
			    fmov_rr (dreg, sreg);
			    break;
			  case 0x01: /* FINT */
			    frndint_rr (dreg, sreg);
			    break;
			  case 0x02: /* FSINH */
			    ffunc_rr (sinh, dreg, sreg);
			    break;
			  case 0x03: /* FINTRZ */
			    frndintz_rr (dreg, sreg);
			    break;
		    case 0x04: /* FSQRT */
		      fsqrt_rr (dreg, sreg);
		      break;
		    case 0x06: /* FLOGNP1 */
		      ffunc_rr (log1p, dreg, sreg);
		      break;
		    case 0x08: /* FETOXM1 */
		      ffunc_rr (expm1, dreg, sreg);
		      break;
		    case 0x09: /* FTANH */
		      ffunc_rr (tanh, dreg, sreg);
		      break;
		    case 0x0a: /* FATAN */
		      ffunc_rr (atan, dreg, sreg);
		      break;
		    case 0x0c: /* FASIN */
		      ffunc_rr (asin, dreg, sreg);
		      break;
		    case 0x0d: /* FATANH */
		      ffunc_rr (atanh, dreg, sreg);
		      break;
		    case 0x0e: /* FSIN */
		      ffunc_rr (sin, dreg, sreg);
		      break;
		    case 0x0f: /* FTAN */
		      ffunc_rr (tan, dreg, sreg);
		      break;
		    case 0x10: /* FETOX */
		      ffunc_rr (exp, dreg, sreg);
		      break;
		    case 0x11: /* FTWOTOX */
		      fpowx_rr (2, dreg, sreg);
		      break;
		    case 0x12: /* FTENTOX */
		      fpowx_rr (10, dreg, sreg);
		      break;
			  case 0x14: /* FLOGN */
			    ffunc_rr (log, dreg, sreg);
			    break;
			  case 0x15: /* FLOG10 */
			    ffunc_rr (log10, dreg, sreg);
			    break;
			  case 0x16: /* FLOG2 */
			    ffunc_rr (log2, dreg, sreg);
			    break;
			  case 0x18: /* FABS */
			    fabs_rr (dreg, sreg);
			    break;
			  case 0x19: /* FCOSH */
			    ffunc_rr (cosh, dreg, sreg);
			    break;
			  case 0x1a: /* FNEG */
			    fneg_rr (dreg, sreg);
			    break;
  			case 0x1c: /* FACOS */
			    ffunc_rr (acos, dreg, sreg);
			    break;
			  case 0x1d: /* FCOS */
			    ffunc_rr (cos, dreg, sreg);
			    break;
			  case 0x20: /* FDIV */
			    fdiv_rr (dreg, sreg);
			    break;
			  case 0x21: /* FMOD */
			    fmod_rr (dreg, sreg);
			    break;
			  case 0x22: /* FADD */
			    fadd_rr (dreg, sreg);
			    break;
			  case 0x23: /* FMUL */
			    fmul_rr (dreg, sreg);
			    break;
			  case 0x24: /* FSGLDIV */
			    fsgldiv_rr (dreg, sreg);
			    break;
			  case 0x60: /* FSDIV */
			    fdiv_rr (dreg, sreg);
			    if (!currprefs.fpu_strict) /* faster, but less strict rounding */
				    break;
			    fcuts_r (dreg);
			    break;
			  case 0x25: /* FREM */
			    frem1_rr (dreg, sreg);
			    break;
        case 0x27: /* FSGLMUL */
			    fsglmul_rr (dreg, sreg);
			    break;
			  case 0x63: /* FSMUL */
			    fmul_rr (dreg, sreg);
			    if (!currprefs.fpu_strict) /* faster, but less strict rounding */
				    break;
			    fcuts_r (dreg);
			    break;
			  case 0x28: /* FSUB */
			    fsub_rr (dreg, sreg);
			    break;
			  case 0x30: /* FSINCOS */
			  case 0x31:
			  case 0x32:
			  case 0x33:
			  case 0x34:
			  case 0x35:
			  case 0x36:
			  case 0x37:
				  FAIL (1);
				  return;
			  case 0x38: /* FCMP */
			    fmov_rr (FP_RESULT, dreg);
			    fsub_rr (FP_RESULT, sreg);
			    return;
			  case 0x3a: /* FTST */
			    fmov_rr (FP_RESULT, sreg);
			    return;
		    case 0x40: /* FSMOVE */
			    if (prec == 1 || !currprefs.fpu_strict) {
				    if (sreg != dreg) /* no <EA> */
					    fmov_rr (dreg, sreg);
			    }
			    else {
  			    fmovs_rr (dreg, sreg);
			    }
          break;
			  case 0x44: /* FDMOVE */
          if (sreg != dreg) /* no <EA> */
  			    fmov_rr (dreg, sreg);
          break;
			  case 0x41: /* FSSQRT */
			    fsqrt_rr (dreg, sreg);
			    if (!currprefs.fpu_strict) /* faster, but less strict rounding */
				    break;
			    fcuts_r (dreg);
			    break;
			  case 0x45: /* FDSQRT */
          fsqrt_rr (dreg, sreg);
          break;
			  case 0x58: /* FSABS */
			    fabs_rr (dreg, sreg);
			    if (prec != 1 && currprefs.fpu_strict)
			      fcuts_r (dreg);
			    break;
			  case 0x5a: /* FSNEG */
			    fneg_rr (dreg, sreg);
			    if (prec != 1 && currprefs.fpu_strict)
			      fcuts_r (dreg);
			    break;
			  case 0x5c: /* FDABS */
			    fabs_rr (dreg, sreg);
			    break;
			  case 0x5e: /* FDNEG */
			    fneg_rr (dreg, sreg);
			    break;
			  case 0x62: /* FSADD */
			    fadd_rr (dreg, sreg);
			    if (!currprefs.fpu_strict) /* faster, but less strict rounding */
				    break;
			    fcuts_r (dreg);
			    break;
			  case 0x64: /* FDDIV */
			    fdiv_rr (dreg, sreg);
			    break;
			  case 0x66: /* FDADD */
			    fadd_rr (dreg, sreg);
			    break;
			  case 0x67: /* FDMUL */
			    fmul_rr (dreg, sreg);
			    break;
			  case 0x68: /* FSSUB */
			    fsub_rr (dreg, sreg);
			    if (!currprefs.fpu_strict) /* faster, but less strict rounding */
				    break;
			    fcuts_r (dreg);
			    break;
			  case 0x6c: /* FDSUB */
			    fsub_rr (dreg, sreg);
			    break;
		    default:
			    FAIL (1);
			    return;
      }
		  fmov_rr (FP_RESULT, dreg);
		  return;
		default:
		  write_log (_T ("Unsupported JIT-FPU instruction: 0x%04x %04x\n"), opcode, extra);
		  FAIL (1);
		  return;
  }
}
#endif
