e5da3227d9
- inserted missing break - use temp variable instead of manipulating dest register fgetexp changed to directly convert int to floatx80
1823 lines
37 KiB
C
1823 lines
37 KiB
C
#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 *)∈
|
|
|
|
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_32: 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
|
|
{
|
|
sint32 temp;
|
|
temp = floatx80_to_int32(source);
|
|
REG_FP[dst] = int32_to_floatx80(temp);
|
|
SET_CONDITION_CODES(REG_FP[dst]); // JFF needs update condition codes
|
|
break;
|
|
}
|
|
case 0x03: // FsintRZ
|
|
{
|
|
sint32 temp;
|
|
temp = floatx80_to_int32_round_to_zero(source);
|
|
REG_FP[dst] = int32_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);
|
|
}
|
|
}
|
|
|
|
|
|
|