This is just used as a means of carting around routines. It's not meant
to directly have functionality embedded within it--this is the job of
the inheriting data structure--so we can just make this a basic struct.
Particularly given all the data members were public to begin with.
Another bit of behavior that we weren't performing correctly is the
unsetting of FPSCR.FI and FPSCR.FR when FPSCR.ZX is supposed to be set.
This is supported in PEM's section 3.3.6.1 where the following is
stated:
"
When a zero divide condition occurs, the following actions are taken:
- Zero divide exception condition bit is set FPSCR[ZX] = 1.
- FPSCR[FR, FI] are cleared.
"
And so, this fixes that behavior.
FPSCR[ZX] is the bit defined to represent the zero divide exception
condition bit, and is defined as (according to PowerPC Microprocessor
Family: The Programming Environments Manual for 32 and 64-bit
Microprocessors, which will be referred to as "PEM" for the rest of this
commit message) at section 3.3.6.1:
"
A zero divide exception condition occurs when a divide instructions is
executed with a zero divisor value and a finite, nonzero dividend value
or when a floating reciprocal estimate single (fres) or a floating
reciprocal square root estimate (frsqrte) instruction is executed with a
zero operand value.
"
Note that it states the divisor must be zero and the dividend must be
nonzero in order for ZX to be set. This means that the interpreter was
performing the wrong behavior for the case where 0/0 (with any sign on
the zeros) is performed. We would incorrectly set the ZX bit when only
the VXZDZ bit should be set.
It's also worth pointing out that N/0 (where N is any finite nonzero
value) and 0/0 are not within the same exception class. N/0 is a zero
divide exception case, while 0/0 is considered an invalid operation
exception case, which is also indicated in the PEM section 3.3.6.1 as
well where it lists the criteria for invalid operation exceptions.
Therefore we should only be setting the VXZDZ bit in the 0/0 case, not
VXZDZ and ZX. This was also verified via hardware tests to ensure that
this behavior indeed holds.
If invalid operation exceptions are enabled and an invalid operation
occurs, then the destination value remains untouched. This fixes issues
that may arise when using these two instructions where the destination
gets steamrolled by an infinity or NaN value.
If a NaN of any type is passed as the operand to either of these
instructions, we shouldn't go down the regular code path, as we end up
potentially setting the wrong flags. For example, we wouldn't set the
FPSCR.VXCVI bit properly. We'd also set FPSCR.FI, when in actuality it
should be unset.
If an SNaN is passed as an operand, we also need to set the FPSCR.VXSNAN
bit as well.
The flag setting behavior for these can be found in Appendix C.4.2 in
PowerPC Microprocessor Family: The Programming Environments Manual for
32 and 64-bit Microprocessors.
fctiwz functions in the same manner as fctiw, with the difference being
that fctiwz always assumes the rounding mode being towards zero. Because
of this, we can implement fctiwz in terms of fctiw's code, but modify it
to accept a rounding mode, allowing us to preserve proper behavior for
both instructions.
We also move Helper_UpdateCR1 to a temporary home in
Interpreter_FPUtils.h for the time being. It would be more desirable to
move it to a new common header for all the helpers, so that even JITs
can use them if they so wish, however, this and the following changes
are intended to only touch the interpreter to keep changes minimal for
fixing instruction behavior.
JitCommon already duplicates the Helper_Mask function within
JitBase.cpp/.h, and the ARM JIT includes the Interpreter header in order
to call Helper_Carry. So a follow up is best suited here, as this
touches two other CPU backends.
If any operand is a signaling NaN, we need to signify this by setting
the VXSNAN bit.
Fixes NaN flag setting for fmsub, fmsubs, fnmsub, fnmsubs, ps_msub, and
ps_nmsub instructions.
If any operand is a signaling NaN, we need to signify this by setting
the VXSNAN bit.
Fixes NaN flag setting for fmadd, fmadds, fnmadd, fnmadds, ps_madd,
ps_nmadd, ps_madds0, and ps_madds1
If either operand is a signaling NaN, we need to signify this by setting
the VXSNAN bit.
This fixes NaN flag setting for fsub, fsubs, and ps_sub instructions.
If either operand is a signaling NaN, we need to signify that by setting
the VXSNAN bit.
This fixes NaN flag setting for fdiv, fdivs and ps_div instructions.
This might happen if someone moves settings between e.g. a PC and
an Android device, or if someone was using JITIL and updates Dolphin.
I also made the panic alert a bit more explanatory.
Makes all of the naming consistent with our code style, and makes
parameters match their header equivalents.
Essentially just a clean-up of things that weren't migrated over
already.
If either of the operands are signaling NaNs, then an invalid operation
exception needs to be indicated within the FPSCR.
This corrects SNaN flag setting for fmul, fmuls, ps_mul, ps_muls0, and
ps_muls1.
If the input is a signaling NaN, then we need to signal that via setting
the FPSCR.VXSNAN bit. We also shouldn't update the FPRF flags if
FPSCR.VE is set.
If the FPSCR.VE bit is set and an invalid operand is passed in, then the FPRF
shouldn't be updated. Similarly this is also the case when the FPSCR.ZE bit
is set and negative or positive zero is passed in as the operand.
If FPSCR.ZE is set and a divide by zero exception is signaled, then the
FPRF shouldn't be updated with a result. Similarly, if the input is an
SNaN and FPSCR.VE is set, then the FPRF shouldn't be updated.
The VX bit is intended to be a summary bit indicating the occurrence of
any kind of invalid operation. Therefore, whenever an invalid operation
exception is set, also set VX.
This corrects our CR flag setting for multiple instructions in certain
scenarios. This corrects flag setting cases in fadd, fadds, fctiw, fctiwz, fdiv,
frsp, frsqrte, fsub, and fsubs (and technically every floating-point
instruction that we make more accurate in the future with regards to
flag setting).
Previously, given cases such as 0x80000000 / 0xFFFFFFFF we'd incorrectly
set the destination register value to zero. If the dividend is negative,
then the destination should be set to -1 (0xFFFFFFFF), however if the
dividend is positive, then the destination should be set to 0.
Note that the 750CL documents state that:
"If an attempt is made to perform either of the divisions --
0x80000000 / -1 or <anything> / 0, then the contents of rD are
undefined, as are the contents of the LT, GT, and EQ bits of the CR0
field (if Rc = 1). In this case, if OE = 1 then OV is set."
So this is a particular behavior of the hardware itself.
Executing a supervisor-level instruction in user mode is supposed to
cause a program exception to occur.
The following supervisor instructions are present:
- dcbi
- mfmsr
- mfspr
- mfsr
- mfsrin
- mtmsr
- mtspr
- mtsr
- mtsrin
- rfi
- tlbie
- tlbsync
In 0337ca116abe9b9b9877e6071ad0697188198885 checks within mfspr and
mtspr were added. This change adds the trivial checks to the other
instructions.
Keeps signed values out of bit arithmetic (not that there's any issues
that could arise from it in these situations, but it does look more
consistent, and silences compiler warnings)
Keeps all of the interpreter-specific exception handling functions
together in a reusable way across translation units, similar to
FPUtils.h for reusable floating-point functions.
There's no reason to use int here as opposed to an unsigned value.
Video_AccessEFB() takes its arguments as u32 values, so we'd be doing
sign conversions for no reason here (along with causing avoidable
compiler warnings).
If a program executing in user mode tries to write to any SPRs other than
XER, LR, or CTR registers, then a program exception occurs. Similarly
this also applies for reading SPRs as well, however the upper and lower
timebase halves can also be read (but not written to).
If HID0.NOOPTI is set, then dcbt and dcbtst are no-oped globally. We
currently don't perform data cache emulation, but we put this in anyway
so this detail isn't forgotten about if data cache emulation is
introduced at some point in the future.
This function in both JITs is only ever called by passing the JIT's code
buffer into it. Given this is already accessible, since the functions
are part of the respective JIT class, we can just remove this parameter.
This also cleans up accesses with the new code buffer, as we don't need
to do janky looking dereference-then-index expressions.