cosmopolitan/tool/build/lib/fpu.c
2022-08-18 17:46:34 -07:00

1190 lines
30 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/math.h"
#include "libc/nt/struct/importobjectheader.internal.h"
#include "libc/str/str.h"
#include "tool/build/lib/case.h"
#include "tool/build/lib/endian.h"
#include "tool/build/lib/flags.h"
#include "tool/build/lib/fpu.h"
#include "tool/build/lib/ldbl.h"
#include "tool/build/lib/memory.h"
#include "tool/build/lib/modrm.h"
#include "tool/build/lib/pun.h"
#include "tool/build/lib/throw.h"
#define FPUREG 0
#define MEMORY 1
#define DISP(x, y, z) ((7 & (x)) << 4 | (y) << 3 | (z))
static void OnFpuStackOverflow(struct Machine *m) {
m->fpu.sw |= kFpuSwIe | kFpuSwC1 | kFpuSwSf;
}
static double OnFpuStackUnderflow(struct Machine *m) {
m->fpu.sw |= kFpuSwIe | kFpuSwSf;
m->fpu.sw &= ~kFpuSwC1;
return -NAN;
}
static double St(struct Machine *m, int i) {
if (FpuGetTag(m, i) == kFpuTagEmpty) OnFpuStackUnderflow(m);
return *FpuSt(m, i);
}
static double St0(struct Machine *m) {
return St(m, 0);
}
static double St1(struct Machine *m) {
return St(m, 1);
}
static double StRm(struct Machine *m) {
return St(m, ModrmRm(m->xedd->op.rde));
}
static void FpuClearRoundup(struct Machine *m) {
m->fpu.sw &= ~kFpuSwC1;
}
static void FpuClearOutOfRangeIndicator(struct Machine *m) {
m->fpu.sw &= ~kFpuSwC2;
}
static void FpuSetSt0(struct Machine *m, double x) {
*FpuSt(m, 0) = x;
}
static void FpuSetStRm(struct Machine *m, double x) {
*FpuSt(m, ModrmRm(m->xedd->op.rde)) = x;
}
static void FpuSetStPop(struct Machine *m, int i, double x) {
*FpuSt(m, i) = x;
FpuPop(m);
}
static void FpuSetStRmPop(struct Machine *m, double x) {
FpuSetStPop(m, ModrmRm(m->xedd->op.rde), x);
}
static int16_t FpuGetMemoryShort(struct Machine *m) {
uint8_t b[2];
return Read16(Load(m, m->fpu.dp, 2, b));
}
static int32_t FpuGetMemoryInt(struct Machine *m) {
uint8_t b[4];
return Read32(Load(m, m->fpu.dp, 4, b));
}
static int64_t FpuGetMemoryLong(struct Machine *m) {
uint8_t b[8];
return Read64(Load(m, m->fpu.dp, 8, b));
}
static float FpuGetMemoryFloat(struct Machine *m) {
union FloatPun u;
u.i = FpuGetMemoryInt(m);
return u.f;
}
static double FpuGetMemoryDouble(struct Machine *m) {
union DoublePun u;
u.i = FpuGetMemoryLong(m);
return u.f;
}
static void FpuSetMemoryShort(struct Machine *m, int16_t i) {
void *p[2];
uint8_t b[2];
Write16(BeginStore(m, m->fpu.dp, 2, p, b), i);
EndStore(m, m->fpu.dp, 2, p, b);
}
static void FpuSetMemoryInt(struct Machine *m, int32_t i) {
void *p[2];
uint8_t b[4];
Write32(BeginStore(m, m->fpu.dp, 4, p, b), i);
EndStore(m, m->fpu.dp, 4, p, b);
}
static void FpuSetMemoryLong(struct Machine *m, int64_t i) {
void *p[2];
uint8_t b[8];
Write64(BeginStore(m, m->fpu.dp, 8, p, b), i);
EndStore(m, m->fpu.dp, 8, p, b);
}
static void FpuSetMemoryFloat(struct Machine *m, float f) {
union FloatPun u = {f};
FpuSetMemoryInt(m, u.i);
}
static void FpuSetMemoryDouble(struct Machine *m, double f) {
union DoublePun u = {f};
FpuSetMemoryLong(m, u.i);
}
static double FpuGetMemoryLdbl(struct Machine *m) {
uint8_t b[10];
return DeserializeLdbl(Load(m, m->fpu.dp, 10, b));
}
static void FpuSetMemoryLdbl(struct Machine *m, double f) {
void *p[2];
uint8_t b[10], t[10];
SerializeLdbl(b, f);
memcpy(BeginStore(m, m->fpu.dp, 10, p, t), b, 10);
EndStore(m, m->fpu.dp, 10, p, t);
}
static double f2xm1(double x) {
return exp2(x) - 1;
}
static double fyl2x(double x, double y) {
return y * log2(x);
}
static double fyl2xp1(double x, double y) {
return y * log2(x + 1);
}
static double fscale(double significand, double exponent) {
if (isunordered(significand, exponent)) return NAN;
return ldexp(significand, exponent);
}
static double x87remainder(double x, double y, uint32_t *sw,
double rem(double, double), double rnd(double)) {
int s;
long q;
double r;
s = 0;
r = rem(x, y);
q = rnd(x / y);
s &= ~kFpuSwC2; /* ty libm */
if (q & 1) s |= kFpuSwC1;
if (q & 2) s |= kFpuSwC3;
if (q & 4) s |= kFpuSwC0;
if (sw) *sw = s | (*sw & ~(kFpuSwC0 | kFpuSwC1 | kFpuSwC2 | kFpuSwC3));
return r;
}
static double fprem(double dividend, double modulus, uint32_t *sw) {
return x87remainder(dividend, modulus, sw, fmod, trunc);
}
static double fprem1(double dividend, double modulus, uint32_t *sw) {
return x87remainder(dividend, modulus, sw, remainder, rint);
}
static double FpuAdd(struct Machine *m, double x, double y) {
if (!isunordered(x, y)) {
switch (isinf(y) << 1 | isinf(x)) {
case 0:
return x + y;
case 1:
return x;
case 2:
return y;
case 3:
if (signbit(x) == signbit(y)) {
return x;
} else {
m->fpu.sw |= kFpuSwIe;
return copysign(NAN, x);
}
default:
for (;;) (void)0;
}
} else {
return NAN;
}
}
static double FpuSub(struct Machine *m, double x, double y) {
if (!isunordered(x, y)) {
switch (isinf(y) << 1 | isinf(x)) {
case 0:
return x - y;
case 1:
return -x;
case 2:
return y;
case 3:
if (signbit(x) == signbit(y)) {
m->fpu.sw |= kFpuSwIe;
return copysign(NAN, x);
} else {
return y;
}
default:
for (;;) (void)0;
}
} else {
return NAN;
}
}
static double FpuMul(struct Machine *m, double x, double y) {
if (!isunordered(x, y)) {
if (!((isinf(x) && !y) || (isinf(y) && !x))) {
return x * y;
} else {
m->fpu.sw |= kFpuSwIe;
return -NAN;
}
} else {
return NAN;
}
}
static double FpuDiv(struct Machine *m, double x, double y) {
if (!isunordered(x, y)) {
if (x || y) {
if (y) {
return x / y;
} else {
m->fpu.sw |= kFpuSwZe;
return copysign(INFINITY, x);
}
} else {
m->fpu.sw |= kFpuSwIe;
return copysign(NAN, x);
}
} else {
return NAN;
}
}
static double FpuRound(struct Machine *m, double x) {
switch ((m->fpu.cw & kFpuCwRc) >> 10) {
case 0:
return rint(x);
case 1:
return floor(x);
case 2:
return ceil(x);
case 3:
return trunc(x);
default:
for (;;) (void)0;
}
}
static void FpuCompare(struct Machine *m, double y) {
double x = St0(m);
m->fpu.sw &= ~(kFpuSwC0 | kFpuSwC1 | kFpuSwC2 | kFpuSwC3);
if (!isunordered(x, y)) {
if (x < y) m->fpu.sw |= kFpuSwC0;
if (x == y) m->fpu.sw |= kFpuSwC3;
} else {
m->fpu.sw |= kFpuSwC0 | kFpuSwC2 | kFpuSwC3 | kFpuSwIe;
}
}
static void OpFxam(struct Machine *m) {
double x;
x = *FpuSt(m, 0);
m->fpu.sw &= ~(kFpuSwC0 | kFpuSwC1 | kFpuSwC2 | kFpuSwC3);
if (signbit(x)) m->fpu.sw |= kFpuSwC1;
if (FpuGetTag(m, 0) == kFpuTagEmpty) {
m->fpu.sw |= kFpuSwC0 | kFpuSwC3;
} else {
switch (fpclassify(x)) {
case FP_NAN:
m->fpu.sw |= kFpuSwC0;
break;
case FP_INFINITE:
m->fpu.sw |= kFpuSwC0 | kFpuSwC2;
break;
case FP_ZERO:
m->fpu.sw |= kFpuSwC3;
break;
case FP_SUBNORMAL:
m->fpu.sw |= kFpuSwC2 | kFpuSwC3;
break;
case FP_NORMAL:
m->fpu.sw |= kFpuSwC2;
break;
default:
for (;;) (void)0;
}
}
}
static void OpFtst(struct Machine *m) {
FpuCompare(m, 0);
}
static void OpFcmovb(struct Machine *m) {
if (GetFlag(m->flags, FLAGS_CF)) {
FpuSetSt0(m, StRm(m));
}
}
static void OpFcmove(struct Machine *m) {
if (GetFlag(m->flags, FLAGS_ZF)) {
FpuSetSt0(m, StRm(m));
}
}
static void OpFcmovbe(struct Machine *m) {
if (GetFlag(m->flags, FLAGS_CF) || GetFlag(m->flags, FLAGS_ZF)) {
FpuSetSt0(m, StRm(m));
}
}
static void OpFcmovu(struct Machine *m) {
if (GetFlag(m->flags, FLAGS_PF)) {
FpuSetSt0(m, StRm(m));
}
}
static void OpFcmovnb(struct Machine *m) {
if (!GetFlag(m->flags, FLAGS_CF)) {
FpuSetSt0(m, StRm(m));
}
}
static void OpFcmovne(struct Machine *m) {
if (!GetFlag(m->flags, FLAGS_ZF)) {
FpuSetSt0(m, StRm(m));
}
}
static void OpFcmovnbe(struct Machine *m) {
if (!(GetFlag(m->flags, FLAGS_CF) || GetFlag(m->flags, FLAGS_ZF))) {
FpuSetSt0(m, StRm(m));
}
}
static void OpFcmovnu(struct Machine *m) {
if (!GetFlag(m->flags, FLAGS_PF)) {
FpuSetSt0(m, StRm(m));
}
}
static void OpFchs(struct Machine *m) {
FpuSetSt0(m, -St0(m));
}
static void OpFabs(struct Machine *m) {
FpuSetSt0(m, fabs(St0(m)));
}
static void OpF2xm1(struct Machine *m) {
FpuSetSt0(m, f2xm1(St0(m)));
}
static void OpFyl2x(struct Machine *m) {
FpuSetStPop(m, 1, fyl2x(St0(m), St1(m)));
}
static void OpFyl2xp1(struct Machine *m) {
FpuSetStPop(m, 1, fyl2xp1(St0(m), St1(m)));
}
static void OpFcos(struct Machine *m) {
FpuClearOutOfRangeIndicator(m);
FpuSetSt0(m, cos(St0(m)));
}
static void OpFsin(struct Machine *m) {
FpuClearOutOfRangeIndicator(m);
FpuSetSt0(m, sin(St0(m)));
}
static void OpFptan(struct Machine *m) {
FpuClearOutOfRangeIndicator(m);
FpuSetSt0(m, tan(St0(m)));
FpuPush(m, 1);
}
static void OpFsincos(struct Machine *m) {
double tsin, tcos;
FpuClearOutOfRangeIndicator(m);
tsin = sin(St0(m));
tcos = cos(St0(m));
FpuSetSt0(m, tsin);
FpuPush(m, tcos);
}
static void OpFpatan(struct Machine *m) {
FpuClearRoundup(m);
FpuSetStPop(m, 1, atan2(St1(m), St0(m)));
}
static void OpFcom(struct Machine *m) {
FpuCompare(m, StRm(m));
}
static void OpFcomp(struct Machine *m) {
FpuCompare(m, StRm(m));
FpuPop(m);
}
static void OpFaddStEst(struct Machine *m) {
FpuSetSt0(m, FpuAdd(m, St0(m), StRm(m)));
}
static void OpFmulStEst(struct Machine *m) {
FpuSetSt0(m, FpuMul(m, St0(m), StRm(m)));
}
static void OpFsubStEst(struct Machine *m) {
FpuSetSt0(m, FpuSub(m, St0(m), StRm(m)));
}
static void OpFsubrStEst(struct Machine *m) {
FpuSetSt0(m, FpuSub(m, StRm(m), St0(m)));
}
static void OpFdivStEst(struct Machine *m) {
FpuSetSt0(m, FpuDiv(m, St0(m), StRm(m)));
}
static void OpFdivrStEst(struct Machine *m) {
FpuSetSt0(m, FpuDiv(m, StRm(m), St0(m)));
}
static void OpFaddEstSt(struct Machine *m) {
FpuSetStRm(m, FpuAdd(m, StRm(m), St0(m)));
}
static void OpFmulEstSt(struct Machine *m) {
FpuSetStRm(m, FpuMul(m, StRm(m), St0(m)));
}
static void OpFsubEstSt(struct Machine *m) {
FpuSetStRm(m, FpuSub(m, St0(m), StRm(m)));
}
static void OpFsubrEstSt(struct Machine *m) {
FpuSetStRm(m, FpuSub(m, StRm(m), St0(m)));
}
static void OpFdivEstSt(struct Machine *m) {
FpuSetStRm(m, FpuDiv(m, StRm(m), St0(m)));
}
static void OpFdivrEstSt(struct Machine *m) {
FpuSetStRm(m, FpuDiv(m, St0(m), StRm(m)));
}
static void OpFaddp(struct Machine *m) {
FpuSetStRmPop(m, FpuAdd(m, St0(m), StRm(m)));
}
static void OpFmulp(struct Machine *m) {
FpuSetStRmPop(m, FpuMul(m, St0(m), StRm(m)));
}
static void OpFcompp(struct Machine *m) {
OpFcomp(m);
FpuPop(m);
}
static void OpFsubp(struct Machine *m) {
FpuSetStRmPop(m, FpuSub(m, St0(m), StRm(m)));
}
static void OpFsubrp(struct Machine *m) {
FpuSetStPop(m, 1, FpuSub(m, StRm(m), St0(m)));
}
static void OpFdivp(struct Machine *m) {
FpuSetStRmPop(m, FpuDiv(m, St0(m), StRm(m)));
}
static void OpFdivrp(struct Machine *m) {
FpuSetStRmPop(m, FpuDiv(m, StRm(m), St0(m)));
}
static void OpFadds(struct Machine *m) {
FpuSetSt0(m, FpuAdd(m, St0(m), FpuGetMemoryFloat(m)));
}
static void OpFmuls(struct Machine *m) {
FpuSetSt0(m, FpuMul(m, St0(m), FpuGetMemoryFloat(m)));
}
static void OpFcoms(struct Machine *m) {
FpuCompare(m, FpuGetMemoryFloat(m));
}
static void OpFcomps(struct Machine *m) {
OpFcoms(m);
FpuPop(m);
}
static void OpFsubs(struct Machine *m) {
FpuSetSt0(m, FpuSub(m, St0(m), FpuGetMemoryFloat(m)));
}
static void OpFsubrs(struct Machine *m) {
FpuSetSt0(m, FpuSub(m, FpuGetMemoryFloat(m), St0(m)));
}
static void OpFdivs(struct Machine *m) {
FpuSetSt0(m, FpuDiv(m, St0(m), FpuGetMemoryFloat(m)));
}
static void OpFdivrs(struct Machine *m) {
FpuSetSt0(m, FpuDiv(m, FpuGetMemoryFloat(m), St0(m)));
}
static void OpFaddl(struct Machine *m) {
FpuSetSt0(m, FpuAdd(m, St0(m), FpuGetMemoryDouble(m)));
}
static void OpFmull(struct Machine *m) {
FpuSetSt0(m, FpuMul(m, St0(m), FpuGetMemoryDouble(m)));
}
static void OpFcoml(struct Machine *m) {
FpuCompare(m, FpuGetMemoryDouble(m));
}
static void OpFcompl(struct Machine *m) {
FpuCompare(m, FpuGetMemoryDouble(m));
FpuPop(m);
}
static void OpFsubl(struct Machine *m) {
FpuSetSt0(m, FpuSub(m, St0(m), FpuGetMemoryDouble(m)));
}
static void OpFsubrl(struct Machine *m) {
FpuSetSt0(m, FpuSub(m, FpuGetMemoryDouble(m), St0(m)));
}
static void OpFdivl(struct Machine *m) {
FpuSetSt0(m, FpuDiv(m, St0(m), FpuGetMemoryDouble(m)));
}
static void OpFdivrl(struct Machine *m) {
FpuSetSt0(m, FpuDiv(m, FpuGetMemoryDouble(m), St0(m)));
}
static void OpFiaddl(struct Machine *m) {
FpuSetSt0(m, FpuAdd(m, St0(m), FpuGetMemoryInt(m)));
}
static void OpFimull(struct Machine *m) {
FpuSetSt0(m, FpuMul(m, St0(m), FpuGetMemoryInt(m)));
}
static void OpFicoml(struct Machine *m) {
FpuCompare(m, FpuGetMemoryInt(m));
}
static void OpFicompl(struct Machine *m) {
OpFicoml(m);
FpuPop(m);
}
static void OpFisubl(struct Machine *m) {
FpuSetSt0(m, FpuSub(m, St0(m), FpuGetMemoryInt(m)));
}
static void OpFisubrl(struct Machine *m) {
FpuSetSt0(m, FpuSub(m, FpuGetMemoryInt(m), St0(m)));
}
static void OpFidivl(struct Machine *m) {
FpuSetSt0(m, FpuDiv(m, St0(m), FpuGetMemoryInt(m)));
}
static void OpFidivrl(struct Machine *m) {
FpuSetSt0(m, FpuDiv(m, FpuGetMemoryInt(m), St0(m)));
}
static void OpFiadds(struct Machine *m) {
FpuSetSt0(m, FpuAdd(m, St0(m), FpuGetMemoryShort(m)));
}
static void OpFimuls(struct Machine *m) {
FpuSetSt0(m, FpuMul(m, St0(m), FpuGetMemoryShort(m)));
}
static void OpFicoms(struct Machine *m) {
FpuCompare(m, FpuGetMemoryShort(m));
}
static void OpFicomps(struct Machine *m) {
OpFicoms(m);
FpuPop(m);
}
static void OpFisubs(struct Machine *m) {
FpuSetSt0(m, FpuSub(m, St0(m), FpuGetMemoryShort(m)));
}
static void OpFisubrs(struct Machine *m) {
FpuSetSt0(m, FpuSub(m, FpuGetMemoryShort(m), St0(m)));
}
static void OpFidivs(struct Machine *m) {
FpuSetSt0(m, FpuDiv(m, St0(m), FpuGetMemoryShort(m)));
}
static void OpFidivrs(struct Machine *m) {
FpuSetSt0(m, FpuDiv(m, FpuGetMemoryShort(m), St0(m)));
}
static void OpFsqrt(struct Machine *m) {
FpuClearRoundup(m);
FpuSetSt0(m, sqrt(St0(m)));
}
static void OpFrndint(struct Machine *m) {
FpuSetSt0(m, FpuRound(m, St0(m)));
}
static void OpFscale(struct Machine *m) {
FpuClearRoundup(m);
FpuSetSt0(m, fscale(St0(m), St1(m)));
}
static void OpFprem(struct Machine *m) {
FpuSetSt0(m, fprem(St0(m), St1(m), &m->fpu.sw));
}
static void OpFprem1(struct Machine *m) {
FpuSetSt0(m, fprem1(St0(m), St1(m), &m->fpu.sw));
}
static void OpFdecstp(struct Machine *m) {
m->fpu.sw = (m->fpu.sw & ~kFpuSwSp) | ((m->fpu.sw - (1 << 11)) & kFpuSwSp);
}
static void OpFincstp(struct Machine *m) {
m->fpu.sw = (m->fpu.sw & ~kFpuSwSp) | ((m->fpu.sw + (1 << 11)) & kFpuSwSp);
}
static void OpFxtract(struct Machine *m) {
double x = St0(m);
FpuSetSt0(m, logb(x));
FpuPush(m, ldexp(x, -ilogb(x)));
}
static void OpFld(struct Machine *m) {
FpuPush(m, StRm(m));
}
static void OpFlds(struct Machine *m) {
FpuPush(m, FpuGetMemoryFloat(m));
}
static void OpFsts(struct Machine *m) {
FpuSetMemoryFloat(m, St0(m));
}
static void OpFstps(struct Machine *m) {
OpFsts(m);
FpuPop(m);
}
static void OpFstpt(struct Machine *m) {
FpuSetMemoryLdbl(m, FpuPop(m));
}
static void OpFstl(struct Machine *m) {
FpuSetMemoryDouble(m, St0(m));
}
static void OpFstpl(struct Machine *m) {
OpFstl(m);
FpuPop(m);
}
static void OpFst(struct Machine *m) {
FpuSetStRm(m, St0(m));
}
static void OpFstp(struct Machine *m) {
FpuSetStRmPop(m, St0(m));
}
static void OpFxch(struct Machine *m) {
double t = StRm(m);
FpuSetStRm(m, St0(m));
FpuSetSt0(m, t);
}
static void OpFldcw(struct Machine *m) {
m->fpu.cw = FpuGetMemoryShort(m);
}
static void OpFldt(struct Machine *m) {
FpuPush(m, FpuGetMemoryLdbl(m));
}
static void OpFldl(struct Machine *m) {
FpuPush(m, FpuGetMemoryDouble(m));
}
static double Fld1(void) {
return 1;
}
static double Fldl2t(void) {
return 0xd.49a784bcd1b8afep-2L; /* log₂10 */
}
static double Fldl2e(void) {
return 0xb.8aa3b295c17f0bcp-3L; /* log₂𝑒 */
}
static double Fldpi(void) {
return 0x1.921fb54442d1846ap+1L; /* π */
}
static double Fldlg2(void) {
return 0x9.a209a84fbcff799p-5L; /* log₁₀2 */
}
static double Fldln2(void) {
return 0xb.17217f7d1cf79acp-4L; /* logₑ2 */
}
static double Fldz(void) {
return 0;
}
static void OpFldConstant(struct Machine *m) {
double x;
switch (ModrmRm(m->xedd->op.rde)) {
CASE(0, x = Fld1());
CASE(1, x = Fldl2t());
CASE(2, x = Fldl2e());
CASE(3, x = Fldpi());
CASE(4, x = Fldlg2());
CASE(5, x = Fldln2());
CASE(6, x = Fldz());
default:
OpUd(m, m->xedd->op.rde);
}
FpuPush(m, x);
}
static void OpFstcw(struct Machine *m) {
FpuSetMemoryShort(m, m->fpu.cw);
}
static void OpFilds(struct Machine *m) {
FpuPush(m, FpuGetMemoryShort(m));
}
static void OpFildl(struct Machine *m) {
FpuPush(m, FpuGetMemoryInt(m));
}
static void OpFildll(struct Machine *m) {
FpuPush(m, FpuGetMemoryLong(m));
}
static void OpFisttpl(struct Machine *m) {
FpuSetMemoryInt(m, FpuPop(m));
}
static void OpFisttpll(struct Machine *m) {
FpuSetMemoryLong(m, FpuPop(m));
}
static void OpFisttps(struct Machine *m) {
FpuSetMemoryShort(m, FpuPop(m));
}
static void OpFists(struct Machine *m) {
FpuSetMemoryShort(m, FpuRound(m, St0(m)));
}
static void OpFistl(struct Machine *m) {
FpuSetMemoryInt(m, FpuRound(m, St0(m)));
}
static void OpFistll(struct Machine *m) {
FpuSetMemoryLong(m, FpuRound(m, St0(m)));
}
static void OpFistpl(struct Machine *m) {
OpFistl(m);
FpuPop(m);
}
static void OpFistpll(struct Machine *m) {
OpFistll(m);
FpuPop(m);
}
static void OpFistps(struct Machine *m) {
OpFists(m);
FpuPop(m);
}
static void OpFcomi(struct Machine *m) {
double x, y;
x = St0(m);
y = StRm(m);
if (!isunordered(x, y)) {
m->flags = SetFlag(m->flags, FLAGS_ZF, x == y);
m->flags = SetFlag(m->flags, FLAGS_CF, x < y);
m->flags = SetFlag(m->flags, FLAGS_PF, false);
} else {
m->fpu.sw |= kFpuSwIe;
m->flags = SetFlag(m->flags, FLAGS_ZF, true);
m->flags = SetFlag(m->flags, FLAGS_CF, true);
m->flags = SetFlag(m->flags, FLAGS_PF, true);
}
}
static void OpFucom(struct Machine *m) {
FpuCompare(m, StRm(m));
}
static void OpFucomp(struct Machine *m) {
FpuCompare(m, StRm(m));
FpuPop(m);
}
static void OpFcomip(struct Machine *m) {
OpFcomi(m);
FpuPop(m);
}
static void OpFucomi(struct Machine *m) {
OpFcomi(m);
}
static void OpFucomip(struct Machine *m) {
OpFcomip(m);
}
static void OpFfree(struct Machine *m) {
FpuSetTag(m, ModrmRm(m->xedd->op.rde), kFpuTagEmpty);
}
static void OpFfreep(struct Machine *m) {
if (ModrmRm(m->xedd->op.rde)) OpFfree(m);
FpuPop(m);
}
static void OpFstswMw(struct Machine *m) {
FpuSetMemoryShort(m, m->fpu.sw);
}
static void OpFstswAx(struct Machine *m) {
Write16(m->ax, m->fpu.sw);
}
static void SetFpuEnv(struct Machine *m, uint8_t p[28]) {
Write16(p + 0, m->fpu.cw);
Write16(p + 4, m->fpu.sw);
Write16(p + 8, m->fpu.tw);
Write64(p + 12, m->fpu.ip);
Write16(p + 18, m->fpu.op);
Write64(p + 20, m->fpu.dp);
}
static void GetFpuEnv(struct Machine *m, uint8_t p[28]) {
m->fpu.cw = Read16(p + 0);
m->fpu.sw = Read16(p + 4);
m->fpu.tw = Read16(p + 8);
}
static void OpFstenv(struct Machine *m) {
void *p[2];
uint8_t b[28];
SetFpuEnv(m, BeginStore(m, m->fpu.dp, sizeof(b), p, b));
EndStore(m, m->fpu.dp, sizeof(b), p, b);
}
static void OpFldenv(struct Machine *m) {
uint8_t b[28];
GetFpuEnv(m, Load(m, m->fpu.dp, sizeof(b), b));
}
static void OpFsave(struct Machine *m) {
int i;
void *p[2];
uint8_t *a, b[108], t[16];
a = BeginStore(m, m->fpu.dp, sizeof(b), p, b);
SetFpuEnv(m, a);
memset(t, 0, sizeof(t));
for (i = 0; i < 8; ++i) {
SerializeLdbl(a + 28 + i * 10, *FpuSt(m, i));
}
EndStore(m, m->fpu.dp, sizeof(b), p, b);
OpFinit(m);
}
static void OpFrstor(struct Machine *m) {
int i;
uint8_t *a, b[108];
a = Load(m, m->fpu.dp, sizeof(b), b);
GetFpuEnv(m, a);
for (i = 0; i < 8; ++i) {
*FpuSt(m, i) = DeserializeLdbl(a + 28 + i * 10);
}
}
static void OpFnclex(struct Machine *m) {
m->fpu.sw &= ~(kFpuSwIe | kFpuSwDe | kFpuSwZe | kFpuSwOe | kFpuSwUe |
kFpuSwPe | kFpuSwEs | kFpuSwSf | kFpuSwBf);
}
static void OpFnop(struct Machine *m) {
/* do nothing */
}
void OpFinit(struct Machine *m) {
m->fpu.cw = 0x037f;
m->fpu.sw = 0;
m->fpu.tw = -1;
}
void OpFwait(struct Machine *m, uint32_t rde) {
int sw, cw;
sw = m->fpu.sw;
cw = m->fpu.cw;
if (((sw & kFpuSwIe) && !(cw & kFpuCwIm)) ||
((sw & kFpuSwDe) && !(cw & kFpuCwDm)) ||
((sw & kFpuSwZe) && !(cw & kFpuCwZm)) ||
((sw & kFpuSwOe) && !(cw & kFpuCwOm)) ||
((sw & kFpuSwUe) && !(cw & kFpuCwUm)) ||
((sw & kFpuSwPe) && !(cw & kFpuCwPm)) ||
((sw & kFpuSwSf) && !(cw & kFpuCwIm))) {
HaltMachine(m, kMachineFpuException);
}
}
int FpuGetTag(struct Machine *m, unsigned i) {
unsigned t;
t = m->fpu.tw;
i += (m->fpu.sw & kFpuSwSp) >> 11;
i &= 7;
i *= 2;
t &= 3 << i;
t >>= i;
return t;
}
void FpuSetTag(struct Machine *m, unsigned i, unsigned t) {
i += (m->fpu.sw & kFpuSwSp) >> 11;
t &= 3;
i &= 7;
i *= 2;
m->fpu.tw &= ~(3 << i);
m->fpu.tw |= t << i;
}
void FpuPush(struct Machine *m, double x) {
if (FpuGetTag(m, -1) != kFpuTagEmpty) OnFpuStackOverflow(m);
m->fpu.sw = (m->fpu.sw & ~kFpuSwSp) | ((m->fpu.sw - (1 << 11)) & kFpuSwSp);
*FpuSt(m, 0) = x;
FpuSetTag(m, 0, kFpuTagValid);
}
double FpuPop(struct Machine *m) {
double x;
if (FpuGetTag(m, 0) != kFpuTagEmpty) {
x = *FpuSt(m, 0);
FpuSetTag(m, 0, kFpuTagEmpty);
} else {
x = OnFpuStackUnderflow(m);
}
m->fpu.sw = (m->fpu.sw & ~kFpuSwSp) | ((m->fpu.sw + (1 << 11)) & kFpuSwSp);
return x;
}
void OpFpu(struct Machine *m, uint32_t rde) {
unsigned op;
bool ismemory;
op = m->xedd->op.opcode & 7;
ismemory = ModrmMod(rde) != 3;
m->fpu.ip = m->ip - m->xedd->length;
m->fpu.op = op << 8 | ModrmMod(rde) << 6 | ModrmReg(rde) << 3 | ModrmRm(rde);
m->fpu.dp = ismemory ? ComputeAddress(m, rde) : 0;
switch (DISP(op, ismemory, ModrmReg(rde))) {
CASE(DISP(0xD8, FPUREG, 0), OpFaddStEst(m));
CASE(DISP(0xD8, FPUREG, 1), OpFmulStEst(m));
CASE(DISP(0xD8, FPUREG, 2), OpFcom(m));
CASE(DISP(0xD8, FPUREG, 3), OpFcomp(m));
CASE(DISP(0xD8, FPUREG, 4), OpFsubStEst(m));
CASE(DISP(0xD8, FPUREG, 5), OpFsubrStEst(m));
CASE(DISP(0xD8, FPUREG, 6), OpFdivStEst(m));
CASE(DISP(0xD8, FPUREG, 7), OpFdivrStEst(m));
CASE(DISP(0xD8, MEMORY, 0), OpFadds(m));
CASE(DISP(0xD8, MEMORY, 1), OpFmuls(m));
CASE(DISP(0xD8, MEMORY, 2), OpFcoms(m));
CASE(DISP(0xD8, MEMORY, 3), OpFcomps(m));
CASE(DISP(0xD8, MEMORY, 4), OpFsubs(m));
CASE(DISP(0xD8, MEMORY, 5), OpFsubrs(m));
CASE(DISP(0xD8, MEMORY, 6), OpFdivs(m));
CASE(DISP(0xD8, MEMORY, 7), OpFdivrs(m));
CASE(DISP(0xD9, FPUREG, 0), OpFld(m));
CASE(DISP(0xD9, FPUREG, 1), OpFxch(m));
CASE(DISP(0xD9, FPUREG, 2), OpFnop(m));
CASE(DISP(0xD9, FPUREG, 3), OpFstp(m));
CASE(DISP(0xD9, FPUREG, 5), OpFldConstant(m));
CASE(DISP(0xD9, MEMORY, 0), OpFlds(m));
CASE(DISP(0xD9, MEMORY, 2), OpFsts(m));
CASE(DISP(0xD9, MEMORY, 3), OpFstps(m));
CASE(DISP(0xD9, MEMORY, 4), OpFldenv(m));
CASE(DISP(0xD9, MEMORY, 5), OpFldcw(m));
CASE(DISP(0xD9, MEMORY, 6), OpFstenv(m));
CASE(DISP(0xD9, MEMORY, 7), OpFstcw(m));
CASE(DISP(0xDA, FPUREG, 0), OpFcmovb(m));
CASE(DISP(0xDA, FPUREG, 1), OpFcmove(m));
CASE(DISP(0xDA, FPUREG, 2), OpFcmovbe(m));
CASE(DISP(0xDA, FPUREG, 3), OpFcmovu(m));
CASE(DISP(0xDA, MEMORY, 0), OpFiaddl(m));
CASE(DISP(0xDA, MEMORY, 1), OpFimull(m));
CASE(DISP(0xDA, MEMORY, 2), OpFicoml(m));
CASE(DISP(0xDA, MEMORY, 3), OpFicompl(m));
CASE(DISP(0xDA, MEMORY, 4), OpFisubl(m));
CASE(DISP(0xDA, MEMORY, 5), OpFisubrl(m));
CASE(DISP(0xDA, MEMORY, 6), OpFidivl(m));
CASE(DISP(0xDA, MEMORY, 7), OpFidivrl(m));
CASE(DISP(0xDB, FPUREG, 0), OpFcmovnb(m));
CASE(DISP(0xDB, FPUREG, 1), OpFcmovne(m));
CASE(DISP(0xDB, FPUREG, 2), OpFcmovnbe(m));
CASE(DISP(0xDB, FPUREG, 3), OpFcmovnu(m));
CASE(DISP(0xDB, FPUREG, 5), OpFucomi(m));
CASE(DISP(0xDB, FPUREG, 6), OpFcomi(m));
CASE(DISP(0xDB, MEMORY, 0), OpFildl(m));
CASE(DISP(0xDB, MEMORY, 1), OpFisttpl(m));
CASE(DISP(0xDB, MEMORY, 2), OpFistl(m));
CASE(DISP(0xDB, MEMORY, 3), OpFistpl(m));
CASE(DISP(0xDB, MEMORY, 5), OpFldt(m));
CASE(DISP(0xDB, MEMORY, 7), OpFstpt(m));
CASE(DISP(0xDC, FPUREG, 0), OpFaddEstSt(m));
CASE(DISP(0xDC, FPUREG, 1), OpFmulEstSt(m));
CASE(DISP(0xDC, FPUREG, 2), OpFcom(m));
CASE(DISP(0xDC, FPUREG, 3), OpFcomp(m));
CASE(DISP(0xDC, FPUREG, 4), OpFsubEstSt(m));
CASE(DISP(0xDC, FPUREG, 5), OpFsubrEstSt(m));
CASE(DISP(0xDC, FPUREG, 6), OpFdivEstSt(m));
CASE(DISP(0xDC, FPUREG, 7), OpFdivrEstSt(m));
CASE(DISP(0xDC, MEMORY, 0), OpFaddl(m));
CASE(DISP(0xDC, MEMORY, 1), OpFmull(m));
CASE(DISP(0xDC, MEMORY, 2), OpFcoml(m));
CASE(DISP(0xDC, MEMORY, 3), OpFcompl(m));
CASE(DISP(0xDC, MEMORY, 4), OpFsubl(m));
CASE(DISP(0xDC, MEMORY, 5), OpFsubrl(m));
CASE(DISP(0xDC, MEMORY, 6), OpFdivl(m));
CASE(DISP(0xDC, MEMORY, 7), OpFdivrl(m));
CASE(DISP(0xDD, FPUREG, 0), OpFfree(m));
CASE(DISP(0xDD, FPUREG, 1), OpFxch(m));
CASE(DISP(0xDD, FPUREG, 2), OpFst(m));
CASE(DISP(0xDD, FPUREG, 3), OpFstp(m));
CASE(DISP(0xDD, FPUREG, 4), OpFucom(m));
CASE(DISP(0xDD, FPUREG, 5), OpFucomp(m));
CASE(DISP(0xDD, MEMORY, 0), OpFldl(m));
CASE(DISP(0xDD, MEMORY, 1), OpFisttpll(m));
CASE(DISP(0xDD, MEMORY, 2), OpFstl(m));
CASE(DISP(0xDD, MEMORY, 3), OpFstpl(m));
CASE(DISP(0xDD, MEMORY, 4), OpFrstor(m));
CASE(DISP(0xDD, MEMORY, 6), OpFsave(m));
CASE(DISP(0xDD, MEMORY, 7), OpFstswMw(m));
CASE(DISP(0xDE, FPUREG, 0), OpFaddp(m));
CASE(DISP(0xDE, FPUREG, 1), OpFmulp(m));
CASE(DISP(0xDE, FPUREG, 2), OpFcomp(m));
CASE(DISP(0xDE, FPUREG, 3), OpFcompp(m));
CASE(DISP(0xDE, FPUREG, 4), OpFsubp(m));
CASE(DISP(0xDE, FPUREG, 5), OpFsubrp(m));
CASE(DISP(0xDE, FPUREG, 6), OpFdivp(m));
CASE(DISP(0xDE, FPUREG, 7), OpFdivrp(m));
CASE(DISP(0xDE, MEMORY, 0), OpFiadds(m));
CASE(DISP(0xDE, MEMORY, 1), OpFimuls(m));
CASE(DISP(0xDE, MEMORY, 2), OpFicoms(m));
CASE(DISP(0xDE, MEMORY, 3), OpFicomps(m));
CASE(DISP(0xDE, MEMORY, 4), OpFisubs(m));
CASE(DISP(0xDE, MEMORY, 5), OpFisubrs(m));
CASE(DISP(0xDE, MEMORY, 6), OpFidivs(m));
CASE(DISP(0xDE, MEMORY, 7), OpFidivrs(m));
CASE(DISP(0xDF, FPUREG, 0), OpFfreep(m));
CASE(DISP(0xDF, FPUREG, 1), OpFxch(m));
CASE(DISP(0xDF, FPUREG, 2), OpFstp(m));
CASE(DISP(0xDF, FPUREG, 3), OpFstp(m));
CASE(DISP(0xDF, FPUREG, 4), OpFstswAx(m));
CASE(DISP(0xDF, FPUREG, 5), OpFucomip(m));
CASE(DISP(0xDF, FPUREG, 6), OpFcomip(m));
CASE(DISP(0xDF, MEMORY, 0), OpFilds(m));
CASE(DISP(0xDF, MEMORY, 1), OpFisttps(m));
CASE(DISP(0xDF, MEMORY, 2), OpFists(m));
CASE(DISP(0xDF, MEMORY, 3), OpFistps(m));
CASE(DISP(0xDF, MEMORY, 5), OpFildll(m));
CASE(DISP(0xDF, MEMORY, 7), OpFistpll(m));
case DISP(0xD9, FPUREG, 4):
switch (ModrmRm(rde)) {
CASE(0, OpFchs(m));
CASE(1, OpFabs(m));
CASE(4, OpFtst(m));
CASE(5, OpFxam(m));
default:
OpUd(m, rde);
}
break;
case DISP(0xD9, FPUREG, 6):
switch (ModrmRm(rde)) {
CASE(0, OpF2xm1(m));
CASE(1, OpFyl2x(m));
CASE(2, OpFptan(m));
CASE(3, OpFpatan(m));
CASE(4, OpFxtract(m));
CASE(5, OpFprem1(m));
CASE(6, OpFdecstp(m));
CASE(7, OpFincstp(m));
default:
for (;;) (void)0;
}
break;
case DISP(0xD9, FPUREG, 7):
switch (ModrmRm(rde)) {
CASE(0, OpFprem(m));
CASE(1, OpFyl2xp1(m));
CASE(2, OpFsqrt(m));
CASE(3, OpFsincos(m));
CASE(4, OpFrndint(m));
CASE(5, OpFscale(m));
CASE(6, OpFsin(m));
CASE(7, OpFcos(m));
default:
for (;;) (void)0;
}
break;
case DISP(0xDb, FPUREG, 4):
switch (ModrmRm(rde)) {
CASE(2, OpFnclex(m));
CASE(3, OpFinit(m));
default:
OpUd(m, rde);
}
break;
default:
OpUd(m, rde);
}
}