ack/plat/linux68k/emu/musashi/m68kfpu.c
tevorbl 04fe0aa53e fixed floats in printf in linux59k platform
problem was implementation of fint & fintrz floating point ops in m68kfpu.c
Both these ops truncated the integer to 32 bits instead of leaving it as an extended precision floating point number

Changed the implementation to use 64 bit ints instead of 32 bit ints.
2020-06-01 20:53:29 +01:00

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 *)&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);
}
}