diff --git a/asm/fcr31.s b/asm/fcr31.s new file mode 100644 index 000000000..21eaa7563 --- /dev/null +++ b/asm/fcr31.s @@ -0,0 +1,69 @@ +.include "macros.inc" +.section .text, "ax" +.set noreorder +.set noat + +glabel fcr_get_rounding_mode +cfc1 $v0, $31 +jr $ra +andi $v0, 0x3 + +glabel fcr_set_rounding_mode +cfc1 $t0, $31 +xor $at, $t0, $a0 +andi $at, $at, 0x3 +xor $at, $at, $t0 +jr $ra +ctc1 $at, $31 + +glabel fcr_get_enabled_exceptions +cfc1 $v0, $31 +srl $v0, $v0, 7 +jr $ra +andi $v0, $v0, 0x1f + +glabel fcr_set_enabled_exceptions +cfc1 $t0, $31 +sll $a0, $a0, 7 +xor $at, $t0, $a0 +andi $at, $at, 0xf80 +xor $at, $at, $t0 +jr $ra +ctc1 $at, $31 + +glabel fcr_enable_exceptions +cfc1 $at, $31 +andi $a0, $a0, 0x1f +sll $a0, $a0, 7 +or $at, $at, $a0 +jr $ra +ctc1 $at, $31 + +glabel fcr_disable_exceptions +cfc1 $at, $31 +andi $a0, $a0, 0x1f +sll $a0, $a0, 7 +or $at, $at, $a0 +subu $at, $at, $a0 +jr $ra +ctc1 $at, $31 + +glabel fcr_get_cause +cfc1 $v0, $31 +srl $v0, $v0, 12 +jr $ra +andi $v0, $v0, 0x3f + +glabel fcr_get_flags +cfc1 $v0, $31 +srl $v0, $v0, 2 +jr $ra +andi $v0, $v0, 0x1f + +glabel fcr_clear_flags +cfc1 $t0, $31 +xor $at, $t0, $0 +andi $at, $at, 0x7c +xor $at, $at, $t0 +jr $ra +ctc1 $at, $31 diff --git a/include/float.h b/include/float.h new file mode 100644 index 000000000..733dde909 --- /dev/null +++ b/include/float.h @@ -0,0 +1,58 @@ +#ifndef GAME_FLOAT_H +#define GAME_FLOAT_H + +#include "types.h" + +typedef enum { + FRC_RM_ROUND_TO_NEAREST = 0x0, + FRC_RM_ROUND_TO_ZERO = 0x1, + FRC_RM_ROUND_TO_POSITIVE_INFINITY = 0x2, + FRC_RM_ROUND_TO_MINUS_INFINITY = 0x3 +} FloatRoundingMode; + +typedef enum { + FRC_EXCEPTION_INEXACT_OPERATION = 0x1, + FRC_EXCEPTION_UNDERFLOW = 0x2, + FRC_EXCEPTION_OVERFLOW = 0x4, + FRC_EXCEPTION_DIVISION_BY_ZERO = 0x8, + FRC_EXCEPTION_INVALID_OPERATION = 0x10, + FRC_EXCEPTION_UNIMPLEMENTED_OPERATION = 0x20 // only for Cause bits +} FloatExceptionBits; + +extern FloatRoundingMode fcr_get_rounding_mode(void); +extern void fcr_set_rounding_mode(FloatRoundingMode); + +// Get the Enable bits in FCR31 +extern FloatExceptionBits fcr_get_enabled_exceptions(void); + +// Set the Enable bits in FCR31 to the given bitset +// NOTE: Ares is currently the only emulator to actually respect the Enable bits. +// Floating point exceptions will still not occur on other emulators. +extern void fcr_set_enabled_exceptions(FloatExceptionBits); + +// Set the Enable bits to 1 for the given exception(s). Do not change any other exceptions. +// NOTE: Ares is currently the only emulator to actually respect the Enable bits. +// Floating point exceptions will still not occur on other emulators. +extern void fcr_enable_exceptions(FloatExceptionBits); + +// Set the Enable bits to 0 for the given exception(s). Do not change any other exceptions. +extern void fcr_disable_exceptions(FloatExceptionBits); + +// Returns TRUE if all of the given exceptions are enabled +inline Bool32 fcr_check_exceptions_enabled(FloatExceptionBits exceptions) { + return (fcr_get_enabled_exceptions() & exceptions) == exceptions; +} + +// Gets the exception(s) raised by the most recently executed floating point instruction +extern FloatExceptionBits fcr_get_cause(void); + +// Gets the current exception flags. These flags are set when an exception would be raised, +// but the exception is not currently enabled. They are only cleared by calling fcr_clear_flags. +// NOTE: This currently will only work on console and Ares. Other emulators will always return 0 +extern FloatExceptionBits fcr_get_flags(void); + +// Clear the current exception flags +extern void fcr_clear_flags(void); + +#endif + diff --git a/sm64.ld b/sm64.ld index dc6a72a32..89a29f315 100755 --- a/sm64.ld +++ b/sm64.ld @@ -162,6 +162,7 @@ SECTIONS #endif KEEP(BUILD_DIR/asm/pj64_get_count_factor_asm.o(.text*)); KEEP(BUILD_DIR/asm/round.o(.text*)); + KEEP(BUILD_DIR/asm/fcr31.o(.text*)); BUILD_DIR/src/boot*.o(.text*); BUILD_DIR/src/hvqm*.o(.text*); diff --git a/src/game/emutest.c b/src/game/emutest.c index b0a3631a1..b6666662f 100644 --- a/src/game/emutest.c +++ b/src/game/emutest.c @@ -8,6 +8,7 @@ #include #include #include "emutest_vc.h" +#include "float.h" #include "types.h" extern OSMesgQueue gSIEventMesgQueue; @@ -90,18 +91,22 @@ void detect_emulator() { } /* - * This check forces RTZ bug on vc - * If console is N64/adequate Emu round-to-nearest (RTN) rounding mode is used - * If console is VC round-to-zero (RTZ) mode is used + * This check forces RZ bug on vc + * If console is N64/adequate Emu, the current rounding mode, which is initialized to round-to-nearest (RN), is used + * If console is VC round-to-zero (RZ) mode is always used * * The double value 0.9999999999999999 used is 0x3FEFFFFFFFFFFFFF in binary * Exponent=01111111110, Mantissa=1111111111111111111111111111111111111111111111111111 - * RTZ will output not 1.0f, RTN will output exactly 1.0f + * RZ will output not 1.0f, RN will output exactly 1.0f */ + const FloatRoundingMode roundingMode = fcr_get_rounding_mode(); + fcr_set_rounding_mode(FRC_RM_ROUND_TO_NEAREST); if (1.0f != round_double_to_float(0.9999999999999999)) { gEmulator = EMU_WIIVC; + fcr_set_rounding_mode(roundingMode); return; } + fcr_set_rounding_mode(roundingMode); // Perform a read from unmapped PIF ram. // On console and well behaved emulators, this echos back the lower half of