Improved WiiVC check to avoid false positives & added functions to read and manipulate fcr31 (#669)

* Added functions to access and manipulate the fcr31 register

* Improved WiiVC emulator check to avoid false positives

* Updated comments

* Moved float.h to include directory
This commit is contained in:
Matt Pharoah
2023-09-22 13:05:53 -04:00
committed by GitHub
parent 42cbaed631
commit 3a312e101c
4 changed files with 137 additions and 4 deletions

69
asm/fcr31.s Normal file
View File

@@ -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

58
include/float.h Normal file
View File

@@ -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

View File

@@ -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*);

View File

@@ -8,6 +8,7 @@
#include <ultra64.h>
#include <string.h>
#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