Serenity Operating System
1/*
2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include "SoftFPU.h"
9#include "Emulator.h"
10#include "SoftCPU.h"
11#include "ValueWithShadow.h"
12
13#include <AK/BitCast.h>
14#include <AK/NumericLimits.h>
15#include <AK/UFixedBigInt.h>
16
17#include <unistd.h>
18
19#if defined(AK_COMPILER_GCC)
20# pragma GCC optimize("O3")
21#endif
22
23#define TODO_INSN() \
24 do { \
25 reportln("\n=={}== Unimplemented instruction: {}\n"sv, getpid(), __FUNCTION__); \
26 m_emulator.dump_backtrace(); \
27 _exit(0); \
28 } while (0)
29
30template<typename T>
31ALWAYS_INLINE void warn_if_uninitialized(T value_with_shadow, char const* message)
32{
33 if (value_with_shadow.is_uninitialized()) [[unlikely]] {
34 reportln("\033[31;1mWarning! Use of uninitialized value: {}\033[0m\n"sv, message);
35 UserspaceEmulator::Emulator::the().dump_backtrace();
36 }
37}
38
39namespace UserspaceEmulator { // NOLINT(readability-implicit-bool-conversion) 0/1 to follow spec closer
40
41ALWAYS_INLINE void SoftFPU::warn_if_mmx_absolute(u8 index) const
42{
43 if (m_reg_is_mmx[index]) [[unlikely]] {
44 reportln("\033[31;1mWarning! Use of an MMX register as an FPU value ({} abs)\033[0m\n"sv, index);
45 m_emulator.dump_backtrace();
46 }
47}
48ALWAYS_INLINE void SoftFPU::warn_if_fpu_absolute(u8 index) const
49{
50 if (!m_reg_is_mmx[index]) [[unlikely]] {
51 reportln("\033[31;1mWarning! Use of an FPU value ({} abs) as an MMX register\033[0m\n"sv, index);
52 m_emulator.dump_backtrace();
53 }
54}
55
56ALWAYS_INLINE long double SoftFPU::fpu_get(u8 index)
57{
58 VERIFY(index < 8);
59 if (!fpu_is_set(index))
60 fpu_set_stack_underflow();
61 warn_if_mmx_absolute(index);
62
63 u8 effective_index = (m_fpu_stack_top + index) % 8;
64
65 return m_storage[effective_index].fp;
66}
67ALWAYS_INLINE void SoftFPU::fpu_set_absolute(u8 index, long double value)
68{
69 VERIFY(index < 8);
70 set_tag_from_value_absolute(index, value);
71 m_storage[index].fp = value;
72 m_reg_is_mmx[index] = false;
73}
74ALWAYS_INLINE void SoftFPU::fpu_set(u8 index, long double value)
75{
76 VERIFY(index < 8);
77 fpu_set_absolute((m_fpu_stack_top + index) % 8, value);
78}
79MMX SoftFPU::mmx_get(u8 index) const
80{
81 VERIFY(index < 8);
82 warn_if_fpu_absolute(index);
83 return m_storage[index].mmx;
84}
85void SoftFPU::mmx_set(u8 index, MMX value)
86{
87 m_storage[index].mmx = value;
88 // The high bytes are set to 0b11... to make the floating-point value NaN.
89 // This way we are technically able to find out if we are reading the wrong
90 // type, but this is still difficult, so we use our own lookup for that
91 m_storage[index].__high = 0xFFFFU;
92 m_reg_is_mmx[index] = true;
93}
94
95ALWAYS_INLINE void SoftFPU::fpu_push(long double value)
96{
97 if (fpu_is_set(7))
98 fpu_set_stack_overflow();
99 m_fpu_stack_top = (m_fpu_stack_top - 1u) % 8;
100
101 fpu_set(0, value);
102}
103
104ALWAYS_INLINE long double SoftFPU::fpu_pop()
105{
106 warn_if_mmx_absolute(m_fpu_stack_top);
107
108 if (!fpu_is_set(0))
109 fpu_set_stack_underflow();
110
111 auto ret = fpu_get(0);
112 fpu_set_tag(0, FPU_Tag::Empty);
113 m_fpu_stack_top = (m_fpu_stack_top + 1u) % 8;
114 return ret;
115}
116
117ALWAYS_INLINE void SoftFPU::fpu_set_exception(FPU_Exception ex)
118{
119 switch (ex) {
120 case FPU_Exception::StackFault:
121 m_fpu_error_stackfault = 1;
122 m_fpu_error_invalid = 1; // Implies InvalidOperation
123 break;
124 case FPU_Exception::InvalidOperation:
125 m_fpu_error_invalid = 1;
126 if (!m_fpu_cw.mask_invalid)
127 break;
128 return;
129 case FPU_Exception::DenormalizedOperand:
130 m_fpu_error_denorm = 1;
131 if (!m_fpu_cw.mask_denorm)
132 break;
133 return;
134 case FPU_Exception::ZeroDivide:
135 m_fpu_error_zero_div = 1;
136 if (!m_fpu_cw.mask_zero_div)
137 break;
138 return;
139 case FPU_Exception::Overflow:
140 m_fpu_error_overflow = 1;
141 if (!m_fpu_cw.mask_overflow)
142 break;
143 return;
144 case FPU_Exception::Underflow:
145 m_fpu_error_underflow = 1;
146 if (!m_fpu_cw.mask_underflow)
147 break;
148 return;
149 case FPU_Exception::Precision:
150 m_fpu_error_precision = 1;
151 if (!m_fpu_cw.mask_precision)
152 break;
153 return;
154 }
155
156 // set exception bit
157 m_fpu_error_summary = 1;
158
159 // FIXME: set traceback
160 // For that we need to get the currently executing instruction and
161 // the previous eip
162
163 // FIXME: Call FPU Exception handler
164 reportln("Trying to call Exception handler from {}"sv, fpu_exception_string(ex));
165 fpu_dump_env();
166 m_emulator.dump_backtrace();
167 TODO();
168}
169
170template<Arithmetic T>
171ALWAYS_INLINE T SoftFPU::round_checked(long double value)
172{
173 T result = static_cast<T>(rintl(value));
174 if (result != value)
175 fpu_set_exception(FPU_Exception::Precision);
176 if (result > value)
177 set_c1(1);
178 else
179 set_c1(0);
180 return result;
181}
182
183template<FloatingPoint T>
184ALWAYS_INLINE T SoftFPU::convert_checked(long double value)
185{
186 T result = static_cast<T>(value);
187 if (auto rnd = value - result) {
188 if (rnd > 0)
189 set_c1(1);
190 else
191 set_c1(0);
192 fpu_set_exception(FPU_Exception::Precision);
193 }
194 return result;
195}
196
197// Instructions
198
199// DATA TRANSFER
200void SoftFPU::FLD_RM32(const X86::Instruction& insn)
201{
202 if (insn.modrm().is_register()) {
203 fpu_push(fpu_get(insn.modrm().register_index()));
204 } else {
205 auto new_f32 = insn.modrm().read32(m_cpu, insn);
206 // FIXME: Respect shadow values
207 fpu_push(bit_cast<float>(new_f32.value()));
208 }
209}
210void SoftFPU::FLD_RM64(const X86::Instruction& insn)
211{
212 VERIFY(!insn.modrm().is_register());
213 auto new_f64 = insn.modrm().read64(m_cpu, insn);
214 // FIXME: Respect shadow values
215 fpu_push(bit_cast<double>(new_f64.value()));
216}
217void SoftFPU::FLD_RM80(const X86::Instruction& insn)
218{
219 VERIFY(!insn.modrm().is_register());
220
221 // long doubles can be up to 128 bits wide in memory for reasons (alignment) and only uses 80 bits of precision
222 // GCC uses 12 bytes in 32 bit and 16 bytes in 64 bit mode
223 // so in the 32 bit case we read a bit to much, but that shouldn't be an issue.
224 // FIXME: Respect shadow values
225 u128 new_f80 = insn.modrm().read128(m_cpu, insn).value();
226
227 fpu_push(*(long double*)new_f80.bytes().data());
228}
229
230void SoftFPU::FST_RM32(const X86::Instruction& insn)
231{
232 VERIFY(!insn.modrm().is_register());
233 float f32 = convert_checked<float>(fpu_get(0));
234
235 if (fpu_is_set(0))
236 insn.modrm().write32(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u32>(f32)));
237 else
238 insn.modrm().write32(m_cpu, insn, ValueWithShadow<u32>(bit_cast<u32>(f32), 0u));
239}
240void SoftFPU::FST_RM64(const X86::Instruction& insn)
241{
242 if (insn.modrm().is_register()) {
243 fpu_set(insn.modrm().register_index(), fpu_get(0));
244 } else {
245 double f64 = convert_checked<double>(fpu_get(0));
246 if (fpu_is_set(0))
247 insn.modrm().write64(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u64>(f64)));
248 else
249 insn.modrm().write64(m_cpu, insn, ValueWithShadow<u64>(bit_cast<u64>(f64), 0ULL));
250 }
251}
252
253void SoftFPU::FSTP_RM32(const X86::Instruction& insn)
254{
255 FST_RM32(insn);
256 fpu_pop();
257}
258void SoftFPU::FSTP_RM64(const X86::Instruction& insn)
259{
260 FST_RM64(insn);
261 fpu_pop();
262}
263void SoftFPU::FSTP_RM80(const X86::Instruction& insn)
264{
265 if (insn.modrm().is_register()) {
266 fpu_set(insn.modrm().register_index(), fpu_get(0));
267 fpu_pop();
268 } else {
269 // FIXME: Respect more shadow values
270 // long doubles can be up to 128 bits wide in memory for reasons (alignment) and only uses 80 bits of precision
271 // gcc uses 12 byte in 32 bit and 16 byte in 64 bit mode
272 // due to only 10 bytes being used, we just write these 10 into memory
273 // We have to do .bytes().data() to get around static type analysis
274 ValueWithShadow<u128> f80 { 0u, 0u };
275 u128 value {};
276 f80 = insn.modrm().read128(m_cpu, insn);
277 *(long double*)value.bytes().data() = fpu_pop();
278 memcpy(f80.value().bytes().data(), &value, 10); // copy
279 f80.set_initialized();
280 insn.modrm().write128(m_cpu, insn, f80);
281 }
282}
283
284void SoftFPU::FILD_RM16(const X86::Instruction& insn)
285{
286 VERIFY(!insn.modrm().is_register());
287 auto m16int = insn.modrm().read16(m_cpu, insn);
288 warn_if_uninitialized(m16int, "int16 loaded as float");
289
290 fpu_push(static_cast<long double>(static_cast<i16>(m16int.value())));
291}
292void SoftFPU::FILD_RM32(const X86::Instruction& insn)
293{
294 VERIFY(!insn.modrm().is_register());
295 auto m32int = insn.modrm().read32(m_cpu, insn);
296 warn_if_uninitialized(m32int, "int32 loaded as float");
297
298 fpu_push(static_cast<long double>(static_cast<i32>(m32int.value())));
299}
300void SoftFPU::FILD_RM64(const X86::Instruction& insn)
301{
302 VERIFY(!insn.modrm().is_register());
303 auto m64int = insn.modrm().read64(m_cpu, insn);
304 warn_if_uninitialized(m64int, "int64 loaded as float");
305
306 fpu_push(static_cast<long double>(static_cast<i64>(m64int.value())));
307}
308
309void SoftFPU::FIST_RM16(const X86::Instruction& insn)
310{
311 VERIFY(!insn.modrm().is_register());
312 auto f = fpu_get(0);
313 set_c1(0);
314 auto int16 = round_checked<i16>(f);
315
316 // FIXME: Respect shadow values
317 insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u16>(int16)));
318}
319void SoftFPU::FIST_RM32(const X86::Instruction& insn)
320{
321 VERIFY(!insn.modrm().is_register());
322 auto f = fpu_get(0);
323 set_c1(0);
324 auto int32 = round_checked<i32>(f);
325 // FIXME: Respect shadow values
326 insn.modrm().write32(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u32>(int32)));
327}
328
329void SoftFPU::FISTP_RM16(const X86::Instruction& insn)
330{
331 FIST_RM16(insn);
332 fpu_pop();
333}
334void SoftFPU::FISTP_RM32(const X86::Instruction& insn)
335{
336 FIST_RM32(insn);
337 fpu_pop();
338}
339void SoftFPU::FISTP_RM64(const X86::Instruction& insn)
340{
341 VERIFY(!insn.modrm().is_register());
342 auto f = fpu_pop();
343 set_c1(0);
344 auto i64 = round_checked<int64_t>(f);
345 // FIXME: Respect shadow values
346 insn.modrm().write64(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u64>(i64)));
347}
348
349void SoftFPU::FISTTP_RM16(const X86::Instruction& insn)
350{
351 VERIFY(!insn.modrm().is_register());
352 set_c1(0);
353 i16 value = static_cast<i16>(fpu_pop());
354 // FIXME: Respect shadow values
355 insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u16>(value)));
356}
357void SoftFPU::FISTTP_RM32(const X86::Instruction& insn)
358{
359 VERIFY(!insn.modrm().is_register());
360 i32 value = static_cast<i32>(fpu_pop());
361 set_c1(0);
362 // FIXME: Respect shadow values
363 insn.modrm().write32(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u32>(value)));
364}
365void SoftFPU::FISTTP_RM64(const X86::Instruction& insn)
366{
367 VERIFY(!insn.modrm().is_register());
368 set_c1(0);
369 i64 value = static_cast<i64>(fpu_pop());
370 // FIXME: Respect shadow values
371 insn.modrm().write64(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u64>(value)));
372}
373
374void SoftFPU::FBLD_M80(const X86::Instruction&) { TODO_INSN(); }
375void SoftFPU::FBSTP_M80(const X86::Instruction&) { TODO_INSN(); }
376
377void SoftFPU::FXCH(const X86::Instruction& insn)
378{
379 VERIFY(insn.modrm().is_register());
380 set_c1(0);
381 auto tmp = fpu_get(0);
382 fpu_set(0, fpu_get(insn.modrm().register_index()));
383 fpu_set(insn.modrm().register_index(), tmp);
384}
385
386void SoftFPU::FCMOVE(const X86::Instruction& insn)
387{
388 VERIFY(insn.modrm().is_register());
389 if (m_cpu.zf())
390 fpu_set(0, fpu_get(insn.modrm().rm()));
391}
392void SoftFPU::FCMOVNE(const X86::Instruction& insn)
393{
394 VERIFY(insn.modrm().is_register());
395 if (!m_cpu.zf())
396 fpu_set(0, fpu_get((insn.modrm().reg_fpu())));
397}
398
399void SoftFPU::FCMOVB(const X86::Instruction& insn)
400{
401 VERIFY(insn.modrm().is_register());
402 if (m_cpu.cf())
403 fpu_set(0, fpu_get(insn.modrm().rm()));
404}
405void SoftFPU::FCMOVNB(const X86::Instruction& insn)
406{
407 VERIFY(insn.modrm().is_register());
408 if (!m_cpu.cf())
409 fpu_set(0, fpu_get(insn.modrm().rm()));
410}
411void SoftFPU::FCMOVBE(const X86::Instruction& insn)
412{
413 VERIFY(insn.modrm().is_register());
414 if (m_cpu.cf() || m_cpu.zf())
415 fpu_set(0, fpu_get(insn.modrm().rm()));
416}
417void SoftFPU::FCMOVNBE(const X86::Instruction& insn)
418{
419 VERIFY(insn.modrm().is_register());
420 if (!(m_cpu.cf() || m_cpu.zf()))
421 fpu_set(0, fpu_get(insn.modrm().rm()));
422}
423
424void SoftFPU::FCMOVU(const X86::Instruction& insn)
425{
426 VERIFY(insn.modrm().is_register());
427 if (m_cpu.pf())
428 fpu_set(0, fpu_get((insn.modrm().reg_fpu())));
429}
430void SoftFPU::FCMOVNU(const X86::Instruction& insn)
431{
432 VERIFY(insn.modrm().is_register());
433 if (!m_cpu.pf())
434 fpu_set(0, fpu_get((insn.modrm().reg_fpu())));
435}
436
437// BASIC ARITHMETIC
438void SoftFPU::FADD_RM32(const X86::Instruction& insn)
439{
440 // FIXME look at ::INC_foo for how mem/reg stuff is handled, and use that here too to make sure this is only called for mem32 ops
441 if (insn.modrm().is_register()) {
442 fpu_set(0, fpu_get(insn.modrm().register_index()) + fpu_get(0));
443 } else {
444 auto new_f32 = insn.modrm().read32(m_cpu, insn);
445 // FIXME: Respect shadow values
446 auto f32 = bit_cast<float>(new_f32.value());
447 fpu_set(0, fpu_get(0) + f32);
448 }
449}
450void SoftFPU::FADD_RM64(const X86::Instruction& insn)
451{
452 // FIXME look at ::INC_foo for how mem/reg stuff is handled, and use that here too to make sure this is only called for mem64 ops
453 if (insn.modrm().is_register()) {
454 fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) + fpu_get(0));
455 } else {
456 auto new_f64 = insn.modrm().read64(m_cpu, insn);
457 // FIXME: Respect shadow values
458 auto f64 = bit_cast<double>(new_f64.value());
459 fpu_set(0, fpu_get(0) + f64);
460 }
461}
462void SoftFPU::FADDP(const X86::Instruction& insn)
463{
464 VERIFY(insn.modrm().is_register());
465 fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) + fpu_get(0));
466 fpu_pop();
467}
468
469void SoftFPU::FIADD_RM32(const X86::Instruction& insn)
470{
471 VERIFY(!insn.modrm().is_register());
472 auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value();
473 // FIXME: Respect shadow values
474 fpu_set(0, fpu_get(0) + (long double)m32int);
475}
476void SoftFPU::FIADD_RM16(const X86::Instruction& insn)
477{
478 VERIFY(!insn.modrm().is_register());
479 auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value();
480 // FIXME: Respect shadow values
481 fpu_set(0, fpu_get(0) + (long double)m16int);
482}
483
484void SoftFPU::FSUB_RM32(const X86::Instruction& insn)
485{
486 if (insn.modrm().is_register()) {
487 fpu_set(0, fpu_get(0) - fpu_get(insn.modrm().register_index()));
488 } else {
489 auto new_f32 = insn.modrm().read32(m_cpu, insn);
490 // FIXME: Respect shadow values
491 auto f32 = bit_cast<float>(new_f32.value());
492 fpu_set(0, fpu_get(0) - f32);
493 }
494}
495void SoftFPU::FSUB_RM64(const X86::Instruction& insn)
496{
497 if (insn.modrm().is_register()) {
498 // Note: This is FSUBR (DC E8+i FSUBR st(i) st(0)) in the spec
499 fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) - fpu_get(0));
500 } else {
501 auto new_f64 = insn.modrm().read64(m_cpu, insn);
502 // FIXME: Respect shadow values
503 auto f64 = bit_cast<double>(new_f64.value());
504 fpu_set(0, fpu_get(0) - f64);
505 }
506}
507void SoftFPU::FSUBP(const X86::Instruction& insn)
508{
509 VERIFY(insn.modrm().is_register());
510 fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) - fpu_get(0));
511 fpu_pop();
512}
513
514void SoftFPU::FSUBR_RM32(const X86::Instruction& insn)
515{
516 if (insn.modrm().is_register()) {
517 fpu_set(0, fpu_get(insn.modrm().register_index()) - fpu_get(0));
518 } else {
519 auto new_f32 = insn.modrm().read32(m_cpu, insn);
520 // FIXME: Respect shadow values
521 auto f32 = bit_cast<float>(new_f32.value());
522 fpu_set(0, f32 - fpu_get(0));
523 }
524}
525void SoftFPU::FSUBR_RM64(const X86::Instruction& insn)
526{
527 if (insn.modrm().is_register()) {
528 // Note: This is FSUB (DC E0+i FSUB st(i) st(0)) in the spec
529 fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) - fpu_get(0));
530 } else {
531 auto new_f64 = insn.modrm().read64(m_cpu, insn);
532 // FIXME: Respect shadow values
533 auto f64 = bit_cast<double>(new_f64.value());
534 fpu_set(0, f64 - fpu_get(0));
535 }
536}
537void SoftFPU::FSUBRP(const X86::Instruction& insn)
538{
539 VERIFY(insn.modrm().is_register());
540 fpu_set(insn.modrm().register_index(), fpu_get(0) - fpu_get(insn.modrm().register_index()));
541 fpu_pop();
542}
543
544void SoftFPU::FISUB_RM32(const X86::Instruction& insn)
545{
546 VERIFY(!insn.modrm().is_register());
547 auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value();
548 // FIXME: Respect shadow values
549 fpu_set(0, fpu_get(0) - (long double)m32int);
550}
551void SoftFPU::FISUB_RM16(const X86::Instruction& insn)
552{
553 VERIFY(!insn.modrm().is_register());
554 auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value();
555 // FIXME: Respect shadow values
556 fpu_set(0, fpu_get(0) - (long double)m16int);
557}
558
559void SoftFPU::FISUBR_RM16(const X86::Instruction& insn)
560{
561 VERIFY(!insn.modrm().is_register());
562 auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value();
563 // FIXME: Respect shadow values
564 fpu_set(0, (long double)m16int - fpu_get(0));
565}
566void SoftFPU::FISUBR_RM32(const X86::Instruction& insn)
567{
568 VERIFY(!insn.modrm().is_register());
569 auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value();
570 // FIXME: Respect shadow values
571 fpu_set(0, (long double)m32int - fpu_get(0));
572}
573
574void SoftFPU::FMUL_RM32(const X86::Instruction& insn)
575{
576 // FIXME look at ::INC_foo for how mem/reg stuff is handled, and use that here too to make sure this is only called for mem32 ops
577 if (insn.modrm().is_register()) {
578 fpu_set(0, fpu_get(0) * fpu_get(insn.modrm().register_index()));
579 } else {
580 auto new_f32 = insn.modrm().read32(m_cpu, insn);
581 // FIXME: Respect shadow values
582 auto f32 = bit_cast<float>(new_f32.value());
583 fpu_set(0, fpu_get(0) * f32);
584 }
585}
586void SoftFPU::FMUL_RM64(const X86::Instruction& insn)
587{
588 // FIXME look at ::INC_foo for how mem/reg stuff is handled, and use that here too to make sure this is only called for mem64 ops
589 if (insn.modrm().is_register()) {
590 fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) * fpu_get(0));
591 } else {
592 auto new_f64 = insn.modrm().read64(m_cpu, insn);
593 // FIXME: Respect shadow values
594 auto f64 = bit_cast<double>(new_f64.value());
595 fpu_set(0, fpu_get(0) * f64);
596 }
597}
598void SoftFPU::FMULP(const X86::Instruction& insn)
599{
600 VERIFY(insn.modrm().is_register());
601 fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) * fpu_get(0));
602 fpu_pop();
603}
604
605void SoftFPU::FIMUL_RM32(const X86::Instruction& insn)
606{
607 VERIFY(!insn.modrm().is_register());
608 auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value();
609 // FIXME: Respect shadow values
610 fpu_set(0, fpu_get(0) * m32int);
611}
612void SoftFPU::FIMUL_RM16(const X86::Instruction& insn)
613{
614 VERIFY(!insn.modrm().is_register());
615 auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value();
616 // FIXME: Respect shadow values
617 fpu_set(0, fpu_get(0) * m16int);
618}
619
620void SoftFPU::FDIV_RM32(const X86::Instruction& insn)
621{
622 if (insn.modrm().is_register()) {
623 fpu_set(0, fpu_get(0) / fpu_get(insn.modrm().register_index()));
624 } else {
625 auto new_f32 = insn.modrm().read32(m_cpu, insn);
626 // FIXME: Respect shadow values
627 auto f32 = bit_cast<float>(new_f32.value());
628 // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0
629 fpu_set(0, fpu_get(0) / f32);
630 }
631}
632void SoftFPU::FDIV_RM64(const X86::Instruction& insn)
633{
634 if (insn.modrm().is_register()) {
635 // Note: This is FDIVR (DC F0+i FDIVR st(i) st(0)) in the spec
636 fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) / fpu_get(0));
637 } else {
638 auto new_f64 = insn.modrm().read64(m_cpu, insn);
639 // FIXME: Respect shadow values
640 auto f64 = bit_cast<double>(new_f64.value());
641 // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0
642 fpu_set(0, fpu_get(0) / f64);
643 }
644}
645void SoftFPU::FDIVP(const X86::Instruction& insn)
646{
647 VERIFY(insn.modrm().is_register());
648 // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0
649 fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) / fpu_get(0));
650 fpu_pop();
651}
652
653void SoftFPU::FDIVR_RM32(const X86::Instruction& insn)
654{
655 if (insn.modrm().is_register()) {
656 fpu_set(0, fpu_get(insn.modrm().register_index()) / fpu_get(0));
657 } else {
658 auto new_f32 = insn.modrm().read32(m_cpu, insn);
659 // FIXME: Respect shadow values
660 auto f32 = bit_cast<float>(new_f32.value());
661 // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0
662 fpu_set(0, f32 / fpu_get(0));
663 }
664}
665void SoftFPU::FDIVR_RM64(const X86::Instruction& insn)
666{
667 if (insn.modrm().is_register()) {
668 // Note: This is FDIV (DC F8+i FDIV st(i) st(0)) in the spec
669 fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) / fpu_get(0));
670 } else {
671 auto new_f64 = insn.modrm().read64(m_cpu, insn);
672 // FIXME: Respect shadow values
673 auto f64 = bit_cast<double>(new_f64.value());
674 // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0
675 fpu_set(0, f64 / fpu_get(0));
676 }
677}
678void SoftFPU::FDIVRP(const X86::Instruction& insn)
679{
680 VERIFY(insn.modrm().is_register());
681 // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0
682 fpu_set(insn.modrm().register_index(), fpu_get(0) / fpu_get(insn.modrm().register_index()));
683 fpu_pop();
684}
685
686void SoftFPU::FIDIV_RM16(const X86::Instruction& insn)
687{
688 VERIFY(!insn.modrm().is_register());
689 auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value();
690 // FIXME: Respect shadow values
691 // FIXME: Raise IA on 0 / _=0, raise Z on finite / +-0
692 fpu_set(0, fpu_get(0) / m16int);
693}
694void SoftFPU::FIDIV_RM32(const X86::Instruction& insn)
695{
696 VERIFY(!insn.modrm().is_register());
697 auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value();
698 // FIXME: Respect shadow values
699 // FIXME: Raise IA on 0 / _=0, raise Z on finite / +-0
700 fpu_set(0, fpu_get(0) / m32int);
701}
702
703void SoftFPU::FIDIVR_RM16(const X86::Instruction& insn)
704{
705 VERIFY(!insn.modrm().is_register());
706 auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value();
707 // FIXME: Respect shadow values
708 // FIXME: Raise IA on 0 / _=0, raise Z on finite / +-0
709 fpu_set(0, m16int / fpu_get(0));
710}
711void SoftFPU::FIDIVR_RM32(const X86::Instruction& insn)
712{
713 VERIFY(!insn.modrm().is_register());
714 auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value();
715 // FIXME: Respect shadow values
716 // FIXME: Raise IA on 0 / _=0, raise Z on finite / +-0
717 fpu_set(0, m32int / fpu_get(0));
718}
719
720void SoftFPU::FPREM(const X86::Instruction&)
721{
722 // FIXME: FPREM should only be able to reduce top's exponent by a maximum
723 // amount of 32-63 (impl-specific)
724 long double top = fpu_get(0);
725 long double one = fpu_get(1);
726
727 int Q = static_cast<int>(truncl(top / one));
728 top = top - (one * Q);
729 set_c2(0);
730 set_c1(Q & 1);
731 set_c3((Q >> 1) & 1);
732 set_c0((Q >> 2) & 1);
733
734 fpu_set(0, top);
735}
736void SoftFPU::FPREM1(const X86::Instruction&)
737{
738 // FIXME: FPREM1 should only be able to reduce top's exponent by a maximum
739 // amount of 32-63 (impl-specific)
740 long double top = fpu_get(0);
741 long double one = fpu_get(1);
742
743 int Q = static_cast<int>(roundl(top / one));
744 top = top - (one * Q);
745 set_c2(0);
746 set_c1(Q & 1);
747 set_c3((Q >> 1) & 1);
748 set_c0((Q >> 2) & 1);
749
750 fpu_set(0, top);
751}
752void SoftFPU::FABS(const X86::Instruction&)
753{
754 set_c1(0);
755 fpu_set(0, __builtin_fabsl(fpu_get(0)));
756}
757void SoftFPU::FCHS(const X86::Instruction&)
758{
759 set_c1(0);
760 fpu_set(0, -fpu_get(0));
761}
762
763void SoftFPU::FRNDINT(const X86::Instruction&)
764{
765 // FIXME: Raise #IA #D
766 auto res = round_checked<long double>(fpu_get(0));
767 fpu_set(0, res);
768}
769
770void SoftFPU::FSCALE(const X86::Instruction&)
771{
772 // FIXME: Raise #IA #D #U #O #P
773 fpu_set(0, fpu_get(0) * exp2l(truncl(fpu_get(1))));
774}
775
776void SoftFPU::FSQRT(const X86::Instruction&)
777{
778 // FIXME: Raise #IA #D #P
779 if (fpu_get(0) < 0)
780 fpu_set_exception(FPU_Exception::InvalidOperation);
781 fpu_set(0, sqrtl(fpu_get(0)));
782}
783
784void SoftFPU::FXTRACT(const X86::Instruction&) { TODO_INSN(); }
785
786// COMPARISON
787
788// FIXME: there may be an implicit argument, how is this conveyed by the insn
789void SoftFPU::FCOM_RM32(const X86::Instruction&) { TODO_INSN(); }
790void SoftFPU::FCOM_RM64(const X86::Instruction&) { TODO_INSN(); }
791void SoftFPU::FCOMP_RM32(const X86::Instruction&) { TODO_INSN(); }
792void SoftFPU::FCOMP_RM64(const X86::Instruction&) { TODO_INSN(); }
793void SoftFPU::FCOMPP(const X86::Instruction&)
794{
795 if (fpu_isnan(0) || fpu_isnan(1)) {
796 fpu_set_exception(FPU_Exception::InvalidOperation);
797 if (m_fpu_cw.mask_invalid)
798 fpu_set_unordered();
799 } else {
800 set_c2(0);
801 set_c0(fpu_get(0) < fpu_get(1));
802 set_c3(fpu_get(0) == fpu_get(1));
803 }
804 fpu_pop();
805 fpu_pop();
806}
807
808void SoftFPU::FUCOM(const X86::Instruction&) { TODO_INSN(); } // Needs QNaN detection
809void SoftFPU::FUCOMP(const X86::Instruction&) { TODO_INSN(); }
810void SoftFPU::FUCOMPP(const X86::Instruction&) { TODO_INSN(); }
811
812void SoftFPU::FICOM_RM16(const X86::Instruction& insn)
813{
814 // FIXME: Check for denormals
815 VERIFY(insn.modrm().is_register());
816 auto val_shd = insn.modrm().read16(m_cpu, insn);
817 warn_if_uninitialized(val_shd, "int16 compare to float");
818 auto val = static_cast<i16>(val_shd.value());
819 if (fpu_isnan(0)) {
820 fpu_set_unordered();
821 } else {
822 set_c0(fpu_get(0) < val);
823 set_c2(0);
824 set_c3(fpu_get(0) == val);
825 }
826 set_c1(0);
827}
828void SoftFPU::FICOM_RM32(const X86::Instruction& insn)
829{
830 // FIXME: Check for denormals
831 VERIFY(insn.modrm().is_register());
832 auto val_shd = insn.modrm().read32(m_cpu, insn);
833 warn_if_uninitialized(val_shd, "int32 compare to float");
834 auto val = static_cast<i32>(val_shd.value());
835 if (fpu_isnan(0)) {
836 fpu_set_unordered();
837 } else {
838 set_c0(fpu_get(0) < val);
839 set_c2(0);
840 set_c3(fpu_get(0) == val);
841 }
842 set_c1(0);
843}
844void SoftFPU::FICOMP_RM16(const X86::Instruction& insn)
845{
846 FICOM_RM16(insn);
847 fpu_pop();
848}
849void SoftFPU::FICOMP_RM32(const X86::Instruction& insn)
850{
851 FICOM_RM32(insn);
852 fpu_pop();
853}
854
855void SoftFPU::FCOMI(const X86::Instruction& insn)
856{
857 auto i = insn.modrm().rm();
858 // FIXME: QNaN / exception handling.
859 set_c0(0);
860 if (isnan(fpu_get(0)) || isnan(fpu_get(1))) {
861 fpu_set_exception(FPU_Exception::InvalidOperation);
862 m_cpu.set_zf(1);
863 m_cpu.set_pf(1);
864 m_cpu.set_cf(1);
865 } else {
866 m_cpu.set_zf(fpu_get(0) == fpu_get(i));
867 m_cpu.set_pf(false);
868 m_cpu.set_cf(fpu_get(0) < fpu_get(i));
869 }
870 if (!fpu_is_set(1))
871 fpu_set_exception(FPU_Exception::Underflow);
872
873 m_cpu.set_of(false);
874 m_cpu.set_af(false);
875 m_cpu.set_sf(false);
876
877 // FIXME: Taint should be based on ST(0) and ST(i)
878 m_cpu.m_flags_tainted = false;
879}
880void SoftFPU::FCOMIP(const X86::Instruction& insn)
881{
882 FCOMI(insn);
883 fpu_pop();
884}
885
886void SoftFPU::FUCOMI(const X86::Instruction& insn)
887{
888 auto i = insn.modrm().rm();
889 // FIXME: Unordered comparison checks.
890 // FIXME: QNaN / exception handling.
891 set_c1(0);
892 if (fpu_isnan(0) || fpu_isnan(i)) {
893 m_cpu.set_zf(true);
894 m_cpu.set_pf(true);
895 m_cpu.set_cf(true);
896 } else {
897 m_cpu.set_zf(fpu_get(0) == fpu_get(i));
898 m_cpu.set_pf(false);
899 m_cpu.set_cf(fpu_get(0) < fpu_get(i));
900 }
901 m_cpu.set_of(false);
902 m_cpu.set_af(false);
903 m_cpu.set_sf(false);
904
905 // FIXME: Taint should be based on ST(0) and ST(i)
906 m_cpu.m_flags_tainted = false;
907}
908void SoftFPU::FUCOMIP(const X86::Instruction& insn)
909{
910 FUCOMI(insn);
911 fpu_pop();
912}
913
914void SoftFPU::FTST(const X86::Instruction&)
915{
916 // FIXME: maybe check for denormal
917 set_c1(0);
918 if (fpu_isnan(0))
919 // raise #IA?
920 fpu_set_unordered();
921 else {
922 set_c0(fpu_get(0) < 0.);
923 set_c2(0);
924 set_c3(fpu_get(0) == 0.);
925 }
926}
927void SoftFPU::FXAM(const X86::Instruction&)
928{
929 if (m_reg_is_mmx[m_fpu_stack_top]) {
930 // technically a subset of NaN/INF, with the Tag set to valid,
931 // but we have our own helper for this
932 set_c0(0);
933 set_c2(0);
934 set_c3(0);
935 } else {
936 switch (fpu_get_tag(0)) {
937 case FPU_Tag::Valid:
938 set_c0(0);
939 set_c2(1);
940 set_c3(0);
941 break;
942 case FPU_Tag::Zero:
943 set_c0(1);
944 set_c2(0);
945 set_c3(0);
946 break;
947 case FPU_Tag::Special:
948 if (isinf(fpu_get(0))) {
949 set_c0(1);
950 set_c2(1);
951 set_c3(0);
952 } else if (isnan(fpu_get(0))) {
953 set_c0(1);
954 set_c2(0);
955 set_c3(0);
956 } else {
957 // denormalized
958 set_c0(0);
959 set_c2(1);
960 set_c3(1);
961 }
962 break;
963 case FPU_Tag::Empty:
964 set_c0(1);
965 set_c2(0);
966 set_c3(1);
967 break;
968 default:
969 VERIFY_NOT_REACHED();
970 }
971 }
972 set_c1(signbit(fpu_get(0)));
973}
974
975// TRANSCENDENTAL
976void SoftFPU::FSIN(const X86::Instruction&)
977{
978 // FIXME: Raise #IA #D #P
979 // FIXME: Set C1 on when result was rounded up, cleared otherwise
980 // FIXME: Set C2 to 1 if ST(0) is outside range of -2^63 to +2^63; else set to 0
981 // ST(0) shall remain unchanged in this case
982 fpu_set(0, sinl(fpu_get(0)));
983}
984void SoftFPU::FCOS(const X86::Instruction&)
985{
986 // FIXME: Raise #IA #D #P
987 // FIXME: Set C1 on when result was rounded up, cleared otherwise
988 // FIXME: Set C2 to 1 if ST(0) is outside range of -2^63 to +2^63; else set to 0
989 // ST(0) shall remain unchanged in this case
990 fpu_set(0, cosl(fpu_get(0)));
991}
992void SoftFPU::FSINCOS(const X86::Instruction&)
993{
994 // FIXME: Raise #IA #D #P
995 // FIXME: Set C1 on when result was rounded up, cleared otherwise
996 // FIXME: Set C2 to 1 if ST(0) is outside range of -2^63 to +2^63; else set to 0
997 // ST(0) shall remain unchanged in this case
998 long double sin = sinl(fpu_get(0));
999 long double cos = cosl(fpu_get(0));
1000 fpu_set(0, sin);
1001 fpu_push(cos);
1002}
1003
1004void SoftFPU::FPTAN(const X86::Instruction&)
1005{
1006 // FIXME: Raise #IA #D #U #P
1007 // FIXME: Set C1 on when result was rounded up, cleared otherwise
1008 // FIXME: Set C2 to 1 if ST(0) is outside range of -2^63 to +2^63; else set to 0
1009 // ST(0) shall remain unchanged in this case
1010 fpu_set(0, tanl(fpu_get(0)));
1011 fpu_push(1.0f);
1012}
1013void SoftFPU::FPATAN(const X86::Instruction&)
1014{
1015 // FIXME: Raise #IA #D #U #P
1016 // FIXME: Set C1 on when result was rounded up, cleared otherwise
1017 // Note: Not implemented 80287 quirk:
1018 // Restriction to 0 ≤ |ST(1)| < |ST(0)| < +∞
1019 fpu_set(1, atan2l(fpu_get(1), fpu_get(0)));
1020 fpu_pop();
1021}
1022
1023void SoftFPU::F2XM1(const X86::Instruction&)
1024{
1025 // FIXME: Raise #IA #D #U #P
1026 // FIXME: Set C1 on when result was rounded up, cleared otherwise
1027 // FIXME: Validate ST(0) is in range –1.0 to +1.0
1028 auto val = fpu_get(0);
1029 fpu_set(0, exp2(val) - 1.0l);
1030}
1031void SoftFPU::FYL2X(const X86::Instruction&)
1032{
1033 // FIXME: Set C1 on when result was rounded up, cleared otherwise
1034 // FIXME: Raise #IA #D #U #O #P
1035 auto x = fpu_get(0);
1036 auto y = fpu_get(1);
1037 if (x < 0. && !isinf(x)) {
1038 fpu_set_exception(FPU_Exception::InvalidOperation);
1039 // FIXME: Spec does not say what to do here....
1040 // So lets just ask libm....
1041 fpu_set(1, y * log2l(x));
1042 } else if (x == 0.) {
1043 if (y == 0)
1044 fpu_set_exception(FPU_Exception::InvalidOperation);
1045 fpu_set_exception(FPU_Exception::ZeroDivide);
1046 fpu_set(1, INFINITY * (signbit(y) ? 1 : -1));
1047 } else {
1048 fpu_set(1, y * log2l(x));
1049 }
1050 fpu_pop();
1051}
1052void SoftFPU::FYL2XP1(const X86::Instruction&)
1053{
1054 // FIXME: Raise #IA #O #U #P #D
1055 auto x = fpu_get(0);
1056 auto y = fpu_get(1);
1057 if (x == 0 && isinf(y))
1058 fpu_set_exception(FPU_Exception::InvalidOperation);
1059
1060 fpu_set(1, (y * log2l(x + 1.0l)));
1061 fpu_pop();
1062}
1063
1064// LOAD CONSTANT
1065void SoftFPU::FLD1(const X86::Instruction&)
1066{
1067 set_c1(0);
1068 fpu_push(1.0l);
1069}
1070void SoftFPU::FLDZ(const X86::Instruction&)
1071{
1072 set_c1(0);
1073 fpu_push(0.0l);
1074}
1075void SoftFPU::FLDPI(const X86::Instruction&)
1076{
1077 set_c1(0);
1078 fpu_push(M_PIl);
1079}
1080void SoftFPU::FLDL2E(const X86::Instruction&)
1081{
1082 set_c1(0);
1083 fpu_push(M_LOG2El);
1084}
1085void SoftFPU::FLDLN2(const X86::Instruction&)
1086{
1087 set_c1(0);
1088 fpu_push(M_LN2l);
1089}
1090void SoftFPU::FLDL2T(const X86::Instruction&)
1091{
1092 set_c1(0);
1093 fpu_push(log2l(10.0l));
1094}
1095void SoftFPU::FLDLG2(const X86::Instruction&)
1096{
1097 set_c1(0);
1098 fpu_push(log10l(2.0l));
1099}
1100
1101// CONTROL
1102void SoftFPU::FINCSTP(const X86::Instruction&)
1103{
1104 m_fpu_stack_top = (m_fpu_stack_top + 1u) % 8u;
1105 set_c1(0);
1106}
1107void SoftFPU::FDECSTP(const X86::Instruction&)
1108{
1109 m_fpu_stack_top = (m_fpu_stack_top - 1u) % 8u;
1110 set_c1(0);
1111}
1112
1113void SoftFPU::FFREE(const X86::Instruction& insn)
1114{
1115 fpu_set_tag(insn.modrm().reg_fpu(), FPU_Tag::Empty);
1116}
1117void SoftFPU::FFREEP(const X86::Instruction& insn)
1118{
1119 FFREE(insn);
1120 fpu_pop();
1121}
1122
1123void SoftFPU::FNINIT(const X86::Instruction&)
1124{
1125 m_fpu_cw.cw = 0x037F;
1126 m_fpu_sw = 0;
1127 m_fpu_tw = 0xFFFF;
1128
1129 m_fpu_ip = 0;
1130 m_fpu_cs = 0;
1131
1132 m_fpu_dp = 0;
1133 m_fpu_ds = 0;
1134
1135 m_fpu_iop = 0;
1136};
1137void SoftFPU::FNCLEX(const X86::Instruction&)
1138{
1139 m_fpu_error_invalid = 0;
1140 m_fpu_error_denorm = 0;
1141 m_fpu_error_zero_div = 0;
1142 m_fpu_error_overflow = 0;
1143 m_fpu_error_underflow = 0;
1144 m_fpu_error_precision = 0;
1145 m_fpu_error_stackfault = 0;
1146 m_fpu_busy = 0;
1147}
1148
1149void SoftFPU::FNSTCW(const X86::Instruction& insn)
1150{
1151 insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(m_fpu_cw.cw));
1152}
1153void SoftFPU::FLDCW(const X86::Instruction& insn)
1154{
1155 m_fpu_cw.cw = insn.modrm().read16(m_cpu, insn).value();
1156
1157 // Just let the host's x87 handle the rounding for us
1158 // We do not want to accedentally raise an FP-Exception on the host, so we
1159 // mask all exceptions
1160 AK::X87ControlWord temp = m_fpu_cw;
1161 temp.mask_invalid = 1;
1162 temp.mask_denorm = 1;
1163 temp.mask_zero_div = 1;
1164 temp.mask_overflow = 1;
1165 temp.mask_underflow = 1;
1166 temp.mask_precision = 1;
1167 AK::set_cw_x87(temp);
1168}
1169
1170void SoftFPU::FNSTENV(const X86::Instruction& insn)
1171{
1172 // Assuming we are always in Protected mode
1173 // FIXME: 16-bit Format
1174
1175 // 32-bit Format
1176 /* 31--------------16---------------0
1177 * | | CW | 0
1178 * +----------------+---------------+
1179 * | | SW | 4
1180 * +----------------+---------------+
1181 * | | TW | 8
1182 * +----------------+---------------+
1183 * | FIP | 12
1184 * +----+-----------+---------------+
1185 * |0000|fpuOp[10:0]| FIP_sel | 16
1186 * +----+-----------+---------------+
1187 * | FDP | 20
1188 * +----------------+---------------+
1189 * | | FDP_ds | 24
1190 * +----------------|---------------+
1191 * */
1192
1193 auto address = insn.modrm().resolve(m_cpu, insn);
1194
1195 m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_cw.cw));
1196 address.set_offset(address.offset() + 4);
1197 m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_sw));
1198 address.set_offset(address.offset() + 4);
1199 m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_tw));
1200 address.set_offset(address.offset() + 4);
1201 m_cpu.write_memory32(address, shadow_wrap_as_initialized(m_fpu_ip));
1202 address.set_offset(address.offset() + 4);
1203 m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_cs));
1204 address.set_offset(address.offset() + 2);
1205 m_cpu.write_memory16(address, shadow_wrap_as_initialized<u16>(m_fpu_iop & 0x3FFU));
1206 address.set_offset(address.offset() + 2);
1207 m_cpu.write_memory32(address, shadow_wrap_as_initialized(m_fpu_dp));
1208 address.set_offset(address.offset() + 4);
1209 m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_ds));
1210}
1211void SoftFPU::FLDENV(const X86::Instruction& insn)
1212{
1213 // Assuming we are always in Protected mode
1214 // FIXME: 16-bit Format
1215 auto address = insn.modrm().resolve(m_cpu, insn);
1216
1217 // FIXME: Shadow Values
1218 m_fpu_cw.cw = m_cpu.read_memory16(address).value();
1219 // See note in FLDCW
1220 AK::X87ControlWord temp = m_fpu_cw;
1221 temp.mask_invalid = 1;
1222 temp.mask_denorm = 1;
1223 temp.mask_zero_div = 1;
1224 temp.mask_overflow = 1;
1225 temp.mask_underflow = 1;
1226 temp.mask_precision = 1;
1227 AK::set_cw_x87(temp);
1228
1229 address.set_offset(address.offset() + 4);
1230 m_fpu_sw = m_cpu.read_memory16(address).value();
1231 address.set_offset(address.offset() + 4);
1232 m_fpu_tw = m_cpu.read_memory16(address).value();
1233 address.set_offset(address.offset() + 4);
1234 m_fpu_ip = m_cpu.read_memory32(address).value();
1235 address.set_offset(address.offset() + 4);
1236 m_fpu_cs = m_cpu.read_memory16(address).value();
1237 address.set_offset(address.offset() + 2);
1238 m_fpu_iop = m_cpu.read_memory16(address).value();
1239 address.set_offset(address.offset() + 2);
1240 m_fpu_dp = m_cpu.read_memory32(address).value();
1241 address.set_offset(address.offset() + 4);
1242 m_fpu_ds = m_cpu.read_memory16(address).value();
1243}
1244
1245void SoftFPU::FNSAVE(const X86::Instruction& insn)
1246{
1247 FNSTENV(insn);
1248
1249 auto address = insn.modrm().resolve(m_cpu, insn);
1250 address.set_offset(address.offset() + 28); // size of the ENV
1251
1252 // write fpu-stack to memory
1253 u8 raw_data[80];
1254 for (int i = 0; i < 8; ++i) {
1255 memcpy(raw_data + 10 * i, &m_storage[i], 10);
1256 }
1257 for (int i = 0; i < 5; ++i) {
1258 // FIXME: Shadow Value
1259 m_cpu.write_memory128(address, shadow_wrap_as_initialized(((u128*)raw_data)[i]));
1260 address.set_offset(address.offset() + 16);
1261 }
1262
1263 FNINIT(insn);
1264}
1265void SoftFPU::FRSTOR(const X86::Instruction& insn)
1266{
1267 FLDENV(insn);
1268
1269 auto address = insn.modrm().resolve(m_cpu, insn);
1270 address.set_offset(address.offset() + 28); // size of the ENV
1271
1272 // read fpu-stack from memory
1273 u8 raw_data[80];
1274 for (int i = 0; i < 5; ++i) {
1275 // FIXME: Shadow Value
1276 ((u128*)raw_data)[i] = m_cpu.read_memory128(address).value();
1277 address.set_offset(address.offset() + 16);
1278 }
1279 for (int i = 0; i < 8; ++i) {
1280 memcpy(&m_storage[i], raw_data + 10 * i, 10);
1281 }
1282
1283 memset(m_reg_is_mmx, 0, sizeof(m_reg_is_mmx));
1284}
1285
1286void SoftFPU::FNSTSW(const X86::Instruction& insn)
1287{
1288 insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(m_fpu_sw));
1289}
1290void SoftFPU::FNSTSW_AX(const X86::Instruction&)
1291{
1292 m_cpu.set_ax(shadow_wrap_as_initialized(m_fpu_sw));
1293}
1294// FIXME: FWAIT
1295void SoftFPU::FNOP(const X86::Instruction&) { }
1296
1297// DO NOTHING?
1298void SoftFPU::FNENI(const X86::Instruction&) { TODO_INSN(); }
1299void SoftFPU::FNDISI(const X86::Instruction&) { TODO_INSN(); }
1300void SoftFPU::FNSETPM(const X86::Instruction&) { TODO_INSN(); }
1301
1302// MMX
1303// helpers
1304#define LOAD_MM_MM64M() \
1305 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ \
1306 MMX mm; \
1307 MMX mm64m; \
1308 if (insn.modrm().mod() == 0b11) { /* 0b11 signals a register */ \
1309 mm64m = mmx_get(insn.modrm().rm()); \
1310 } else { \
1311 auto temp = insn.modrm().read64(m_cpu, insn); \
1312 warn_if_uninitialized(temp, "Read of uninitialized Memory as Packed integer"); \
1313 mm64m.raw = temp.value(); \
1314 } \
1315 mm = mmx_get(insn.modrm().reg())
1316
1317#define MMX_intrinsic(intrinsic, res_type, actor_type) \
1318 LOAD_MM_MM64M(); \
1319 mm.res_type = __builtin_ia32_##intrinsic(mm.actor_type, mm64m.actor_type); \
1320 mmx_set(insn.modrm().reg(), mm); \
1321 mmx_common();
1322
1323// ARITHMETIC
1324void SoftFPU::PADDB_mm1_mm2m64(const X86::Instruction& insn)
1325{
1326 LOAD_MM_MM64M();
1327 mm.v8 += mm64m.v8;
1328 mmx_set(insn.modrm().reg(), mm);
1329 mmx_common();
1330}
1331void SoftFPU::PADDW_mm1_mm2m64(const X86::Instruction& insn)
1332{
1333 LOAD_MM_MM64M();
1334 mm.v16 += mm64m.v16;
1335 mmx_set(insn.modrm().reg(), mm);
1336 mmx_common();
1337}
1338void SoftFPU::PADDD_mm1_mm2m64(const X86::Instruction& insn)
1339{
1340 LOAD_MM_MM64M();
1341 mm.v32 += mm64m.v32;
1342 mmx_set(insn.modrm().reg(), mm);
1343 mmx_common();
1344}
1345void SoftFPU::PADDSB_mm1_mm2m64(const X86::Instruction& insn)
1346{
1347 MMX_intrinsic(paddsb, v8, v8);
1348}
1349void SoftFPU::PADDSW_mm1_mm2m64(const X86::Instruction& insn)
1350{
1351 MMX_intrinsic(paddsw, v16, v16);
1352}
1353void SoftFPU::PADDUSB_mm1_mm2m64(const X86::Instruction& insn)
1354{
1355 MMX_intrinsic(paddusb, v8, v8);
1356}
1357void SoftFPU::PADDUSW_mm1_mm2m64(const X86::Instruction& insn)
1358{
1359 MMX_intrinsic(paddusw, v16, v16);
1360}
1361
1362void SoftFPU::PSUBB_mm1_mm2m64(const X86::Instruction& insn)
1363{
1364 LOAD_MM_MM64M();
1365 mm.v8 -= mm64m.v8;
1366 mmx_set(insn.modrm().reg(), mm);
1367 mmx_common();
1368}
1369void SoftFPU::PSUBW_mm1_mm2m64(const X86::Instruction& insn)
1370{
1371 LOAD_MM_MM64M();
1372 mm.v16 -= mm64m.v16;
1373 mmx_set(insn.modrm().reg(), mm);
1374 mmx_common();
1375}
1376void SoftFPU::PSUBD_mm1_mm2m64(const X86::Instruction& insn)
1377{
1378 LOAD_MM_MM64M();
1379 mm.v32 -= mm64m.v32;
1380 mmx_set(insn.modrm().reg(), mm);
1381 mmx_common();
1382}
1383void SoftFPU::PSUBSB_mm1_mm2m64(const X86::Instruction& insn)
1384{
1385 MMX_intrinsic(psubsb, v8, v8);
1386}
1387void SoftFPU::PSUBSW_mm1_mm2m64(const X86::Instruction& insn)
1388{
1389 MMX_intrinsic(psubsw, v16, v16);
1390}
1391void SoftFPU::PSUBUSB_mm1_mm2m64(const X86::Instruction& insn)
1392{
1393 MMX_intrinsic(psubusb, v8, v8);
1394}
1395void SoftFPU::PSUBUSW_mm1_mm2m64(const X86::Instruction& insn)
1396{
1397 MMX_intrinsic(psubusw, v16, v16);
1398}
1399
1400void SoftFPU::PMULHW_mm1_mm2m64(const X86::Instruction& insn)
1401{
1402 MMX_intrinsic(pmulhw, v16, v16);
1403}
1404void SoftFPU::PMULLW_mm1_mm2m64(const X86::Instruction& insn)
1405{
1406 MMX_intrinsic(pmullw, v16, v16);
1407}
1408
1409void SoftFPU::PMADDWD_mm1_mm2m64(const X86::Instruction& insn)
1410{
1411 MMX_intrinsic(pmaddwd, v32, v16);
1412}
1413
1414// COMPARISON
1415void SoftFPU::PCMPEQB_mm1_mm2m64(const X86::Instruction& insn)
1416{
1417 LOAD_MM_MM64M();
1418
1419 mm.v8 = mm.v8 == mm64m.v8;
1420
1421 mmx_set(insn.modrm().reg(), mm);
1422 mmx_common();
1423}
1424void SoftFPU::PCMPEQW_mm1_mm2m64(const X86::Instruction& insn)
1425{
1426 LOAD_MM_MM64M();
1427
1428 mm.v16 = mm.v16 == mm64m.v16;
1429 mmx_set(insn.modrm().reg(), mm);
1430 mmx_common();
1431}
1432void SoftFPU::PCMPEQD_mm1_mm2m64(const X86::Instruction& insn)
1433{
1434 LOAD_MM_MM64M();
1435
1436 mm.v32 = mm.v32 == mm64m.v32;
1437
1438 mmx_set(insn.modrm().reg(), mm);
1439 mmx_common();
1440}
1441
1442void SoftFPU::PCMPGTB_mm1_mm2m64(const X86::Instruction& insn)
1443{
1444 LOAD_MM_MM64M();
1445
1446 mm.v8 = mm.v8 > mm64m.v8;
1447
1448 mmx_set(insn.modrm().reg(), mm);
1449 mmx_common();
1450}
1451void SoftFPU::PCMPGTW_mm1_mm2m64(const X86::Instruction& insn)
1452{
1453 LOAD_MM_MM64M();
1454
1455 mm.v16 = mm.v16 > mm64m.v16;
1456
1457 mmx_set(insn.modrm().reg(), mm);
1458 mmx_common();
1459}
1460void SoftFPU::PCMPGTD_mm1_mm2m64(const X86::Instruction& insn)
1461{
1462 LOAD_MM_MM64M();
1463
1464 mm.v32 = mm.v32 > mm64m.v32;
1465
1466 mmx_set(insn.modrm().reg(), mm);
1467 mmx_common();
1468}
1469
1470// CONVERSION
1471void SoftFPU::PACKSSDW_mm1_mm2m64(const X86::Instruction& insn)
1472{
1473 MMX_intrinsic(packssdw, v16, v32);
1474}
1475void SoftFPU::PACKSSWB_mm1_mm2m64(const X86::Instruction& insn)
1476{
1477 MMX_intrinsic(packsswb, v8, v16);
1478}
1479void SoftFPU::PACKUSWB_mm1_mm2m64(const X86::Instruction& insn)
1480{
1481 MMX_intrinsic(packuswb, v8, v16);
1482}
1483
1484// UNPACK
1485void SoftFPU::PUNPCKHBW_mm1_mm2m64(const X86::Instruction& insn)
1486{
1487 MMX_intrinsic(punpckhbw, v8, v8);
1488}
1489
1490void SoftFPU::PUNPCKHWD_mm1_mm2m64(const X86::Instruction& insn)
1491{
1492 MMX_intrinsic(punpckhwd, v16, v16);
1493}
1494void SoftFPU::PUNPCKHDQ_mm1_mm2m64(const X86::Instruction& insn)
1495{
1496 MMX_intrinsic(punpckhdq, v32, v32);
1497}
1498
1499void SoftFPU::PUNPCKLBW_mm1_mm2m32(const X86::Instruction& insn)
1500{
1501 MMX_intrinsic(punpcklbw, v8, v8);
1502}
1503void SoftFPU::PUNPCKLWD_mm1_mm2m32(const X86::Instruction& insn)
1504{
1505 MMX_intrinsic(punpcklwd, v16, v16);
1506}
1507void SoftFPU::PUNPCKLDQ_mm1_mm2m32(const X86::Instruction& insn)
1508{
1509 MMX_intrinsic(punpckldq, v32, v32);
1510}
1511
1512// LOGICAL
1513void SoftFPU::PAND_mm1_mm2m64(const X86::Instruction& insn)
1514{
1515 LOAD_MM_MM64M();
1516
1517 mm.raw &= mm64m.raw;
1518
1519 mmx_set(insn.modrm().reg(), mm);
1520 mmx_common();
1521}
1522void SoftFPU::PANDN_mm1_mm2m64(const X86::Instruction& insn)
1523{
1524 LOAD_MM_MM64M();
1525
1526 mm.raw &= ~mm64m.raw;
1527
1528 mmx_set(insn.modrm().reg(), mm);
1529 mmx_common();
1530}
1531void SoftFPU::POR_mm1_mm2m64(const X86::Instruction& insn)
1532{
1533 LOAD_MM_MM64M();
1534
1535 mm.raw |= mm64m.raw;
1536
1537 mmx_set(insn.modrm().reg(), mm);
1538 mmx_common();
1539}
1540void SoftFPU::PXOR_mm1_mm2m64(const X86::Instruction& insn)
1541{
1542 LOAD_MM_MM64M();
1543
1544 mm.raw ^= mm64m.raw;
1545
1546 mmx_set(insn.modrm().reg(), mm);
1547 mmx_common();
1548}
1549
1550// SHIFT
1551void SoftFPU::PSLLW_mm1_mm2m64(const X86::Instruction& insn)
1552{
1553 LOAD_MM_MM64M();
1554
1555 mm.v16 <<= mm64m.v16;
1556
1557 mmx_set(insn.modrm().reg(), mm);
1558 mmx_common();
1559}
1560void SoftFPU::PSLLW_mm1_imm8(const X86::Instruction& insn)
1561{
1562 VERIFY(!insn.has_operand_size_override_prefix()); // SSE2
1563 u8 imm = insn.imm8();
1564 MMX mm = mmx_get(insn.modrm().reg());
1565
1566 mm.v16 <<= imm;
1567
1568 mmx_set(insn.modrm().reg(), mm);
1569 mmx_common();
1570}
1571void SoftFPU::PSLLD_mm1_mm2m64(const X86::Instruction& insn)
1572{
1573 LOAD_MM_MM64M();
1574
1575 mm.v32 <<= mm64m.v32;
1576
1577 mmx_set(insn.modrm().reg(), mm);
1578 mmx_common();
1579}
1580void SoftFPU::PSLLD_mm1_imm8(const X86::Instruction& insn)
1581{
1582 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1583 u8 imm = insn.imm8();
1584 MMX mm = mmx_get(insn.modrm().reg());
1585
1586 mm.v32 <<= imm;
1587
1588 mmx_set(insn.modrm().reg(), mm);
1589 mmx_common();
1590}
1591void SoftFPU::PSLLQ_mm1_mm2m64(const X86::Instruction& insn)
1592{
1593 LOAD_MM_MM64M();
1594
1595 mm.raw <<= mm64m.raw;
1596
1597 mmx_set(insn.modrm().reg(), mm);
1598 mmx_common();
1599}
1600void SoftFPU::PSLLQ_mm1_imm8(const X86::Instruction& insn)
1601{
1602 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1603 u8 imm = insn.imm8();
1604 MMX mm = mmx_get(insn.modrm().reg());
1605
1606 mm.raw <<= imm;
1607
1608 mmx_set(insn.modrm().reg(), mm);
1609 mmx_common();
1610}
1611void SoftFPU::PSRAW_mm1_mm2m64(const X86::Instruction& insn)
1612{
1613 LOAD_MM_MM64M();
1614
1615 mm.v16 >>= mm64m.v16;
1616
1617 mmx_set(insn.modrm().reg(), mm);
1618 mmx_common();
1619}
1620void SoftFPU::PSRAW_mm1_imm8(const X86::Instruction& insn)
1621{
1622 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1623 u8 imm = insn.imm8();
1624 MMX mm = mmx_get(insn.modrm().reg());
1625
1626 mm.v16 >>= imm;
1627
1628 mmx_set(insn.modrm().reg(), mm);
1629 mmx_common();
1630}
1631void SoftFPU::PSRAD_mm1_mm2m64(const X86::Instruction& insn)
1632{
1633 LOAD_MM_MM64M();
1634
1635 mm.v32 >>= mm64m.v32;
1636
1637 mmx_set(insn.modrm().reg(), mm);
1638 mmx_common();
1639}
1640void SoftFPU::PSRAD_mm1_imm8(const X86::Instruction& insn)
1641{
1642 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1643 u8 imm = insn.imm8();
1644 MMX mm = mmx_get(insn.modrm().reg());
1645
1646 mm.v32 >>= imm;
1647
1648 mmx_set(insn.modrm().reg(), mm);
1649 mmx_common();
1650}
1651void SoftFPU::PSRLW_mm1_mm2m64(const X86::Instruction& insn)
1652{
1653 LOAD_MM_MM64M();
1654
1655 mm.v16u >>= mm64m.v16u;
1656
1657 mmx_set(insn.modrm().reg(), mm);
1658 mmx_common();
1659}
1660void SoftFPU::PSRLW_mm1_imm8(const X86::Instruction& insn)
1661{
1662 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1663 u8 imm = insn.imm8();
1664 MMX mm = mmx_get(insn.modrm().reg());
1665
1666 mm.v16u >>= imm;
1667
1668 mmx_set(insn.modrm().reg(), mm);
1669 mmx_common();
1670}
1671void SoftFPU::PSRLD_mm1_mm2m64(const X86::Instruction& insn)
1672{
1673 LOAD_MM_MM64M();
1674
1675 mm.v32u >>= mm64m.v32u;
1676
1677 mmx_set(insn.modrm().reg(), mm);
1678 mmx_common();
1679}
1680void SoftFPU::PSRLD_mm1_imm8(const X86::Instruction& insn)
1681{
1682 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1683 u8 imm = insn.imm8();
1684 MMX mm = mmx_get(insn.modrm().reg());
1685
1686 mm.v32u >>= imm;
1687
1688 mmx_set(insn.modrm().reg(), mm);
1689 mmx_common();
1690}
1691void SoftFPU::PSRLQ_mm1_mm2m64(const X86::Instruction& insn)
1692{
1693 LOAD_MM_MM64M();
1694
1695 mm.raw >>= mm64m.raw;
1696
1697 mmx_set(insn.modrm().reg(), mm);
1698 mmx_common();
1699}
1700void SoftFPU::PSRLQ_mm1_imm8(const X86::Instruction& insn)
1701{
1702 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1703 u8 imm = insn.imm8();
1704 MMX mm = mmx_get(insn.modrm().reg());
1705
1706 mm.raw >>= imm;
1707
1708 mmx_set(insn.modrm().reg(), mm);
1709 mmx_common();
1710}
1711
1712// DATA TRANSFER
1713void SoftFPU::MOVD_mm1_rm32(const X86::Instruction& insn)
1714{
1715 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1716 u8 mmx_index = insn.modrm().reg();
1717 // FIXME:: Shadow Value
1718 // upper half is zeroed out
1719 mmx_set(mmx_index, { .raw = insn.modrm().read32(m_cpu, insn).value() });
1720 mmx_common();
1721};
1722void SoftFPU::MOVD_rm32_mm2(const X86::Instruction& insn)
1723{
1724 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1725 u8 mmx_index = insn.modrm().reg();
1726 // FIXME:: Shadow Value
1727 insn.modrm().write32(m_cpu, insn,
1728 shadow_wrap_as_initialized(static_cast<u32>(mmx_get(mmx_index).raw)));
1729 mmx_common();
1730};
1731
1732void SoftFPU::MOVQ_mm1_mm2m64(const X86::Instruction& insn)
1733{
1734 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1735 // FIXME: Shadow Value
1736 if (insn.modrm().mod() == 0b11) {
1737 // instruction
1738 mmx_set(insn.modrm().reg(),
1739 mmx_get(insn.modrm().rm()));
1740 } else {
1741 mmx_set(insn.modrm().reg(),
1742 { .raw = insn.modrm().read64(m_cpu, insn).value() });
1743 }
1744 mmx_common();
1745}
1746void SoftFPU::MOVQ_mm1m64_mm2(const X86::Instruction& insn)
1747{
1748 VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */
1749 if (insn.modrm().mod() == 0b11) {
1750 // instruction
1751 mmx_set(insn.modrm().rm(),
1752 mmx_get(insn.modrm().reg()));
1753 } else {
1754 // FIXME: Shadow Value
1755 insn.modrm().write64(m_cpu, insn,
1756 shadow_wrap_as_initialized(mmx_get(insn.modrm().reg()).raw));
1757 }
1758 mmx_common();
1759}
1760void SoftFPU::MOVQ_mm1_rm64(const X86::Instruction&) { TODO_INSN(); }; // long mode
1761void SoftFPU::MOVQ_rm64_mm2(const X86::Instruction&) { TODO_INSN(); }; // long mode
1762
1763// EMPTY MMX STATE
1764void SoftFPU::EMMS(const X86::Instruction&)
1765{
1766 // clear tagword
1767 m_fpu_tw = 0xFFFF;
1768}
1769
1770}