4141#include < cfenv>
4242#include < numbers>
4343
44+ #if defined(__clang_major__) && (__clang_major__ < 12)
45+ // can't guarantee floating point environment control works
46+ #else
47+ #pragma STDC FENV_ACCESS ON
48+ #endif
49+
4450// From FPSCR.RM: 00 = round to nearest, 01 = round to zero; 10/11 are reserved
4551static const uint8_t fpmode_source[4 ] = { uml::ROUND_ROUND , uml::ROUND_TRUNC , uml::ROUND_ROUND , uml::ROUND_ROUND };
52+ static const int feround_source[4 ] = { FE_TONEAREST , FE_TOWARDZERO , FE_TONEAREST , FE_TONEAREST };
53+
54+ namespace {
55+
56+ // For convenience, we run in the host's rounding mode most of the time and only switch during actual FPU ops.
57+ // That shields read/write handlers and the debugger hook from seeing unexpected rounding mode changes.
58+ class host_rounding_scope
59+ {
60+ public:
61+ host_rounding_scope (uint32_t fpscr, int hostmode) :
62+ m_hostmode (hostmode),
63+ m_changed (feround_source[fpscr & 3 ] != hostmode)
64+ {
65+ if (m_changed)
66+ {
67+ std::fesetround (feround_source[fpscr & 3 ]);
68+ }
69+ }
70+
71+ ~host_rounding_scope ()
72+ {
73+ if (m_changed)
74+ {
75+ std::fesetround (m_hostmode);
76+ }
77+ }
78+
79+ host_rounding_scope (const host_rounding_scope &) = delete ;
80+ host_rounding_scope &operator =(const host_rounding_scope &) = delete ;
81+
82+ private:
83+ const int m_hostmode;
84+ const bool m_changed;
85+ };
86+
87+ } // anonymous namespace
4688
4789
4890DEFINE_DEVICE_TYPE (SH3 , sh3_device, " sh3" , " Hitachi SH-3 (Unidentified)" )
@@ -1503,10 +1545,6 @@ inline void sh34_base_device::LDSMFPSCR(const uint16_t opcode)
15031545#endif
15041546 m_sh2_state->m_fpu_sz = (m_sh2_state->m_fpscr & SZ ) ? 1 : 0 ;
15051547 m_sh2_state->m_fpu_pr = (m_sh2_state->m_fpscr & PR ) ? 1 : 0 ;
1506- if (!m_isdrc)
1507- {
1508- sh4_set_host_rounding ();
1509- }
15101548}
15111549
15121550/* LDC.L @Rm+,DBR */
@@ -1563,8 +1601,6 @@ inline void sh34_base_device::LDSFPSCR(const uint16_t opcode)
15631601#endif
15641602 m_sh2_state->m_fpu_sz = (m_sh2_state->m_fpscr & SZ ) ? 1 : 0 ;
15651603 m_sh2_state->m_fpu_pr = (m_sh2_state->m_fpscr & PR ) ? 1 : 0 ;
1566- if (!m_isdrc)
1567- sh4_set_host_rounding ();
15681604}
15691605
15701606/* LDC Rm,DBR */
@@ -2147,11 +2183,14 @@ inline void sh34_base_device::FLOAT(const uint16_t opcode)
21472183 if (n & 1 )
21482184 fatalerror (" SH-4: FLOAT opcode used with n %d" , n);
21492185
2186+ // int32 -> double is an exact conversion so the rounding mode doesn't matter
21502187 n = n & 14 ;
21512188 FP_RFD (n) = (double )*((int32_t *)&m_sh2_state->m_fpul );
21522189 }
21532190 else /* PR = 0 */
21542191 {
2192+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2193+
21552194 FP_RFS (n) = (float )*((int32_t *)&m_sh2_state->m_fpul );
21562195 }
21572196}
@@ -2251,6 +2290,8 @@ inline void sh34_base_device::FCNVDS(const uint16_t opcode)
22512290
22522291 if (m_sh2_state->m_fpu_pr ) /* PR = 1 */
22532292 {
2293+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2294+
22542295 n = n & 14 ;
22552296 if (m_sh2_state->m_fpscr & RM )
22562297 m_sh2_state->m_fr [n | NATIVE_ENDIAN_VALUE_LE_BE (0 , 1 )] &= 0xe0000000 ; /* round toward zero*/
@@ -2277,6 +2318,8 @@ inline void sh34_base_device::FADD(const uint16_t opcode)
22772318 uint32_t m = REG_M ;
22782319 uint32_t n = REG_N ;
22792320
2321+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2322+
22802323 if (m_sh2_state->m_fpu_pr ) /* PR = 1 */
22812324 {
22822325 n = n & 14 ;
@@ -2296,6 +2339,8 @@ inline void sh34_base_device::FSUB(const uint16_t opcode)
22962339 uint32_t m = REG_M ;
22972340 uint32_t n = REG_N ;
22982341
2342+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2343+
22992344 if (m_sh2_state->m_fpu_pr ) /* PR = 1 */
23002345 {
23012346 n = n & 14 ;
@@ -2316,6 +2361,8 @@ inline void sh34_base_device::FMUL(const uint16_t opcode)
23162361 uint32_t m = REG_M ;
23172362 uint32_t n = REG_N ;
23182363
2364+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2365+
23192366 if (m_sh2_state->m_fpu_pr ) /* PR = 1 */
23202367 {
23212368 n = n & 14 ;
@@ -2335,6 +2382,8 @@ inline void sh34_base_device::FDIV(const uint16_t opcode)
23352382 uint32_t m = REG_M ;
23362383 uint32_t n = REG_N ;
23372384
2385+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2386+
23382387 if (m_sh2_state->m_fpu_pr ) /* PR = 1 */
23392388 {
23402389 n = n & 14 ;
@@ -2353,6 +2402,8 @@ inline void sh34_base_device::FMAC(const uint16_t opcode)
23532402 uint32_t m = REG_M ;
23542403 uint32_t n = REG_N ;
23552404
2405+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2406+
23562407 if (m_sh2_state->m_fpu_pr == 0 ) /* PR = 0 */
23572408 {
23582409 const float p = FP_RFS (0 ) * FP_RFS (m);
@@ -2366,6 +2417,8 @@ inline void sh34_base_device::FSQRT(const uint16_t opcode)
23662417{
23672418 uint32_t n = REG_N ;
23682419
2420+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2421+
23692422 if (m_sh2_state->m_fpu_pr ) /* PR = 1 */
23702423 {
23712424 n = n & 14 ;
@@ -2382,10 +2435,13 @@ inline void sh34_base_device::FSRRA(const uint16_t opcode)
23822435{
23832436 uint32_t n = REG_N ;
23842437
2438+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2439+
23852440 FP_RFS (n) = 1 .0f / sqrtf (FP_RFS (n));
23862441}
23872442
23882443/* FSSCA FPUL,FRn PR=0 1111nnn011111101 */
2444+ // sinf/cosf are libm calls so we *don't* override the rounding mode here.
23892445void sh34_base_device::FSSCA (const uint16_t opcode)
23902446{
23912447 uint32_t n = REG_N ;
@@ -2402,6 +2458,8 @@ inline void sh34_base_device::FIPR(const uint16_t opcode)
24022458 uint32_t m = (n & 3 ) << 2 ;
24032459 n = n & 12 ;
24042460
2461+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2462+
24052463 float ml[4 ];
24062464 for (int a = 0 ; a < 4 ; a++)
24072465 ml[a] = FP_RFS (n + a) * FP_RFS (m + a);
@@ -2414,6 +2472,8 @@ void sh34_base_device::FTRV(const uint16_t opcode)
24142472 uint32_t n = REG_N ;
24152473 n = n & 12 ;
24162474
2475+ const host_rounding_scope round (m_sh2_state->m_fpscr , m_host_round);
2476+
24172477 float sum[4 ];
24182478 for (int i = 0 ; i < 4 ; i++)
24192479 {
@@ -3022,9 +3082,8 @@ void sh34_base_device::execute_run()
30223082 return ;
30233083 }
30243084
3025- // Save and restore the host's rounding mode since we're going to override it for the FPU
3026- const int hostround = std::fegetround ();
3027- sh4_set_host_rounding ();
3085+ // save the host's rounding mode so the RAII helper can restore it when necessary
3086+ m_host_round = std::fegetround ();
30283087
30293088 do
30303089 {
@@ -3053,8 +3112,6 @@ void sh34_base_device::execute_run()
30533112
30543113 m_sh2_state->icount --;
30553114 } while (m_sh2_state->icount > 0 );
3056-
3057- std::fesetround (hostround);
30583115}
30593116
30603117void sh3_base_device::device_start ()
@@ -3932,7 +3989,8 @@ void sh34_base_device::device_start()
39323989 // state_add(STATE_GENPCBASE, "CURPC", m_sh2_state->m_ppc).noshow();
39333990 state_add (STATE_GENPCBASE , " CURPC" , m_sh2_state->pc ).callimport ().noshow ();
39343991
3935- memcpy (m_fpmode, fpmode_source, sizeof (fpmode_source));
3992+ memcpy (m_sh2_state->m_fpmode , fpmode_source, sizeof (fpmode_source));
3993+ m_host_round = FE_TONEAREST ;
39363994
39373995 m_sh2_state->m_fzero = 0 .0f ;
39383996 m_sh2_state->m_fone = 1 .0f ;
@@ -3953,7 +4011,7 @@ void sh34_base_device::device_start()
39534011
39544012 drc_start ();
39554013
3956- m_drcuml->symbol_add (&m_fpmode, sizeof (m_fpmode), " fpmode" );
4014+ m_drcuml->symbol_add (&m_sh2_state-> m_fpmode , sizeof (m_sh2_state-> m_fpmode ), " fpmode" );
39574015}
39584016
39594017void sh34_base_device::state_import (const device_state_entry &entry)
@@ -4352,20 +4410,9 @@ void sh34_base_device::generate_update_cycles(drcuml_block &block, compiler_stat
43524410
43534411void sh34_base_device::generate_set_fmod (drcuml_block &block)
43544412{
4355- UML_AND (block, I0 , mem (&m_sh2_state->m_fpscr ), 3 ); // and i0,fpscr,3
4356- UML_LOAD (block, I0 , &m_fpmode[0 ], I0 , SIZE_BYTE , SCALE_x1); // load i0,fpmode,i0,byte
4357- UML_SETFMOD (block, I0 ); // setfmod i0
4358- }
4359-
4360- /* -------------------------------------------------
4361- sh4_set_host_rounding - point the host FPU at
4362- the mode FPSCR selects, for the interpreter
4363- -------------------------------------------------*/
4364-
4365- void sh34_base_device::sh4_set_host_rounding ()
4366- {
4367- static const int feround[4 ] = { FE_TONEAREST , FE_TOWARDZERO , FE_TONEAREST , FE_TONEAREST };
4368- std::fesetround (feround[m_sh2_state->m_fpscr & 3 ]);
4413+ UML_AND (block, I0 , mem (&m_sh2_state->m_fpscr ), 3 ); // and i0,fpscr,3
4414+ UML_LOAD (block, I0 , &m_sh2_state->m_fpmode [0 ], I0 , SIZE_BYTE , SCALE_x1); // load i0,fpmode,i0,byte
4415+ UML_SETFMOD (block, I0 ); // setfmod i0
43694416}
43704417
43714418/* -------------------------------------------------
@@ -5918,11 +5965,8 @@ bool sh34_base_device::generate_group_15_op1111_0x13_op1111_0xf13_FTRV(drcuml_bl
59185965 return true ;
59195966}
59205967
5921- // The backend restores the host default rounding mode around a C call, so FPSCR.RM has to
5922- // be re-applied here
59235968void sh34_base_device::func_FSSCA ()
59245969{
5925- sh4_set_host_rounding ();
59265970 FSSCA (m_sh2_state->arg0 );
59275971}
59285972static void cfunc_FSSCA (void *param) { ((sh34_base_device *)param)->func_FSSCA (); };
0 commit comments