VR4300
The VR4300 is the CPU of the Nintendo 64, and is a NEC VR4300 with slight modifications. Running at 93.75 MHz, the VR4300 handles game logic, reading responding to controller input from the PIF, and preparing display lists and audio command lists for the RCP to draw and synthesize audio.
The VR4300 also contains an FPU that the VR4300 identifies as a co-processor (CP1) despite not being one. The FPU speeds up operations with 32-bit and 64-bit floating-point numbers. The VR4300 integrates another coprocessor called The System Control Coprocessor (CP0). The system control processor contains a Memory Management Unit (MMU) and a Translation Lookaside Buffer (TLB).
Naming History
In 1993, MIPS Technologies, Inc. (MTI), the same company that designed the MIPS III architecture, developed the R4200 microprocessor. Later in 1995, they made the R4300i, a derivative of the R4200. When MTI licensed it to NEC and Toshiba, those companies renamed the chips the VR4300 and TX4300 respectively. The microprocessor used in the N64's CPU, is a derivative of NEC's VR4300.
Over the years, these model names have been incorrectly identified. There is no such thing as a VR4300i, nor is there a NEC R4300.
Revision Identifiers
Warning: Not all board revisions have been tested. It is possible that some board revisions contain different revision identifiers than what is documented here so far.
The VR4300 has two revision identifier registers: the Coprocessor 0 PRId (Processor Revision Identifier) Register and the Coprocessor 1 FCR0 implementation/revision register.
PRId bits [15:8] is the processor id number and bits [7:0] is the revision number. The revision number is further split into major and minor revisions, in 4 bits each. All VR4300 units will report 0x0B (11) for the processor id number, however the revision number may vary: retail N64 units have so far been found to report either 0x10 (1.0, early units) or 0x22 (2.2, later units), and the iQue Player reports 0x40 (4.0).
FCR0 bits [15:8] is the implementation number and bits [7:0] is the revision number. All VR4300 units will report 0x0B (11) for the implementation number. Retail N64 units and the iQue Player have so far been found to report 0x00 for the revision number.
Modifications
The VR4300 used in the N64 has been modified compared to the original VR4300 chip. Currently the known differences include:
- Six pins are in different locations
Original | N64 | |
---|---|---|
/INT1 | 58 | 57 |
JTCK | 57 | 58 |
/EValid | 105 | 104 |
/Reset | 104 | 105 |
DivMode0 | 116 | 112 |
DivMode1 | 112 | 116 |
There may be other differences, more research is required.
Known Bugs
Multiplication Bug
Some VR4300 CPUs contain the “VR4300 multiplication bug”. This causes incorrect results to be generated, under certain circumstances, after computing a floating-point multiplication. The bug was fixed in later processor steppings, and affects early model Nintendo 64 consoles generally NUS-01 (Japan Only), NUS-02 (Japan Only), NUS-03 (First US Revision).
GCC accepts the -mfix4300
flag, which tells GCC to generate code with a workaround for this bug—two nop
instructions are inserted after every mul.s (fp)
, mul.d (fp)
, or mult (integer)
.
For example, consider this function:
float mul(float x, float y) { return x * y; }
Without the fix it may generate this code:
jal mul nop mul.s $f1,$f13,$f15 mul: jr $31 mul.s $f0,$f12,$f14
The mul.s after the nop (red) may produce unexpected results, if the operands in the mul.s after the jr (yellow) include NaN, Zero or Infinity.
With the fix it may generate this code:
mul.s $f1,$f13,$f15 nop jal mul nop nop mul: mul.s $f0,$f12,$f14 jr $31 nop
Depending on the other instructions that can be reordered the nops could be other instructions that perform work, so this is a worst case scenario. There may be a ROM which tests if your hardware is affected by this bug, but determining based on the Motherboard revision is easier.
32-bit Shift Right Arithmetic Bug
The sra
and srav
instructions do not work as the VR4300 processor manual describes. The processor manual claims that when an arithmetic right shift is performed the most significant bits of the lower 32 bits are filled with copies of bit 31, the 32-bit sign bit, and then bit 31 is sign-extended into the upper 32 bits of the register. In practice, the most significant bits are first filled with the bits from the upper 32 bits of the register, and then the new bit 31 is used for sign extension into the upper 32 bits of the register. This leaks 64-bit state that should always be inaccessible when executing this instruction, regardless of whether the processor is running in 32-bit or 64-bit mode. Since most 32-bit instructions will sign-extend into the upper 32 bits of the register, it is rare for this bug to cause visible effects if a program only uses 32-bit instructions.
For example, consider executing sra with inputs 0x0123456789ABCDEF and 16. If we follow the manual, we would get 0xFFFFFFFFFFFF89AB by shifting right by 16 and populating the upper 48 bits with the original sign bit which is 1. In C, this would be rd = (uint64_t)(int32_t)((int32_t)rt >> sa)
. On hardware, we instead get 0x00000000456789AB by shifting right by 16 and populating the upper 32 bits with the new sign bit which is now 0. In C, this would be rd = (uint64_t)(int32_t)((int64_t)rt >> sa)
.
This is considered a bug because it breaks the idea of ISA backwards compatibility. Earlier MIPS CPUs that were internally 32-bit would follow the calculation outlined in the processor manual simply because there is no 64-bit state to leak and the ALU was designed to do the correct 32-bit sign extension. In order to maintain compatibility with these earlier 32-bit only ISAs the same behavior would be expected regardless of whether the CPU has become 64-bit internally, however this is not the case hence it breaks strict compatibility.
It is not known if this bug was ever fixed, it is present in more consoles than the multiplication bug.
Sign extension bugs
32-bit signed integer multiplication (mult
) and division (div
) do not work as expected when the input registers are not properly sign-extended 32-bit values. The expected result would be for the processor to sign-extend both inputs from 32-bit to 64-bit, filling the upper 32 bits with a copy of bit 31, however this does not happen. mult
acts as a 64-bit by 35-bit signed multiplication, meaning the second operand is sign-extended on bit 34 before computing a 64-bit multiplication. div
usually acts as a 32-bit by 35-bit signed division (the dividend is sign-extended on bit 31 while the divisor is sign-extended on bit 34 before computing a 64-bit division) except when bits 63 and 31 of the divisor are not equal. When bits 63 and 31 of the divisor are not equal, the quotient output to the LO register is not correct. It is currently unclear how the outputs of this last case are arrived at. The remainder output to the HI register is at least related to the inputs and the quotient in the expected way: remainder = (int32_t)(dividend - quotient * divisor)
, where the operations are carried out as 64-bit.
It's not clear if this should really be called a "bug", it's more like undefined behavior, however the results are highly counter-intuitive and leaks 64-bit processor state into the 32-bit operating environment.
It is not known if this behavior is different on other processor revisions. These results are for processor revision 2.2 (see the Revision Identifiers section).