Fix lfs/stfs with SNaNs.

This commit is contained in:
Andrew Church 2015-01-18 07:02:50 +09:00
parent 42b55c60e7
commit b7761beee9
2 changed files with 88 additions and 8 deletions

View File

@ -3059,7 +3059,16 @@ private:
void LFSX(u32 frd, u32 ra, u32 rb) void LFSX(u32 frd, u32 ra, u32 rb)
{ {
const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb];
CPU.FPR[frd] = vm::get_ref<be_t<float>>(vm::cast(addr)).value(); float val = vm::get_ref<be_t<float>>(vm::cast(addr)).value();
if (!FPRdouble::IsNaN(val))
{
CPU.FPR[frd] = val;
}
else
{
u64 bits = (u32&)val;
(u64&)CPU.FPR[frd] = (bits & 0x80000000) << 32 | 7ULL << 60 | (bits & 0x7fffffff) << 29;
}
} }
void SRW(u32 ra, u32 rs, u32 rb, bool rc) void SRW(u32 ra, u32 rs, u32 rb, bool rc)
{ {
@ -3120,7 +3129,16 @@ private:
void LFSUX(u32 frd, u32 ra, u32 rb) void LFSUX(u32 frd, u32 ra, u32 rb)
{ {
const u64 addr = CPU.GPR[ra] + CPU.GPR[rb]; const u64 addr = CPU.GPR[ra] + CPU.GPR[rb];
CPU.FPR[frd] = vm::get_ref<be_t<float>>(vm::cast(addr)).value(); float val = vm::get_ref<be_t<float>>(vm::cast(addr)).value();
if (!FPRdouble::IsNaN(val))
{
CPU.FPR[frd] = val;
}
else
{
u64 bits = (u32&)val;
(u64&)CPU.FPR[frd] = (bits & 0x80000000) << 32 | 7ULL << 60 | (bits & 0x7fffffff) << 29;
}
CPU.GPR[ra] = addr; CPU.GPR[ra] = addr;
} }
void SYNC(u32 l) void SYNC(u32 l)
@ -3176,7 +3194,17 @@ private:
void STFSX(u32 frs, u32 ra, u32 rb) void STFSX(u32 frs, u32 ra, u32 rb)
{ {
const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb];
vm::get_ref<be_t<float>>(vm::cast(addr)) = (float)CPU.FPR[frs]; double val = CPU.FPR[frs];
if (!FPRdouble::IsNaN(val))
{
vm::get_ref<be_t<float>>(vm::cast(addr)) = (float)val;
}
else
{
u64 bits = (u64&)val;
u32 bits32 = (bits>>32 & 0x80000000) | (bits>>29 & 0x7fffffff);
vm::get_ref<be_t<u32>>(vm::cast(addr)) = (float)bits32;
}
} }
void STVRX(u32 vs, u32 ra, u32 rb) void STVRX(u32 vs, u32 ra, u32 rb)
{ {
@ -3188,7 +3216,17 @@ private:
void STFSUX(u32 frs, u32 ra, u32 rb) void STFSUX(u32 frs, u32 ra, u32 rb)
{ {
const u64 addr = CPU.GPR[ra] + CPU.GPR[rb]; const u64 addr = CPU.GPR[ra] + CPU.GPR[rb];
vm::get_ref<be_t<float>>(vm::cast(addr)) = (float)CPU.FPR[frs]; double val = CPU.FPR[frs];
if (!FPRdouble::IsNaN(val))
{
vm::get_ref<be_t<float>>(vm::cast(addr)) = (float)val;
}
else
{
u64 bits = (u64&)val;
u32 bits32 = (bits>>32 & 0x80000000) | (bits>>29 & 0x7fffffff);
vm::get_ref<be_t<u32>>(vm::cast(addr)) = (float)bits32;
}
CPU.GPR[ra] = addr; CPU.GPR[ra] = addr;
} }
void STSWI(u32 rd, u32 ra, u32 nb) void STSWI(u32 rd, u32 ra, u32 nb)
@ -3459,12 +3497,30 @@ private:
void LFS(u32 frd, u32 ra, s32 d) void LFS(u32 frd, u32 ra, s32 d)
{ {
const u64 addr = ra ? CPU.GPR[ra] + d : d; const u64 addr = ra ? CPU.GPR[ra] + d : d;
CPU.FPR[frd] = vm::get_ref<be_t<float>>(vm::cast(addr)).value(); float val = vm::get_ref<be_t<float>>(vm::cast(addr)).value();
if (!FPRdouble::IsNaN(val))
{
CPU.FPR[frd] = val;
}
else
{
u64 bits = (u32&)val;
(u64&)CPU.FPR[frd] = (bits & 0x80000000) << 32 | 7ULL << 60 | (bits & 0x7fffffff) << 29;
}
} }
void LFSU(u32 frd, u32 ra, s32 ds) void LFSU(u32 frd, u32 ra, s32 ds)
{ {
const u64 addr = CPU.GPR[ra] + ds; const u64 addr = CPU.GPR[ra] + ds;
CPU.FPR[frd] = vm::get_ref<be_t<float>>(vm::cast(addr)).value(); float val = vm::get_ref<be_t<float>>(vm::cast(addr)).value();
if (!FPRdouble::IsNaN(val))
{
CPU.FPR[frd] = val;
}
else
{
u64 bits = (u32&)val;
(u64&)CPU.FPR[frd] = (bits & 0x80000000) << 32 | 7ULL << 60 | (bits & 0x7fffffff) << 29;
}
CPU.GPR[ra] = addr; CPU.GPR[ra] = addr;
} }
void LFD(u32 frd, u32 ra, s32 d) void LFD(u32 frd, u32 ra, s32 d)
@ -3481,12 +3537,32 @@ private:
void STFS(u32 frs, u32 ra, s32 d) void STFS(u32 frs, u32 ra, s32 d)
{ {
const u64 addr = ra ? CPU.GPR[ra] + d : d; const u64 addr = ra ? CPU.GPR[ra] + d : d;
vm::get_ref<be_t<float>>(vm::cast(addr)) = (float)CPU.FPR[frs]; double val = CPU.FPR[frs];
if (!FPRdouble::IsNaN(val))
{
vm::get_ref<be_t<float>>(vm::cast(addr)) = (float)val;
}
else
{
u64 bits = (u64&)val;
u32 bits32 = (bits>>32 & 0x80000000) | (bits>>29 & 0x7fffffff);
vm::get_ref<be_t<u32>>(vm::cast(addr)) = (float)bits32;
}
} }
void STFSU(u32 frs, u32 ra, s32 d) void STFSU(u32 frs, u32 ra, s32 d)
{ {
const u64 addr = CPU.GPR[ra] + d; const u64 addr = CPU.GPR[ra] + d;
vm::get_ref<be_t<float>>(vm::cast(addr)) = (float)CPU.FPR[frs]; double val = CPU.FPR[frs];
if (!FPRdouble::IsNaN(val))
{
vm::get_ref<be_t<float>>(vm::cast(addr)) = (float)val;
}
else
{
u64 bits = (u64&)val;
u32 bits32 = (bits>>32 & 0x80000000) | (bits>>29 & 0x7fffffff);
vm::get_ref<be_t<u32>>(vm::cast(addr)) = (float)bits32;
}
CPU.GPR[ra] = addr; CPU.GPR[ra] = addr;
} }
void STFD(u32 frs, u32 ra, s32 d) void STFD(u32 frs, u32 ra, s32 d)

View File

@ -443,18 +443,22 @@ struct PPCdouble
PPCdouble() : _u64(0) PPCdouble() : _u64(0)
{ {
type = UpdateType();
} }
PPCdouble(double val) : _double(val) PPCdouble(double val) : _double(val)
{ {
type = UpdateType();
} }
PPCdouble(u64 val) : _u64(val) PPCdouble(u64 val) : _u64(val)
{ {
type = UpdateType();
} }
PPCdouble(u32 val) : _u64(val) PPCdouble(u32 val) : _u64(val)
{ {
type = UpdateType();
} }
}; };