#include <math.h>
#include <stdio.h>
#include <stdarg.h>


extern void exit(int);

static void fatalerror(char *format, ...) {
  va_list ap;
  va_start(ap,format);
  vfprintf(stderr,format,ap);  // JFF: fixed. Was using fprintf and arguments were wrong
  va_end(ap);
  exit(1);
}

#define FPCC_N			0x08000000
#define FPCC_Z			0x04000000
#define FPCC_I			0x02000000
#define FPCC_NAN		0x01000000

#define DOUBLE_INFINITY					(unsigned long long)(0x7ff0000000000000)
#define DOUBLE_EXPONENT					(unsigned long long)(0x7ff0000000000000)
#define DOUBLE_MANTISSA					(unsigned long long)(0x000fffffffffffff)

extern flag floatx80_is_nan( floatx80 a );

// masks for packed dwords, positive k-factor
static uint32 pkmask2[18] =
{
 0xffffffff, 0, 0xf0000000, 0xff000000, 0xfff00000, 0xffff0000,
 0xfffff000, 0xffffff00, 0xfffffff0, 0xffffffff,
 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
 0xffffffff, 0xffffffff, 0xffffffff
};

static uint32 pkmask3[18] =
{
 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0xf0000000, 0xff000000, 0xfff00000, 0xffff0000,
 0xfffff000, 0xffffff00, 0xfffffff0, 0xffffffff,
};

static inline double fx80_to_double(floatx80 fx)
{
  uint64 d;
  double *foo;

  foo = (double *)&d;

  d = floatx80_to_float64(fx);

  return *foo;
}

static inline floatx80 double_to_fx80(double in)
{
  uint64 *d;

  d = (uint64 *)&in;

  return float64_to_floatx80(*d);
}

static inline floatx80 load_extended_float80(uint32 ea)
{
  uint32 d1,d2;
	uint16 d3;
  floatx80 fp;

  d3 = m68ki_read_16(ea);
  d1 = m68ki_read_32(ea+4);
  d2 = m68ki_read_32(ea+8);

  fp.high = d3;
  fp.low = ((uint64)d1<<32) | (d2 & 0xffffffff);

  return fp;
}

static inline void store_extended_float80(uint32 ea, floatx80 fpr)
{
  m68ki_write_16(ea+0, fpr.high);
  m68ki_write_16(ea+2, 0);
  m68ki_write_32(ea+4, (fpr.low>>32)&0xffffffff);
  m68ki_write_32(ea+8, fpr.low&0xffffffff);
}

static inline floatx80 load_pack_float80(uint32 ea)
{
  uint32 dw1, dw2, dw3;
  floatx80 result;
  double tmp;
  char str[128], *ch;

  dw1 = m68ki_read_32(ea);
  dw2 = m68ki_read_32(ea+4);
  dw3 = m68ki_read_32(ea+8);

  ch = &str[0];
  if (dw1 & 0x80000000)	// mantissa sign
    {
      *ch++ = '-';
    }
  *ch++ = (char)((dw1 & 0xf) + '0');
  *ch++ = '.';
  *ch++ = (char)(((dw2 >> 28) & 0xf) + '0');
  *ch++ = (char)(((dw2 >> 24) & 0xf) + '0');
  *ch++ = (char)(((dw2 >> 20) & 0xf) + '0');
  *ch++ = (char)(((dw2 >> 16) & 0xf) + '0');
  *ch++ = (char)(((dw2 >> 12) & 0xf) + '0');
  *ch++ = (char)(((dw2 >> 8)  & 0xf) + '0');
  *ch++ = (char)(((dw2 >> 4)  & 0xf) + '0');
  *ch++ = (char)(((dw2 >> 0)  & 0xf) + '0');
  *ch++ = (char)(((dw3 >> 28) & 0xf) + '0');
  *ch++ = (char)(((dw3 >> 24) & 0xf) + '0');
  *ch++ = (char)(((dw3 >> 20) & 0xf) + '0');
  *ch++ = (char)(((dw3 >> 16) & 0xf) + '0');
  *ch++ = (char)(((dw3 >> 12) & 0xf) + '0');
  *ch++ = (char)(((dw3 >> 8)  & 0xf) + '0');
  *ch++ = (char)(((dw3 >> 4)  & 0xf) + '0');
  *ch++ = (char)(((dw3 >> 0)  & 0xf) + '0');
  *ch++ = 'E';
  if (dw1 & 0x40000000)	// exponent sign
    {
      *ch++ = '-';
    }
  *ch++ = (char)(((dw1 >> 24) & 0xf) + '0');
  *ch++ = (char)(((dw1 >> 20) & 0xf) + '0');
  *ch++ = (char)(((dw1 >> 16) & 0xf) + '0');
  *ch = '\0';

  sscanf(str, "%le", &tmp);

  result = double_to_fx80(tmp);

  return result;
}

static inline void store_pack_float80(uint32 ea, int k, floatx80 fpr)
{
  uint32 dw1, dw2, dw3;
  char str[128], *ch;
  int i, j, exp;

  dw1 = dw2 = dw3 = 0;
  ch = &str[0];

  sprintf(str, "%.16e", fx80_to_double(fpr));

  if (*ch == '-')
    {
      ch++;
      dw1 = 0x80000000;
    }

  if (*ch == '+')
    {
      ch++;
    }

  dw1 |= (*ch++ - '0');

  if (*ch == '.')
    {
      ch++;
    }

  // handle negative k-factor here
  if ((k <= 0) && (k >= -13))
    {
      exp = 0;
      for (i = 0; i < 3; i++)
	{
	  if (ch[18+i] >= '0' && ch[18+i] <= '9')
	    {
	      exp = (exp << 4) | (ch[18+i] - '0');
	    }
	}

      if (ch[17] == '-')
	{
	  exp = -exp;
	}

      k = -k;
      // last digit is (k + exponent - 1)
      k += (exp - 1);

      // round up the last significant mantissa digit
      if (ch[k+1] >= '5')
	{
	  ch[k]++;
	}

      // zero out the rest of the mantissa digits
      for (j = (k+1); j < 16; j++)
	{
	  ch[j] = '0';
	}

      // now zero out K to avoid tripping the positive K detection below
      k = 0;
    }

  // crack 8 digits of the mantissa
  for (i = 0; i < 8; i++)
    {
      dw2 <<= 4;
      if (*ch >= '0' && *ch <= '9')
	{
	  dw2 |= *ch++ - '0';
	}
    }

  // next 8 digits of the mantissa
  for (i = 0; i < 8; i++)
    {
      dw3 <<= 4;
      if (*ch >= '0' && *ch <= '9')
	dw3 |= *ch++ - '0';
    }

  // handle masking if k is positive
  if (k >= 1)
    {
      if (k <= 17)
	{
	  dw2 &= pkmask2[k];
	  dw3 &= pkmask3[k];
	}
      else
	{
	  dw2 &= pkmask2[17];
	  dw3 &= pkmask3[17];
	  //			m68ki_cpu.fpcr |=  (need to set OPERR bit)
	}
    }

  // finally, crack the exponent
  if (*ch == 'e' || *ch == 'E')
    {
      ch++;
      if (*ch == '-')
	{
	  ch++;
	  dw1 |= 0x40000000;
	}

      if (*ch == '+')
	{
	  ch++;
	}

      j = 0;
      for (i = 0; i < 3; i++)
	{
	  if (*ch >= '0' && *ch <= '9')
	    {
	      j = (j << 4) | (*ch++ - '0');
	    }
	}

      dw1 |= (j << 16);
    }

  m68ki_write_32(ea, dw1);
  m68ki_write_32(ea+4, dw2);
  m68ki_write_32(ea+8, dw3);
}

static inline void SET_CONDITION_CODES(floatx80 reg)
{
  REG_FPSR &= ~(FPCC_N|FPCC_Z|FPCC_I|FPCC_NAN);

  // sign flag
  if (reg.high & 0x8000)
    {
      REG_FPSR |= FPCC_N;
    }

  // zero flag
  if (((reg.high & 0x7fff) == 0) && ((reg.low<<1) == 0))
    {
      REG_FPSR |= FPCC_Z;
    }

  // infinity flag
  if (((reg.high & 0x7fff) == 0x7fff) && ((reg.low<<1) == 0))
    {
      REG_FPSR |= FPCC_I;
    }

  // NaN flag
  if (floatx80_is_nan(reg))
    {
      REG_FPSR |= FPCC_NAN;
    }
}

static inline int TEST_CONDITION(int condition)
{
  int n = (REG_FPSR & FPCC_N) != 0;
  int z = (REG_FPSR & FPCC_Z) != 0;
  int nan = (REG_FPSR & FPCC_NAN) != 0;
  int r = 0;
  switch (condition)
    {
      case 0x10:
      case 0x00:		return 0;					// False

      case 0x11:
      case 0x01:		return (z);					// Equal

      case 0x12:
      case 0x02:		return (!(nan || z || n));			// Greater Than

      case 0x13:
      case 0x03:		return (z || !(nan || n));			// Greater or Equal

      case 0x14:
      case 0x04:		return (n && !(nan || z));			// Less Than

      case 0x15:
      case 0x05:		return (z || (n && !nan));			// Less Than or Equal

      case 0x16:
      case 0x06:		return !nan && !z;

      case 0x17:
      case 0x07:		return !nan;

      case 0x18:
      case 0x08:		return nan;

      case 0x19:
      case 0x09:		return nan || z;

      case 0x1a:
      case 0x0a:		return (nan || !(n || z));			// Not Less Than or Equal

      case 0x1b:
      case 0x0b:		return (nan || z || !n);			// Not Less Than

      case 0x1c:
      case 0x0c:		return (nan || (n && !z));			// Not Greater or Equal Than

      case 0x1d:
      case 0x0d:		return (nan || z || n);				// Not Greater Than

      case 0x1e:
      case 0x0e:		return (!z);					// Not Equal

      case 0x1f:
      case 0x0f:		return 1;					// True

      default:		fatalerror("M68kFPU: test_condition: unhandled condition %02X\n", condition);
    }

  return r;
}

static uint8 READ_EA_8(int ea)
{
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);

  switch (mode)
    {
      case 0:		// Dn
	{
	  return REG_D[reg];
	}
      case 2: 	// (An)
	{
	  uint32 ea = REG_A[reg];
	  return m68ki_read_8(ea);
	}
      case 5:		// (d16, An)
	{
	  uint32 ea = EA_AY_DI_8();
	  return m68ki_read_8(ea);
	}
      case 6:		// (An) + (Xn) + d8
	{
	  uint32 ea = EA_AY_IX_8();
	  return m68ki_read_8(ea);
	}
      case 7:
	{
	  switch (reg)
	    {
	      case 0:		// (xxx).W
		{
		  uint32 ea = (uint32)OPER_I_16();
		  return m68ki_read_8(ea);
		}
	      case 1:		// (xxx).L
		{
		  uint32 d1 = OPER_I_16();
		  uint32 d2 = OPER_I_16();
		  uint32 ea = (d1 << 16) | d2;
		  return m68ki_read_8(ea);
		}
	      case 4:		// #<data>
		{
		  return  OPER_I_8();
		}
	      default:	fatalerror("M68kFPU: READ_EA_8: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
	    }
	  break;
	}
      default:	fatalerror("M68kFPU: READ_EA_8: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
    }

  return 0;
}

static uint16 READ_EA_16(int ea)
{
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);

  switch (mode)
    {
      case 0:		// Dn
	{
			return (uint16)(REG_D[reg]);
	}
      case 2:		// (An)
	{
	  uint32 ea = REG_A[reg];
	  return m68ki_read_16(ea);
	}
      case 5:		// (d16, An)
	{
	  uint32 ea = EA_AY_DI_16();
	  return m68ki_read_16(ea);
	}
      case 6:		// (An) + (Xn) + d8
	{
	  uint32 ea = EA_AY_IX_16();
	  return m68ki_read_16(ea);
	}
      case 7:
	{
	  switch (reg)
	    {
	      case 0:		// (xxx).W
		{
		  uint32 ea = (uint32)OPER_I_16();
		  return m68ki_read_16(ea);
		}
	      case 1:		// (xxx).L
		{
		  uint32 d1 = OPER_I_16();
		  uint32 d2 = OPER_I_16();
		  uint32 ea = (d1 << 16) | d2;
		  return m68ki_read_16(ea);
		}
	      case 4:		// #<data>
		{
		  return OPER_I_16();
		}

	      default:	fatalerror("M68kFPU: READ_EA_16: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
	    }
	  break;
	}
      default:	fatalerror("M68kFPU: READ_EA_16: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
    }

  return 0;
}

static uint32 READ_EA_32(int ea)
{
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);

  switch (mode)
    {
      case 0:		// Dn
	{
	  return REG_D[reg];
	}
      case 2:		// (An)
	{
	  uint32 ea = REG_A[reg];
	  return m68ki_read_32(ea);
	}
      case 3:		// (An)+
	{
	  uint32 ea = EA_AY_PI_32();
	  return m68ki_read_32(ea);
	}
      case 5:		// (d16, An)
	{
	  uint32 ea = EA_AY_DI_32();
	  return m68ki_read_32(ea);
	}
      case 6:		// (An) + (Xn) + d8
	{
	  uint32 ea = EA_AY_IX_32();
	  return m68ki_read_32(ea);
	}
      case 7:
	{
	  switch (reg)
	    {
	      case 0:		// (xxx).W
		{
		  uint32 ea = (uint32)OPER_I_16();
		  return m68ki_read_32(ea);
		}
	      case 1:		// (xxx).L
		{
		  uint32 d1 = OPER_I_16();
		  uint32 d2 = OPER_I_16();
		  uint32 ea = (d1 << 16) | d2;
		  return m68ki_read_32(ea);
		}
	      case 2:		// (d16, PC)
		{
		  uint32 ea = EA_PCDI_32();
		  return m68ki_read_32(ea);
		}
	      case 4:		// #<data>
		{
		  return  OPER_I_32();
		}
	      default:	fatalerror("M68kFPU: READ_EA_32: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
	    }
	  break;
	}
      default:	fatalerror("M68kFPU: READ_EA_32: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
    }
  return 0;
}

static uint64 READ_EA_64(int ea)
{
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);
  uint32 h1, h2;

  switch (mode)
    {
      case 2:		// (An)
	{
	  uint32 ea = REG_A[reg];
	  h1 = m68ki_read_32(ea+0);
	  h2 = m68ki_read_32(ea+4);
	  return  (uint64)(h1) << 32 | (uint64)(h2);
	}
      case 3:		// (An)+
	{
	  uint32 ea = REG_A[reg];
	  REG_A[reg] += 8;
	  h1 = m68ki_read_32(ea+0);
	  h2 = m68ki_read_32(ea+4);
	  return  (uint64)(h1) << 32 | (uint64)(h2);
	}
      case 5:		// (d16, An)
	{
	  uint32 ea = EA_AY_DI_32();
	  h1 = m68ki_read_32(ea+0);
	  h2 = m68ki_read_32(ea+4);
	  return  (uint64)(h1) << 32 | (uint64)(h2);
	}
      case 7:
	{
	  switch (reg)
	    {
	      case 4:		// #<data>
		{
		  h1 = OPER_I_32();
		  h2 = OPER_I_32();
		  return  (uint64)(h1) << 32 | (uint64)(h2);
		}
	      case 2:		// (d16, PC)
		{
		  uint32 ea = EA_PCDI_32();
		  h1 = m68ki_read_32(ea+0);
		  h2 = m68ki_read_32(ea+4);
		  return  (uint64)(h1) << 32 | (uint64)(h2);
		}
	      default:	fatalerror("M68kFPU: READ_EA_64: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
	    }
	  break;
	}
      default:	fatalerror("M68kFPU: READ_EA_64: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
    }

  return 0;
}


static floatx80 READ_EA_FPE(int mode, int reg, uint32 di_mode_ea)
{
  floatx80 fpr;

  switch (mode)
    {
      case 2:		// (An)
	{
	  uint32 ea = REG_A[reg];
	  fpr = load_extended_float80(ea);
	  break;
	}

      case 3:		// (An)+
	{
	  uint32 ea = REG_A[reg];
	  REG_A[reg] += 12;
	  fpr = load_extended_float80(ea);
	  break;
	}
      case 5:		// (d16, An)  (added by JFF)
	{
	  fpr = load_extended_float80(di_mode_ea);
         
	  break;

	}
      case 7:	// extended modes
	{
	  switch (reg)
	    {
	      case 2:	// (d16, PC)
		{
		  uint32 ea = EA_PCDI_32();
		  fpr = load_extended_float80(ea);
		}
		break;

	      case 3:	// (d16,PC,Dx.w)
		{
		  uint32 ea = EA_PCIX_32();
		  fpr = load_extended_float80(ea);
		}
		break;
	      case 4: // immediate (JFF)
		{
		  uint32 ea = REG_PC;
		  fpr = load_extended_float80(ea);
		  REG_PC += 12;
		}
		break;
	      default:
		fatalerror("M68kFPU: READ_EA_FPE: unhandled mode %d, reg %d, at %08X\n", mode, reg, REG_PC);
		break;
	    }
	}
	break;

      default:	fatalerror("M68kFPU: READ_EA_FPE: unhandled mode %d, reg %d, at %08X\n", mode, reg, REG_PC); break;
    }

  return fpr;
}

static floatx80 READ_EA_PACK(int ea)
{
  floatx80 fpr;
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);

  switch (mode)
    {
      case 2:		// (An)
	{
	  uint32 ea = REG_A[reg];
	  fpr = load_pack_float80(ea);
	  break;
	}

      case 3:		// (An)+
	{
	  uint32 ea = REG_A[reg];
	  REG_A[reg] += 12;
	  fpr = load_pack_float80(ea);
	  break;
	}

      case 7:	// extended modes
	{
	  switch (reg)
	    {
	      case 3:	// (d16,PC,Dx.w)
		{
		  uint32 ea = EA_PCIX_32();
		  fpr = load_pack_float80(ea);
		}
		break;

	      default:
		fatalerror("M68kFPU: READ_EA_PACK: unhandled mode %d, reg %d, at %08X\n", mode, reg, REG_PC);
		break;
	    }
	}
	break;

      default:	fatalerror("M68kFPU: READ_EA_PACK: unhandled mode %d, reg %d, at %08X\n", mode, reg, REG_PC); break;
    }

  return fpr;
}

static void WRITE_EA_8(int ea, uint8 data)
{
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);

  switch (mode)
    {
      case 0:		// Dn
	{
	  REG_D[reg] = data;
	  break;
	}
      case 2:		// (An)
	{
	  uint32 ea = REG_A[reg];
	  m68ki_write_8(ea, data);
	  break;
	}
      case 3:		// (An)+
	{
	  uint32 ea = EA_AY_PI_8();
	  m68ki_write_8(ea, data);
	  break;
	}
      case 4:		// -(An)
	{
	  uint32 ea = EA_AY_PD_8();
	  m68ki_write_8(ea, data);
	  break;
	}
      case 5:		// (d16, An)
	{
	  uint32 ea = EA_AY_DI_8();
	  m68ki_write_8(ea, data);
	  break;
	}
      case 6:		// (An) + (Xn) + d8
	{
	  uint32 ea = EA_AY_IX_8();
	  m68ki_write_8(ea, data);
	  break;
	}
      case 7:
	{
	  switch (reg)
	    {
	      case 1:		// (xxx).B
		{
		  uint32 d1 = OPER_I_16();
		  uint32 d2 = OPER_I_16();
		  uint32 ea = (d1 << 16) | d2;
		  m68ki_write_8(ea, data);
		  break;
		}
	      case 2:		// (d16, PC)
		{
		  uint32 ea = EA_PCDI_16();
		  m68ki_write_8(ea, data);
		  break;
		}
	      default:	fatalerror("M68kFPU: WRITE_EA_8: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
	    }
	  break;
	}
      default:	fatalerror("M68kFPU: WRITE_EA_8: unhandled mode %d, reg %d, data %08X at %08X\n", mode, reg, data, REG_PC);
    }
}

static void WRITE_EA_16(int ea, uint16 data)
{
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);

  switch (mode)
    {
      case 0:		// Dn
	{
	  REG_D[reg] = data;
	  break;
	}
      case 2:		// (An)
	{
	  uint32 ea = REG_A[reg];
	  m68ki_write_16(ea, data);
	  break;
	}
      case 3:		// (An)+
	{
	  uint32 ea = EA_AY_PI_16();
	  m68ki_write_16(ea, data);
	  break;
	}
      case 4:		// -(An)
	{
	  uint32 ea = EA_AY_PD_16();
	  m68ki_write_16(ea, data);
	  break;
	}
      case 5:		// (d16, An)
	{
	  uint32 ea = EA_AY_DI_16();
	  m68ki_write_16(ea, data);
	  break;
	}
      case 6:		// (An) + (Xn) + d8
	{
	  uint32 ea = EA_AY_IX_16();
	  m68ki_write_16(ea, data);
	  break;
	}
      case 7:
	{
	  switch (reg)
	    {
	      case 1:		// (xxx).W
		{
		  uint32 d1 = OPER_I_16();
		  uint32 d2 = OPER_I_16();
		  uint32 ea = (d1 << 16) | d2;
		  m68ki_write_16(ea, data);
		  break;
		}
	      case 2:		// (d16, PC)
		{
		  uint32 ea = EA_PCDI_16();
		  m68ki_write_16(ea, data);
		  break;
		}
	      default:	fatalerror("M68kFPU: WRITE_EA_16: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
	    }
	  break;
	}
      default:	fatalerror("M68kFPU: WRITE_EA_16: unhandled mode %d, reg %d, data %08X at %08X\n", mode, reg, data, REG_PC);
    }
}

static void WRITE_EA_32(int ea, uint32 data)
{
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);

  switch (mode)
    {
      case 0:		// Dn
	{
	  REG_D[reg] = data;
	  break;
	}
      case 1:		// An
	{
	  REG_A[reg] = data;
	  break;
	}
      case 2:		// (An)
	{
	  uint32 ea = REG_A[reg];
	  m68ki_write_32(ea, data);
	  break;
	}
      case 3:		// (An)+
	{
	  uint32 ea = EA_AY_PI_32();
	  m68ki_write_32(ea, data);
	  break;
	}
      case 4:		// -(An)
	{
	  uint32 ea = EA_AY_PD_32();
	  m68ki_write_32(ea, data);
	  break;
	}
      case 5:		// (d16, An)
	{
	  uint32 ea = EA_AY_DI_32();
	  m68ki_write_32(ea, data);
	  break;
	}
      case 6:		// (An) + (Xn) + d8
	{
	  uint32 ea = EA_AY_IX_32();
	  m68ki_write_32(ea, data);
	  break;
	}
      case 7:
	{
	  switch (reg)
	    {
	      case 1:		// (xxx).L
		{
		  uint32 d1 = OPER_I_16();
		  uint32 d2 = OPER_I_16();
		  uint32 ea = (d1 << 16) | d2;
                  m68ki_write_32(ea, data);
                  break;
		}
	      case 2:		// (d16, PC)
		{
		  uint32 ea = EA_PCDI_32();
                  m68ki_write_32(ea, data);
                  break;
		}
	      default:	fatalerror("M68kFPU: WRITE_EA_32: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
	    }
	  break;
	}
      default:	fatalerror("M68kFPU: WRITE_EA_32: unhandled mode %d, reg %d, data %08X at %08X\n", mode, reg, data, REG_PC);
    }
}

static void WRITE_EA_64(int ea, uint64 data)
{
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);

  switch (mode)
    {
      case 2:		// (An)
	{
	  uint32 ea = REG_A[reg];
	  m68ki_write_32(ea, (uint32)(data >> 32));
	  m68ki_write_32(ea+4, (uint32)(data));
	  break;
	}
      case 4:		// -(An)
	{
	  uint32 ea;
	  REG_A[reg] -= 8;
	  ea = REG_A[reg];
	  m68ki_write_32(ea+0, (uint32)(data >> 32));
	  m68ki_write_32(ea+4, (uint32)(data));
	  break;
	}
      case 5:		// (d16, An)
	{
	  uint32 ea = EA_AY_DI_32();
	  m68ki_write_32(ea+0, (uint32)(data >> 32));
	  m68ki_write_32(ea+4, (uint32)(data));
	  break;
        }


      case 7:    // (TBB)
        {
          switch (reg)
            {
              case 1:		// (xxx).L
                {
                  uint32 d1 = OPER_I_16();
                  uint32 d2 = OPER_I_16();
                  uint32 ea = (d1 << 16) | d2;
                  m68ki_write_32(ea+0, (uint32)(data >> 32));
                  m68ki_write_32(ea+4, (uint32)(data));
                  break;
                }
              case 2:		// (d16, PC)
                {
                  uint32 ea = EA_PCDI_32();
                  m68ki_write_32(ea+0, (uint32)(data >> 32));
                  m68ki_write_32(ea+4, (uint32)(data));
                  break;
                }
              default:	fatalerror("M68kFPU: WRITE_EA_64: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
            }
          break;
        }




      default:	fatalerror("M68kFPU: WRITE_EA_64: unhandled mode %d, reg %d, data %08X%08X at %08X\n", mode, reg, (uint32)(data >> 32), (uint32)(data), REG_PC);
    }
}

static void WRITE_EA_FPE(int mode, int reg, floatx80 fpr, uint32 di_mode_ea)
{


  switch (mode)
    {
      case 2:		// (An)
	{
	  uint32 ea;
	  ea = REG_A[reg];
	  store_extended_float80(ea, fpr);
	  break;
	}

      case 3:		// (An)+
	{
	  uint32 ea;
	  ea = REG_A[reg];
	  store_extended_float80(ea, fpr);
	  REG_A[reg] += 12;
	  break;
	}

      case 4:		// -(An)
	{
	  uint32 ea;
	  REG_A[reg] -= 12;
	  ea = REG_A[reg];
	  store_extended_float80(ea, fpr);
	  break;
	}
      case 5:		// (d16, An)  (added by JFF)
	{
	  // EA_AY_DI_32() should not be done here because fmovem would increase
	  // PC each time, reading incorrect displacement & advancing PC too much
	  // uint32 ea = EA_AY_DI_32();
	  store_extended_float80(di_mode_ea, fpr);
         
	  break;

	}
      case 7:
	{
	  switch (reg)
	    {
	      default:	fatalerror("M68kFPU: WRITE_EA_FPE: unhandled mode %d, reg %d, at %08X\n", mode, reg, REG_PC);
	    }
	  break;
	}
      default:	fatalerror("M68kFPU: WRITE_EA_FPE: unhandled mode %d, reg %d, at %08X\n", mode, reg, REG_PC);
    }
}

static void WRITE_EA_PACK(int ea, int k, floatx80 fpr)
{
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);

  switch (mode)
    {
      case 2:		// (An)
	{
	  uint32 ea;
	  ea = REG_A[reg];
	  store_pack_float80(ea, k, fpr);
	  break;
	}

      case 3:		// (An)+
	{
	  uint32 ea;
	  ea = REG_A[reg];
	  store_pack_float80(ea, k, fpr);
	  REG_A[reg] += 12;
	  break;
	}

      case 4:		// -(An)
	{
	  uint32 ea;
	  REG_A[reg] -= 12;
	  ea = REG_A[reg];
	  store_pack_float80(ea, k, fpr);
	  break;
	}

      case 7:
	{
	  switch (reg)
	    {
	      default:	fatalerror("M68kFPU: WRITE_EA_PACK: unhandled mode %d, reg %d, at %08X\n", mode, reg, REG_PC);
	    }
	}
	break;
      default:	fatalerror("M68kFPU: WRITE_EA_PACK: unhandled mode %d, reg %d, at %08X\n", mode, reg, REG_PC);
    }
}


static void fpgen_rm_reg(uint16 w2)
{
  int ea = REG_IR & 0x3f;
  int rm = (w2 >> 14) & 0x1;
  int src = (w2 >> 10) & 0x7;
  int dst = (w2 >>  7) & 0x7;
  int opmode = w2 & 0x7f;
  floatx80 source;

  // fmovecr #$f, fp0	f200 5c0f

  if (rm)
    {
      switch (src)
	{
	  case 0:		// Long-Word Integer
	    {
	      sint32 d = READ_EA_32(ea);
	      source = int32_to_floatx80(d);
	      break;
	    }
	  case 1:		// Single-precision Real
	    {
	      uint32 d = READ_EA_32(ea);
	      source = float32_to_floatx80(d);
	      break;
	    }
	  case 2:		// Extended-precision Real
	    {
	      int imode = (ea >> 3) & 0x7;
	      int reg = (ea & 0x7);
	      uint32 di_mode_ea = imode == 5 ? (REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16())) : 0;
	      source = READ_EA_FPE(imode,reg,di_mode_ea);
	      break;
	    }
	  case 3:		// Packed-decimal Real
	    {
	      source = READ_EA_PACK(ea);
	      break;
	    }
	  case 4:		// Word Integer
	    {
	      sint16 d = READ_EA_16(ea);
	      source = int32_to_floatx80((sint32)d);
	      break;
	    }
	  case 5:		// Double-precision Real
	    {
	      uint64 d = READ_EA_64(ea);

	      source = float64_to_floatx80(d);
	      break;
	    }
	  case 6:		// Byte Integer
	    {
	      sint8 d = READ_EA_8(ea);
	      source = int32_to_floatx80((sint32)d);
	      break;
	    }
	  case 7:		// FMOVECR load from constant ROM
	    {
	      switch (w2 & 0x7f)
		{
		  case 0x0:	// Pi
		    source.high = 0x4000;
		    source.low = U64(0xc90fdaa22168c235);
		    break;

		  case 0xb:	// log10(2)
		    source.high = 0x3ffd;
		    source.low = U64(0x9a209a84fbcff798);
		    break;

		  case 0xc:	// e
		    source.high = 0x4000;
		    source.low = U64(0xadf85458a2bb4a9b);
		    break;

		  case 0xd:	// log2(e)
		    source.high = 0x3fff;
		    source.low = U64(0xb8aa3b295c17f0bc);
		    break;

		  case 0xe:	// log10(e)
		    source.high = 0x3ffd;
		    source.low = U64(0xde5bd8a937287195);
		    break;

		  case 0xf:	// 0.0
		    source = int32_to_floatx80((sint32)0);
		    break;

		  case 0x30:	// ln(2)
		    source.high = 0x3ffe;
		    source.low = U64(0xb17217f7d1cf79ac);
		    break;

		  case 0x31:	// ln(10)
		    source.high = 0x4000;
		    source.low = U64(0x935d8dddaaa8ac17);
		    break;

		  case 0x32:	// 1 (or 100?  manuals are unclear, but 1 would make more sense)
		    source = int32_to_floatx80((sint32)1);
		    break;

		  case 0x33:	// 10^1
		    source = int32_to_floatx80((sint32)10);
		    break;

		  case 0x34:	// 10^2
		    source = int32_to_floatx80((sint32)10*10);
		    break;

		  default:
		    fatalerror("fmove_rm_reg: unknown constant ROM offset %x at %08x\n", w2&0x7f, REG_PC-4);
		    break;
		}

	      // handle it right here, the usual opmode bits aren't valid in the FMOVECR case
	      REG_FP[dst] = source;	      
	      SET_CONDITION_CODES(REG_FP[dst]); // JFF when destination is a register, we HAVE to update FPCR
	      USE_CYCLES(4);
	      return;
	    }
	  default:	fatalerror("fmove_rm_reg: invalid source specifier %x at %08X\n", src, REG_PC-4);
	}
    }
  else
    {
      source = REG_FP[src];
    }



  switch (opmode)
    {
      case 0x00:		// FMOVE
	{
	  REG_FP[dst] = source;
	  SET_CONDITION_CODES(REG_FP[dst]);  // JFF needs update condition codes
	  USE_CYCLES(4);
	  break;
	}
      case 0x01:		// Fsint
	{
          sint64 temp;
          temp = floatx80_to_int64(source);
          REG_FP[dst] = int64_to_floatx80(temp);
          SET_CONDITION_CODES(REG_FP[dst]);  // JFF needs update condition codes
          break;
        }
      case 0x03:		// FsintRZ
        {
          sint64 temp;
          temp = floatx80_to_int64_round_to_zero(source);
          REG_FP[dst] = int64_to_floatx80(temp);
          SET_CONDITION_CODES(REG_FP[dst]);  // JFF needs update condition codes
	  break;
	}
      case 0x04:		// FSQRT
	{
	  REG_FP[dst] = floatx80_sqrt(source);
	  SET_CONDITION_CODES(REG_FP[dst]);
	  USE_CYCLES(109);
	  break;
	}
      case 0x18:		// FABS
	{
	  REG_FP[dst] = source;
	  REG_FP[dst].high &= 0x7fff;
	  SET_CONDITION_CODES(REG_FP[dst]);
	  USE_CYCLES(3);
	  break;
	}
      case 0x1a:		// FNEG
	{
	  REG_FP[dst] = source;
	  REG_FP[dst].high ^= 0x8000;
	  SET_CONDITION_CODES(REG_FP[dst]);
	  USE_CYCLES(3);
          break;
        }
      case 0x1e:		// FGETEXP
	{
          sint16 temp;

          temp = source.high;	// get the exponent
          temp -= 0x3fff;	// take off the bias
          REG_FP[dst] = int32_to_floatx80((int32)temp);
          SET_CONDITION_CODES(REG_FP[dst]);
	  USE_CYCLES(6);
          break;
        }


      case 0x1f:		// FGETMANT  (TBB)
        {
          floatx80 temp = source;
          temp.high &= ~0x7fff;	// clear the bias, keep sign
          temp.high |= 0x3fff;	// set new bias, 1.0 <= result < 2.0
          REG_FP[dst] = temp;
          SET_CONDITION_CODES(REG_FP[dst]);
          USE_CYCLES(6);
          break;
        }


      case 0x60:		// FSDIVS (JFF) (source has already been converted to floatx80)
      case 0x20:		// FDIV
	{
	  REG_FP[dst] = floatx80_div(REG_FP[dst], source);
	  SET_CONDITION_CODES(REG_FP[dst]); // JFF
	  USE_CYCLES(43);
	  break;
	}
      case 0x22:		// FADD
	{
	  REG_FP[dst] = floatx80_add(REG_FP[dst], source);
	  SET_CONDITION_CODES(REG_FP[dst]);
	  USE_CYCLES(9);
	  break;
	}
 
      case 0x63:		// FSMULS (JFF) (source has already been converted to floatx80)
      case 0x23:		// FMUL
	{
	  REG_FP[dst] = floatx80_mul(REG_FP[dst], source);
	  SET_CONDITION_CODES(REG_FP[dst]);
	  USE_CYCLES(11);  // maybe number of cycles is slightly inferior for FSMULS
	  break;
	}
      case 0x25:		// FREM
	{
	  REG_FP[dst] = floatx80_rem(REG_FP[dst], source);
	  SET_CONDITION_CODES(REG_FP[dst]);
	  USE_CYCLES(43);	// guess
	  break;
	}
      case 0x28:		// FSUB
	{
	  REG_FP[dst] = floatx80_sub(REG_FP[dst], source);
	  SET_CONDITION_CODES(REG_FP[dst]);
	  USE_CYCLES(9);
	  break;
	}
      case 0x38:		// FCMP
	{
	  floatx80 res;
	  res = floatx80_sub(REG_FP[dst], source);
	  SET_CONDITION_CODES(res);
	  USE_CYCLES(7);
	  break;
	}
      case 0x3a:		// FTST
	{
	  floatx80 res;
	  res = source;
	  SET_CONDITION_CODES(res);
	  USE_CYCLES(7);
	  break;
	}

      default:	fatalerror("fpgen_rm_reg: unimplemented opmode %02X at %08X\n", opmode, REG_PC-4);
    }
}

static void fmove_reg_mem(uint16 w2)
{
  int ea = REG_IR & 0x3f;
  int src = (w2 >>  7) & 0x7;
  int dst = (w2 >> 10) & 0x7;
  int k = (w2 & 0x7f);

  switch (dst)
    {
      case 0:		// Long-Word Integer
	{
	  sint32 d = (sint32)floatx80_to_int32(REG_FP[src]);
	  WRITE_EA_32(ea, d);
	  break;
	}
      case 1:		// Single-precision Real
	{
	  uint32 d = floatx80_to_float32(REG_FP[src]);
	  WRITE_EA_32(ea, d);
	  break;
	}
      case 2:		// Extended-precision Real
	{
	  int mode = (ea >> 3) & 0x7;
	  int reg = (ea & 0x7);
	  uint32 di_mode_ea = mode == 5 ? (REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16())) : 0;
	    		      
	  WRITE_EA_FPE(mode, reg, REG_FP[src], di_mode_ea);
	  break;
	}
      case 3:		// Packed-decimal Real with Static K-factor
	{
	  // sign-extend k
	  k = (k & 0x40) ? (k | 0xffffff80) : (k & 0x7f);
	  WRITE_EA_PACK(ea, k, REG_FP[src]);
	  break;
	}
      case 4:		// Word Integer
	{
	  WRITE_EA_16(ea, (sint16)floatx80_to_int32(REG_FP[src]));
	  break;
	}
      case 5:		// Double-precision Real
	{
	  uint64 d;

	  d = floatx80_to_float64(REG_FP[src]);

	  WRITE_EA_64(ea, d);
	  break;
	}
      case 6:		// Byte Integer
	{
	  WRITE_EA_8(ea, (sint8)floatx80_to_int32(REG_FP[src]));
	  break;
	}
      case 7:		// Packed-decimal Real with Dynamic K-factor
	{
	  WRITE_EA_PACK(ea, REG_D[k>>4], REG_FP[src]);
	  break;
	}
    }

  USE_CYCLES(12);
}

static void fmove_fpcr(uint16 w2)
{
  int ea = REG_IR & 0x3f;
  int dir = (w2 >> 13) & 0x1;
  int reg = (w2 >> 10) & 0x7;

  if (dir)	// From system control reg to <ea>
    {
      if (reg & 4) WRITE_EA_32(ea, REG_FPCR);
      if (reg & 2) WRITE_EA_32(ea, REG_FPSR);
      if (reg & 1) WRITE_EA_32(ea, REG_FPIAR);
    }
  else		// From <ea> to system control reg
    {
      if (reg & 4) 
	{
	  REG_FPCR = READ_EA_32(ea);
	  // JFF: need to update rounding mode from softfloat module
	  float_rounding_mode = (REG_FPCR >> 4) & 0x3;
	  
	}
	  
      if (reg & 2) REG_FPSR = READ_EA_32(ea);
      if (reg & 1) REG_FPIAR = READ_EA_32(ea);
    }

  USE_CYCLES(10);
}

static void fmovem(uint16 w2)
{
  int i;
  int ea = REG_IR & 0x3f;
  int dir = (w2 >> 13) & 0x1;
  int mode = (w2 >> 11) & 0x3;
  int reglist = w2 & 0xff;

  if (dir)	// From FP regs to mem
    {
      /* Mode field: Specifies the type of the register list and addressing mode.
	 1 & 3 are unsupported right now
	 1  Dynamic register list, predecrement addressing mode.
	   3  Dynamic register list, postincrement or control addressing mode.
       */
      
      switch (mode)
	{
	  case 2:		// (JFF): Static register list, postincrement or control addressing mode.     
	    {
	      int imode = (ea >> 3) & 0x7;
	      int reg = (ea & 0x7);
	      int di_mode = imode == 5;	      
	      
	      uint32 di_mode_ea = di_mode ? (REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16())) : 0;
	    		      
		      
	      for (i=0; i < 8; i++)
		{
		  if (reglist & (1 << i))
		    {
		      WRITE_EA_FPE(imode,reg, REG_FP[7-i],di_mode_ea);
		      USE_CYCLES(2);
		      if (di_mode)
			{
			  di_mode_ea += 12;
			}
		      
		    }
		}
	      break;
	    }
	  case 0:		// Static register list, predecrement addressing mode
	    {
	      int imode = (ea >> 3) & 0x7;
	      int reg = (ea & 0x7);
	      // the "di_mode_ea" parameter kludge is required here else WRITE_EA_FPE would have
	      // to call EA_AY_DI_32() (that advances PC & reads displacement) each time
	      // when the proper behaviour is 1) read once, 2) increment ea for each matching register
	      // this forces to pre-read the mode (named "imode") so we can decide to read displacement, only once
	      int di_mode = imode == 5;	      
	      uint32 di_mode_ea =  di_mode ? (REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16())) : 0;

	      for (i=0; i < 8; i++)
		{
		  if (reglist & (1 << i))
		    {
		      WRITE_EA_FPE(imode,reg, REG_FP[i],di_mode_ea);
		      USE_CYCLES(2);
		      if (di_mode)
			{
			  di_mode_ea += 12;
			}
		    }
		}
	      break;
	    }

	  default:	fatalerror("040fpu0: FMOVEM: mode %d unimplemented at %08X\n", mode, REG_PC-4);
	}
    }
  else		// From mem to FP regs
    {
      switch (mode)
	{
	  case 2:		// Static register list, postincrement addressing mode
	    {
	      int imode = (ea >> 3) & 0x7;
	      int reg = (ea & 0x7);
	      int di_mode = imode == 5;	    
	      uint32 di_mode_ea = di_mode ? (REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16())) : 0;
	      
	      for (i=0; i < 8; i++)
		{
		  if (reglist & (1 << i))
		    {
		      REG_FP[7-i] = READ_EA_FPE(imode,reg,di_mode_ea);
		      USE_CYCLES(2);
		      if (di_mode)
			{
			  di_mode_ea += 12;
			}
		      
		    }
		}
	      break;
	    }

	  default:	fatalerror("040fpu0: FMOVEM: mode %d unimplemented at %08X\n", mode, REG_PC-4);
	}
    }
}


static void fscc()
{
  // added by JFF, this seems to work properly now 
  int condition = OPER_I_16() & 0x3f;

  int cc = TEST_CONDITION(condition);
  
  if ((REG_IR & 0x38) == 0)
    {
      // If the specified floating-point condition is true, sets the byte integer operand at
      // the destination to TRUE (all ones); otherwise, sets the byte to FALSE (all zeros).
      
      REG_D[REG_IR & 7] = (REG_D[REG_IR & 7] & 0xFFFFFF00) | (cc ? 0xff : 0x00);
    }
  else
    {
      // unimplemented
      fatalerror("040fpu0: fscc: memory not implemented at %08X\n", REG_PC-4);
    }
  USE_CYCLES(7);  // JFF unsure of the number of cycles!!
}

static void fbcc16(void)
{
  sint32 offset;
  int condition = REG_IR & 0x3f;

  offset = (sint16)(OPER_I_16());

  // TODO: condition and jump!!!
  if (TEST_CONDITION(condition))
    {
      m68ki_trace_t0();			   /* auto-disable (see m68kcpu.h) */
      m68ki_branch_16(offset-2);
    }

  USE_CYCLES(7);
}

static void fbcc32(void)
{
  sint32 offset;
  int condition = REG_IR & 0x3f;

  offset = OPER_I_32();

  // TODO: condition and jump!!!
  if (TEST_CONDITION(condition))
    {
      m68ki_trace_t0();			   /* auto-disable (see m68kcpu.h) */
      m68ki_branch_32(offset-4);
    }

  USE_CYCLES(7);
}


void m68040_fpu_op0()
{
  m68ki_cpu.fpu_just_reset = 0;

  switch ((REG_IR >> 6) & 0x3)
    {
      case 0:
	{
   	uint16 w2 = OPER_I_16();
	  switch ((w2 >> 13) & 0x7)
	    {
	      case 0x0:	// FPU ALU FP, FP
	      case 0x2:	// FPU ALU ea, FP
                {
                  fpgen_rm_reg(w2);
		  break;
		}

	      case 0x3:	// FMOVE FP, ea
		{
		  fmove_reg_mem(w2);
		  break;
		}

	      case 0x4:	// FMOVEM ea, FPCR
	      case 0x5:	// FMOVEM FPCR, ea
		{
		  fmove_fpcr(w2);
		  break;
		}

	      case 0x6:	// FMOVEM ea, list
	      case 0x7:	// FMOVEM list, ea
		{
		  fmovem(w2);
		  break;
		}

	      default:	fatalerror("M68kFPU: unimplemented subop %d at %08X\n", (w2 >> 13) & 0x7, REG_PC-4);
	    }
	  break;
	}

      case 1:           // FScc (JFF)
	{
	  fscc();
	  break;
	}
      case 2:		// FBcc disp16
	{
	  fbcc16();
	  break;
	}
      case 3:		// FBcc disp32
	{
	  fbcc32();
	  break;
	}

      default:	fatalerror("M68kFPU: unimplemented main op %d at %08X\n", (m68ki_cpu.ir >> 6) & 0x3,  REG_PC-4);
    }
}

static void perform_fsave(uint32 addr, int inc)
{
  if (inc)
    {
      // 68881 IDLE, version 0x1f
      m68ki_write_32(addr, 0x1f180000);
      m68ki_write_32(addr+4, 0);
      m68ki_write_32(addr+8, 0);
      m68ki_write_32(addr+12, 0);
      m68ki_write_32(addr+16, 0);
      m68ki_write_32(addr+20, 0);
      m68ki_write_32(addr+24, 0x70000000);
    }
  else
    {
      m68ki_write_32(addr, 0x70000000);
      m68ki_write_32(addr-4, 0);
      m68ki_write_32(addr-8, 0);
      m68ki_write_32(addr-12, 0);
      m68ki_write_32(addr-16, 0);
      m68ki_write_32(addr-20, 0);
      m68ki_write_32(addr-24, 0x1f180000);
    }
}

// FRESTORE on a NULL frame reboots the FPU - all registers to NaN, the 3 status regs to 0
static void do_frestore_null()
{
  int i;

  REG_FPCR = 0;
  REG_FPSR = 0;
  REG_FPIAR = 0;
  for (i = 0; i < 8; i++)
    {
      REG_FP[i].high = 0x7fff;
      REG_FP[i].low = U64(0xffffffffffffffff);
    }

  // Mac IIci at 408458e6 wants an FSAVE of a just-restored NULL frame to also be NULL
  // The PRM says it's possible to generate a NULL frame, but not how/when/why.  (need the 68881/68882 manual!)
  m68ki_cpu.fpu_just_reset = 1;
}

void m68040_fpu_op1()
{
  int ea = REG_IR & 0x3f;
  int mode = (ea >> 3) & 0x7;
  int reg = (ea & 0x7);
  uint32 addr, temp;

  switch ((REG_IR >> 6) & 0x3)
    {
      case 0:		// FSAVE <ea>
	{
	  switch (mode)
	    {
	      case 3:	// (An)+
		addr = EA_AY_PI_32();

		if (m68ki_cpu.fpu_just_reset)
		  {
		    m68ki_write_32(addr, 0);
		  }
		else
		  {
		    // we normally generate an IDLE frame
		    REG_A[reg] += 6*4;
		    perform_fsave(addr, 1);
		  }
		break;

	      case 4: // -(An)
		addr = EA_AY_PD_32();

		if (m68ki_cpu.fpu_just_reset)
		  {
		    m68ki_write_32(addr, 0);
		  }
		else
		  {
		    // we normally generate an IDLE frame
		    REG_A[reg] -= 6*4;
		    perform_fsave(addr, 0);
		  }
		break;

	      default:
		fatalerror("M68kFPU: FSAVE unhandled mode %d reg %d at %x\n", mode, reg, REG_PC);
	    }
	  break;
	}
	break;

      case 1:		// FRESTORE <ea>
	{
	  switch (mode)
	    {
	      case 2: // (An)
		addr = REG_A[reg];
		temp = m68ki_read_32(addr);

		// check for NULL frame
		if (temp & 0xff000000)
		  {
		    // we don't handle non-NULL frames and there's no pre/post inc/dec to do here
		    m68ki_cpu.fpu_just_reset = 0;
		  }
		else
		  {
		    do_frestore_null();
		  }
		break;

	      case 3:	// (An)+
		addr = EA_AY_PI_32();
		temp = m68ki_read_32(addr);

		// check for NULL frame
		if (temp & 0xff000000)
		  {
		    m68ki_cpu.fpu_just_reset = 0;

		    // how about an IDLE frame?
		    if ((temp & 0x00ff0000) == 0x00180000)
		      {
			REG_A[reg] += 6*4;
		      } // check UNIMP
		    else if ((temp & 0x00ff0000) == 0x00380000)
		      {
			REG_A[reg] += 14*4;
		      } // check BUSY
		    else if ((temp & 0x00ff0000) == 0x00b40000)
		      {
			REG_A[reg] += 45*4;
		      }
		  }
		else
		  {
		    do_frestore_null();
		  }
		break;

	      default:
		fatalerror("M68kFPU: FRESTORE unhandled mode %d reg %d at %x\n", mode, reg, REG_PC);
	    }
	  break;
	}
	break;

      default:	fatalerror("m68040_fpu_op1: unimplemented op %d at %08X\n", (REG_IR >> 6) & 0x3, REG_PC-2);
    }
}



