1 // Copyright 2008 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include <cmath>
6 #include <limits>
7 
8 #include "Common/CommonTypes.h"
9 #include "Common/FloatUtils.h"
10 #include "Core/PowerPC/Interpreter/Interpreter.h"
11 #include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h"
12 #include "Core/PowerPC/PowerPC.h"
13 
14 namespace
15 {
16 // Apply current rounding mode
17 enum class RoundingMode
18 {
19   Nearest = 0b00,
20   TowardsZero = 0b01,
21   TowardsPositiveInfinity = 0b10,
22   TowardsNegativeInfinity = 0b11
23 };
24 
SetFI(UReg_FPSCR * fpscr,int FI)25 static void SetFI(UReg_FPSCR* fpscr, int FI)
26 {
27   if (FI)
28   {
29     SetFPException(fpscr, FPSCR_XX);
30   }
31   fpscr->FI = FI;
32 }
33 
34 // Note that the convert to integer operation is defined
35 // in Appendix C.4.2 in PowerPC Microprocessor Family:
36 // The Programming Environments Manual for 32 and 64-bit Microprocessors
ConvertToInteger(UGeckoInstruction inst,RoundingMode rounding_mode)37 void ConvertToInteger(UGeckoInstruction inst, RoundingMode rounding_mode)
38 {
39   const double b = rPS(inst.FB).PS0AsDouble();
40   u32 value;
41   bool exception_occurred = false;
42 
43   if (std::isnan(b))
44   {
45     if (Common::IsSNAN(b))
46       SetFPException(&FPSCR, FPSCR_VXSNAN);
47 
48     value = 0x80000000;
49     SetFPException(&FPSCR, FPSCR_VXCVI);
50     exception_occurred = true;
51   }
52   else if (b > static_cast<double>(0x7fffffff))
53   {
54     // Positive large operand or +inf
55     value = 0x7fffffff;
56     SetFPException(&FPSCR, FPSCR_VXCVI);
57     exception_occurred = true;
58   }
59   else if (b < -static_cast<double>(0x80000000))
60   {
61     // Negative large operand or -inf
62     value = 0x80000000;
63     SetFPException(&FPSCR, FPSCR_VXCVI);
64     exception_occurred = true;
65   }
66   else
67   {
68     s32 i = 0;
69     switch (rounding_mode)
70     {
71     case RoundingMode::Nearest:
72     {
73       const double t = b + 0.5;
74       i = static_cast<s32>(t);
75 
76       if (t - i < 0 || (t - i == 0 && b > 0))
77       {
78         i--;
79       }
80       break;
81     }
82     case RoundingMode::TowardsZero:
83       i = static_cast<s32>(b);
84       break;
85     case RoundingMode::TowardsPositiveInfinity:
86       i = static_cast<s32>(b);
87       if (b - i > 0)
88       {
89         i++;
90       }
91       break;
92     case RoundingMode::TowardsNegativeInfinity:
93       i = static_cast<s32>(b);
94       if (b - i < 0)
95       {
96         i--;
97       }
98       break;
99     }
100     value = static_cast<u32>(i);
101     const double di = i;
102     if (di == b)
103     {
104       FPSCR.ClearFIFR();
105     }
106     else
107     {
108       // Also sets FPSCR[XX]
109       SetFI(&FPSCR, 1);
110       FPSCR.FR = fabs(di) > fabs(b);
111     }
112   }
113 
114   if (exception_occurred)
115   {
116     FPSCR.ClearFIFR();
117   }
118 
119   if (!exception_occurred || FPSCR.VE == 0)
120   {
121     // Based on HW tests
122     // FPRF is not affected
123     u64 result = 0xfff8000000000000ull | value;
124     if (value == 0 && std::signbit(b))
125       result |= 0x100000000ull;
126 
127     rPS(inst.FD).SetPS0(result);
128   }
129 
130   if (inst.Rc)
131     PowerPC::ppcState.UpdateCR1();
132 }
133 }  // Anonymous namespace
134 
Helper_FloatCompareOrdered(UGeckoInstruction inst,double fa,double fb)135 void Interpreter::Helper_FloatCompareOrdered(UGeckoInstruction inst, double fa, double fb)
136 {
137   FPCC compare_result;
138 
139   if (std::isnan(fa) || std::isnan(fb))
140   {
141     compare_result = FPCC::FU;
142     if (Common::IsSNAN(fa) || Common::IsSNAN(fb))
143     {
144       SetFPException(&FPSCR, FPSCR_VXSNAN);
145       if (FPSCR.VE == 0)
146       {
147         SetFPException(&FPSCR, FPSCR_VXVC);
148       }
149     }
150     else  // QNaN
151     {
152       SetFPException(&FPSCR, FPSCR_VXVC);
153     }
154   }
155   else if (fa < fb)
156   {
157     compare_result = FPCC::FL;
158   }
159   else if (fa > fb)
160   {
161     compare_result = FPCC::FG;
162   }
163   else  // Equals
164   {
165     compare_result = FPCC::FE;
166   }
167 
168   const u32 compare_value = static_cast<u32>(compare_result);
169 
170   // Clear and set the FPCC bits accordingly.
171   FPSCR.FPRF = (FPSCR.FPRF & ~0xF) | compare_value;
172 
173   PowerPC::ppcState.cr.SetField(inst.CRFD, compare_value);
174 }
175 
Helper_FloatCompareUnordered(UGeckoInstruction inst,double fa,double fb)176 void Interpreter::Helper_FloatCompareUnordered(UGeckoInstruction inst, double fa, double fb)
177 {
178   FPCC compare_result;
179 
180   if (std::isnan(fa) || std::isnan(fb))
181   {
182     compare_result = FPCC::FU;
183 
184     if (Common::IsSNAN(fa) || Common::IsSNAN(fb))
185     {
186       SetFPException(&FPSCR, FPSCR_VXSNAN);
187     }
188   }
189   else if (fa < fb)
190   {
191     compare_result = FPCC::FL;
192   }
193   else if (fa > fb)
194   {
195     compare_result = FPCC::FG;
196   }
197   else  // Equals
198   {
199     compare_result = FPCC::FE;
200   }
201 
202   const u32 compare_value = static_cast<u32>(compare_result);
203 
204   // Clear and set the FPCC bits accordingly.
205   FPSCR.FPRF = (FPSCR.FPRF & ~0xF) | compare_value;
206 
207   PowerPC::ppcState.cr.SetField(inst.CRFD, compare_value);
208 }
209 
fcmpo(UGeckoInstruction inst)210 void Interpreter::fcmpo(UGeckoInstruction inst)
211 {
212   const auto& a = rPS(inst.FA);
213   const auto& b = rPS(inst.FB);
214 
215   Helper_FloatCompareOrdered(inst, a.PS0AsDouble(), b.PS0AsDouble());
216 }
217 
fcmpu(UGeckoInstruction inst)218 void Interpreter::fcmpu(UGeckoInstruction inst)
219 {
220   const auto& a = rPS(inst.FA);
221   const auto& b = rPS(inst.FB);
222 
223   Helper_FloatCompareUnordered(inst, a.PS0AsDouble(), b.PS0AsDouble());
224 }
225 
fctiwx(UGeckoInstruction inst)226 void Interpreter::fctiwx(UGeckoInstruction inst)
227 {
228   ConvertToInteger(inst, static_cast<RoundingMode>(FPSCR.RN));
229 }
230 
fctiwzx(UGeckoInstruction inst)231 void Interpreter::fctiwzx(UGeckoInstruction inst)
232 {
233   ConvertToInteger(inst, RoundingMode::TowardsZero);
234 }
235 
fmrx(UGeckoInstruction inst)236 void Interpreter::fmrx(UGeckoInstruction inst)
237 {
238   rPS(inst.FD).SetPS0(rPS(inst.FB).PS0AsU64());
239 
240   // This is a binary instruction. Does not alter FPSCR
241   if (inst.Rc)
242     PowerPC::ppcState.UpdateCR1();
243 }
244 
fabsx(UGeckoInstruction inst)245 void Interpreter::fabsx(UGeckoInstruction inst)
246 {
247   rPS(inst.FD).SetPS0(fabs(rPS(inst.FB).PS0AsDouble()));
248 
249   // This is a binary instruction. Does not alter FPSCR
250   if (inst.Rc)
251     PowerPC::ppcState.UpdateCR1();
252 }
253 
fnabsx(UGeckoInstruction inst)254 void Interpreter::fnabsx(UGeckoInstruction inst)
255 {
256   rPS(inst.FD).SetPS0(rPS(inst.FB).PS0AsU64() | (UINT64_C(1) << 63));
257 
258   // This is a binary instruction. Does not alter FPSCR
259   if (inst.Rc)
260     PowerPC::ppcState.UpdateCR1();
261 }
262 
fnegx(UGeckoInstruction inst)263 void Interpreter::fnegx(UGeckoInstruction inst)
264 {
265   rPS(inst.FD).SetPS0(rPS(inst.FB).PS0AsU64() ^ (UINT64_C(1) << 63));
266 
267   // This is a binary instruction. Does not alter FPSCR
268   if (inst.Rc)
269     PowerPC::ppcState.UpdateCR1();
270 }
271 
fselx(UGeckoInstruction inst)272 void Interpreter::fselx(UGeckoInstruction inst)
273 {
274   const auto& a = rPS(inst.FA);
275   const auto& b = rPS(inst.FB);
276   const auto& c = rPS(inst.FC);
277 
278   rPS(inst.FD).SetPS0((a.PS0AsDouble() >= -0.0) ? c.PS0AsDouble() : b.PS0AsDouble());
279 
280   // This is a binary instruction. Does not alter FPSCR
281   if (inst.Rc)
282     PowerPC::ppcState.UpdateCR1();
283 }
284 
285 // !!! warning !!!
286 // PS1 must be set to the value of PS0 or DragonballZ will be f**ked up
287 // PS1 is said to be undefined
frspx(UGeckoInstruction inst)288 void Interpreter::frspx(UGeckoInstruction inst)  // round to single
289 {
290   const double b = rPS(inst.FB).PS0AsDouble();
291   const double rounded = ForceSingle(FPSCR, b);
292 
293   if (std::isnan(b))
294   {
295     const bool is_snan = Common::IsSNAN(b);
296 
297     if (is_snan)
298       SetFPException(&FPSCR, FPSCR_VXSNAN);
299 
300     if (!is_snan || FPSCR.VE == 0)
301     {
302       rPS(inst.FD).Fill(rounded);
303       PowerPC::UpdateFPRF(b);
304     }
305 
306     FPSCR.ClearFIFR();
307   }
308   else
309   {
310     SetFI(&FPSCR, b != rounded);
311     FPSCR.FR = fabs(rounded) > fabs(b);
312     PowerPC::UpdateFPRF(rounded);
313     rPS(inst.FD).Fill(rounded);
314   }
315 
316   if (inst.Rc)
317     PowerPC::ppcState.UpdateCR1();
318 }
319 
fmulx(UGeckoInstruction inst)320 void Interpreter::fmulx(UGeckoInstruction inst)
321 {
322   const auto& a = rPS(inst.FA);
323   const auto& c = rPS(inst.FC);
324 
325   const FPResult product = NI_mul(&FPSCR, a.PS0AsDouble(), c.PS0AsDouble());
326 
327   if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
328   {
329     const double result = ForceDouble(FPSCR, product.value);
330 
331     rPS(inst.FD).SetPS0(result);
332     FPSCR.FI = 0;  // are these flags important?
333     FPSCR.FR = 0;
334     PowerPC::UpdateFPRF(result);
335   }
336 
337   if (inst.Rc)
338     PowerPC::ppcState.UpdateCR1();
339 }
fmulsx(UGeckoInstruction inst)340 void Interpreter::fmulsx(UGeckoInstruction inst)
341 {
342   const auto& a = rPS(inst.FA);
343   const auto& c = rPS(inst.FC);
344 
345   const double c_value = Force25Bit(c.PS0AsDouble());
346   const FPResult d_value = NI_mul(&FPSCR, a.PS0AsDouble(), c_value);
347 
348   if (FPSCR.VE == 0 || d_value.HasNoInvalidExceptions())
349   {
350     const double result = ForceSingle(FPSCR, d_value.value);
351 
352     rPS(inst.FD).Fill(result);
353     FPSCR.FI = 0;
354     FPSCR.FR = 0;
355     PowerPC::UpdateFPRF(result);
356   }
357 
358   if (inst.Rc)
359     PowerPC::ppcState.UpdateCR1();
360 }
361 
fmaddx(UGeckoInstruction inst)362 void Interpreter::fmaddx(UGeckoInstruction inst)
363 {
364   const auto& a = rPS(inst.FA);
365   const auto& b = rPS(inst.FB);
366   const auto& c = rPS(inst.FC);
367   const FPResult product = NI_madd(&FPSCR, a.PS0AsDouble(), c.PS0AsDouble(), b.PS0AsDouble());
368 
369   if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
370   {
371     const double result = ForceDouble(FPSCR, product.value);
372     rPS(inst.FD).SetPS0(result);
373     PowerPC::UpdateFPRF(result);
374   }
375 
376   if (inst.Rc)
377     PowerPC::ppcState.UpdateCR1();
378 }
379 
fmaddsx(UGeckoInstruction inst)380 void Interpreter::fmaddsx(UGeckoInstruction inst)
381 {
382   const auto& a = rPS(inst.FA);
383   const auto& b = rPS(inst.FB);
384   const auto& c = rPS(inst.FC);
385 
386   const double c_value = Force25Bit(c.PS0AsDouble());
387   const FPResult d_value = NI_madd(&FPSCR, a.PS0AsDouble(), c_value, b.PS0AsDouble());
388 
389   if (FPSCR.VE == 0 || d_value.HasNoInvalidExceptions())
390   {
391     const double result = ForceSingle(FPSCR, d_value.value);
392 
393     rPS(inst.FD).Fill(result);
394     FPSCR.FI = d_value.value != result;
395     FPSCR.FR = 0;
396     PowerPC::UpdateFPRF(result);
397   }
398 
399   if (inst.Rc)
400     PowerPC::ppcState.UpdateCR1();
401 }
402 
faddx(UGeckoInstruction inst)403 void Interpreter::faddx(UGeckoInstruction inst)
404 {
405   const auto& a = rPS(inst.FA);
406   const auto& b = rPS(inst.FB);
407 
408   const FPResult sum = NI_add(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble());
409 
410   if (FPSCR.VE == 0 || sum.HasNoInvalidExceptions())
411   {
412     const double result = ForceDouble(FPSCR, sum.value);
413     rPS(inst.FD).SetPS0(result);
414     PowerPC::UpdateFPRF(result);
415   }
416 
417   if (inst.Rc)
418     PowerPC::ppcState.UpdateCR1();
419 }
faddsx(UGeckoInstruction inst)420 void Interpreter::faddsx(UGeckoInstruction inst)
421 {
422   const auto& a = rPS(inst.FA);
423   const auto& b = rPS(inst.FB);
424 
425   const FPResult sum = NI_add(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble());
426 
427   if (FPSCR.VE == 0 || sum.HasNoInvalidExceptions())
428   {
429     const double result = ForceSingle(FPSCR, sum.value);
430     rPS(inst.FD).Fill(result);
431     PowerPC::UpdateFPRF(result);
432   }
433 
434   if (inst.Rc)
435     PowerPC::ppcState.UpdateCR1();
436 }
437 
fdivx(UGeckoInstruction inst)438 void Interpreter::fdivx(UGeckoInstruction inst)
439 {
440   const auto& a = rPS(inst.FA);
441   const auto& b = rPS(inst.FB);
442 
443   const FPResult quotient = NI_div(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble());
444   const bool not_divide_by_zero = FPSCR.ZE == 0 || quotient.exception != FPSCR_ZX;
445   const bool not_invalid = FPSCR.VE == 0 || quotient.HasNoInvalidExceptions();
446 
447   if (not_divide_by_zero && not_invalid)
448   {
449     const double result = ForceDouble(FPSCR, quotient.value);
450     rPS(inst.FD).SetPS0(result);
451     PowerPC::UpdateFPRF(result);
452   }
453 
454   // FR,FI,OX,UX???
455   if (inst.Rc)
456     PowerPC::ppcState.UpdateCR1();
457 }
fdivsx(UGeckoInstruction inst)458 void Interpreter::fdivsx(UGeckoInstruction inst)
459 {
460   const auto& a = rPS(inst.FA);
461   const auto& b = rPS(inst.FB);
462 
463   const FPResult quotient = NI_div(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble());
464   const bool not_divide_by_zero = FPSCR.ZE == 0 || quotient.exception != FPSCR_ZX;
465   const bool not_invalid = FPSCR.VE == 0 || quotient.HasNoInvalidExceptions();
466 
467   if (not_divide_by_zero && not_invalid)
468   {
469     const double result = ForceSingle(FPSCR, quotient.value);
470     rPS(inst.FD).Fill(result);
471     PowerPC::UpdateFPRF(result);
472   }
473 
474   if (inst.Rc)
475     PowerPC::ppcState.UpdateCR1();
476 }
477 
478 // Single precision only.
fresx(UGeckoInstruction inst)479 void Interpreter::fresx(UGeckoInstruction inst)
480 {
481   const double b = rPS(inst.FB).PS0AsDouble();
482 
483   const auto compute_result = [inst](double value) {
484     const double result = Common::ApproximateReciprocal(value);
485     rPS(inst.FD).Fill(result);
486     PowerPC::UpdateFPRF(result);
487   };
488 
489   if (b == 0.0)
490   {
491     SetFPException(&FPSCR, FPSCR_ZX);
492     FPSCR.ClearFIFR();
493 
494     if (FPSCR.ZE == 0)
495       compute_result(b);
496   }
497   else if (Common::IsSNAN(b))
498   {
499     SetFPException(&FPSCR, FPSCR_VXSNAN);
500     FPSCR.ClearFIFR();
501 
502     if (FPSCR.VE == 0)
503       compute_result(b);
504   }
505   else
506   {
507     if (std::isnan(b) || std::isinf(b))
508       FPSCR.ClearFIFR();
509 
510     compute_result(b);
511   }
512 
513   if (inst.Rc)
514     PowerPC::ppcState.UpdateCR1();
515 }
516 
frsqrtex(UGeckoInstruction inst)517 void Interpreter::frsqrtex(UGeckoInstruction inst)
518 {
519   const double b = rPS(inst.FB).PS0AsDouble();
520 
521   const auto compute_result = [inst](double value) {
522     const double result = Common::ApproximateReciprocalSquareRoot(value);
523     rPS(inst.FD).SetPS0(result);
524     PowerPC::UpdateFPRF(result);
525   };
526 
527   if (b < 0.0)
528   {
529     SetFPException(&FPSCR, FPSCR_VXSQRT);
530     FPSCR.ClearFIFR();
531 
532     if (FPSCR.VE == 0)
533       compute_result(b);
534   }
535   else if (b == 0.0)
536   {
537     SetFPException(&FPSCR, FPSCR_ZX);
538     FPSCR.ClearFIFR();
539 
540     if (FPSCR.ZE == 0)
541       compute_result(b);
542   }
543   else if (Common::IsSNAN(b))
544   {
545     SetFPException(&FPSCR, FPSCR_VXSNAN);
546     FPSCR.ClearFIFR();
547 
548     if (FPSCR.VE == 0)
549       compute_result(b);
550   }
551   else
552   {
553     if (std::isnan(b) || std::isinf(b))
554       FPSCR.ClearFIFR();
555 
556     compute_result(b);
557   }
558 
559   if (inst.Rc)
560     PowerPC::ppcState.UpdateCR1();
561 }
562 
fmsubx(UGeckoInstruction inst)563 void Interpreter::fmsubx(UGeckoInstruction inst)
564 {
565   const auto& a = rPS(inst.FA);
566   const auto& b = rPS(inst.FB);
567   const auto& c = rPS(inst.FC);
568 
569   const FPResult product = NI_msub(&FPSCR, a.PS0AsDouble(), c.PS0AsDouble(), b.PS0AsDouble());
570 
571   if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
572   {
573     const double result = ForceDouble(FPSCR, product.value);
574     rPS(inst.FD).SetPS0(result);
575     PowerPC::UpdateFPRF(result);
576   }
577 
578   if (inst.Rc)
579     PowerPC::ppcState.UpdateCR1();
580 }
581 
fmsubsx(UGeckoInstruction inst)582 void Interpreter::fmsubsx(UGeckoInstruction inst)
583 {
584   const auto& a = rPS(inst.FA);
585   const auto& b = rPS(inst.FB);
586   const auto& c = rPS(inst.FC);
587 
588   const double c_value = Force25Bit(c.PS0AsDouble());
589   const FPResult product = NI_msub(&FPSCR, a.PS0AsDouble(), c_value, b.PS0AsDouble());
590 
591   if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
592   {
593     const double result = ForceSingle(FPSCR, product.value);
594     rPS(inst.FD).Fill(result);
595     PowerPC::UpdateFPRF(result);
596   }
597 
598   if (inst.Rc)
599     PowerPC::ppcState.UpdateCR1();
600 }
601 
fnmaddx(UGeckoInstruction inst)602 void Interpreter::fnmaddx(UGeckoInstruction inst)
603 {
604   const auto& a = rPS(inst.FA);
605   const auto& b = rPS(inst.FB);
606   const auto& c = rPS(inst.FC);
607 
608   const FPResult product = NI_madd(&FPSCR, a.PS0AsDouble(), c.PS0AsDouble(), b.PS0AsDouble());
609 
610   if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
611   {
612     const double tmp = ForceDouble(FPSCR, product.value);
613     const double result = std::isnan(tmp) ? tmp : -tmp;
614 
615     rPS(inst.FD).SetPS0(result);
616     PowerPC::UpdateFPRF(result);
617   }
618 
619   if (inst.Rc)
620     PowerPC::ppcState.UpdateCR1();
621 }
622 
fnmaddsx(UGeckoInstruction inst)623 void Interpreter::fnmaddsx(UGeckoInstruction inst)
624 {
625   const auto& a = rPS(inst.FA);
626   const auto& b = rPS(inst.FB);
627   const auto& c = rPS(inst.FC);
628 
629   const double c_value = Force25Bit(c.PS0AsDouble());
630   const FPResult product = NI_madd(&FPSCR, a.PS0AsDouble(), c_value, b.PS0AsDouble());
631 
632   if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
633   {
634     const double tmp = ForceSingle(FPSCR, product.value);
635     const double result = std::isnan(tmp) ? tmp : -tmp;
636 
637     rPS(inst.FD).Fill(result);
638     PowerPC::UpdateFPRF(result);
639   }
640 
641   if (inst.Rc)
642     PowerPC::ppcState.UpdateCR1();
643 }
644 
fnmsubx(UGeckoInstruction inst)645 void Interpreter::fnmsubx(UGeckoInstruction inst)
646 {
647   const auto& a = rPS(inst.FA);
648   const auto& b = rPS(inst.FB);
649   const auto& c = rPS(inst.FC);
650 
651   const FPResult product = NI_msub(&FPSCR, a.PS0AsDouble(), c.PS0AsDouble(), b.PS0AsDouble());
652 
653   if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
654   {
655     const double tmp = ForceDouble(FPSCR, product.value);
656     const double result = std::isnan(tmp) ? tmp : -tmp;
657 
658     rPS(inst.FD).SetPS0(result);
659     PowerPC::UpdateFPRF(result);
660   }
661 
662   if (inst.Rc)
663     PowerPC::ppcState.UpdateCR1();
664 }
665 
fnmsubsx(UGeckoInstruction inst)666 void Interpreter::fnmsubsx(UGeckoInstruction inst)
667 {
668   const auto& a = rPS(inst.FA);
669   const auto& b = rPS(inst.FB);
670   const auto& c = rPS(inst.FC);
671 
672   const double c_value = Force25Bit(c.PS0AsDouble());
673   const FPResult product = NI_msub(&FPSCR, a.PS0AsDouble(), c_value, b.PS0AsDouble());
674 
675   if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
676   {
677     const double tmp = ForceSingle(FPSCR, product.value);
678     const double result = std::isnan(tmp) ? tmp : -tmp;
679 
680     rPS(inst.FD).Fill(result);
681     PowerPC::UpdateFPRF(result);
682   }
683 
684   if (inst.Rc)
685     PowerPC::ppcState.UpdateCR1();
686 }
687 
fsubx(UGeckoInstruction inst)688 void Interpreter::fsubx(UGeckoInstruction inst)
689 {
690   const auto& a = rPS(inst.FA);
691   const auto& b = rPS(inst.FB);
692 
693   const FPResult difference = NI_sub(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble());
694 
695   if (FPSCR.VE == 0 || difference.HasNoInvalidExceptions())
696   {
697     const double result = ForceDouble(FPSCR, difference.value);
698     rPS(inst.FD).SetPS0(result);
699     PowerPC::UpdateFPRF(result);
700   }
701 
702   if (inst.Rc)
703     PowerPC::ppcState.UpdateCR1();
704 }
705 
fsubsx(UGeckoInstruction inst)706 void Interpreter::fsubsx(UGeckoInstruction inst)
707 {
708   const auto& a = rPS(inst.FA);
709   const auto& b = rPS(inst.FB);
710 
711   const FPResult difference = NI_sub(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble());
712 
713   if (FPSCR.VE == 0 || difference.HasNoInvalidExceptions())
714   {
715     const double result = ForceSingle(FPSCR, difference.value);
716     rPS(inst.FD).Fill(result);
717     PowerPC::UpdateFPRF(result);
718   }
719 
720   if (inst.Rc)
721     PowerPC::ppcState.UpdateCR1();
722 }
723