1.2
Nintendo 64 Developers Newsletters will be published periodically, as needed. These feature software and hardware system anomalies, which have been discovered, and their solutions and/or work-arounds. Development tips will also be included.
The following back-to-back multiply code sequence in the processor pipeline has the potential of producing an incorrect result in the second multiply:
mul.[s,d] fd,fs,ft mul.[s,d] fd,fs,ft or [D]MULT[U] rs,rt
The error happens only when the first multiply is single- or double-precision floating-point operation and when one or both of its source operands are:
Signalling Not-a-Number (sNaN), 0 (Zero), or infinity (Inf).The second multiply instruction may produce an incorrect result depending on the operands of the 1st multiply and the operands of the and multiply. The second multiply can be a multiply of any data type: floating-point or integer, single- or double-precision, signed or unsigned integer.
This code sequence can occur in the pipeline in two ways:
When an instruction of any kind (e.g. NOP) is executed between the two multiply instructions, the problem will not occur.
Release 2.0C includes a patch (patchSG0001118) to the C compiler and Assembler which reorders multiply code to avoid this bug. You must use the compiler option described in the patch release notes, which are located in
patch1118/relnotes/patchSG0001118/ch1.zIf you use a different compiler or code in assembly language, you need to work around the problem as noted above.
Release 2.0C also includes a "checker" program called by makerom to ensure that programs are compiled with the proper compiler options to avoid this bug.
Affected Versions:
This problem happens on versions 1.x, 2.0 and 2.1 of the CPU.
For consecutive calls to osAiSetNextBuffer(void *vaddr, u32 nbytes)
if ( ( (vaddr1 + nbytes1) & 0x00003fff ) == 0x00002000 ) { X = vaddr2 + 0x00002000; // incorrect address } else { X = vaddr2; // correct address }
Depending on the data at vaddr2 + 0x2000, this may cause clicking in the audio.
To assure that the correct address is always read, you can either make sure that ((vaddr + nbytes) & 0x00003fff) != 0x00002000 by moving the buffers, or you can also simply subtract 0x2000 from vaddr2 when vaddr1 + nbytes1 evaluates to the error condition. The second solution will be implemented in the next release (2.0D).
Generally the mac parameter will always be set to 1000 so that objects AT
the far plane (which are about to dissappear from view, or which have just
popped into view) are completely fogged out (using such a setting will keep
objects from visually "popping" on and off near the far clipping plane).
The min parameter can be set anywhere from 0 to almost 1000. The problem
with the documentation is that it indicates that the number is linear with
the Z position where the fog will begin in Z. Actually this number is
linear with the SCREENSPACE Z but not with the object space Z. The
screenspace Z is an inverse function of the object space Z (Zscreen =
k/Zobject) and is "bunched up" at the far plane. Therefore a min value of
500 will actually begin the fog very close to the near plane. A min value
of 700 or more will push fog farther from the near plane so it does not
obscure so much geometry. The solution is to just use bigger numbers for
min. I find that values of 950 or higher sometimes give me the effect I
want.
-Acorn
For example, you can disassemble the section named "code" (as specified in the spec file) in the "chrome" example application executable as follows:
% gdis -S -t .code.text letters
Here is a portion of the output ...
[ 144] 0x80200050: 27 bd ff 90 addiu sp,sp,-112 [ 144] 0x80200054: af bf 00 1c sw ra,28(sp) 145: int i, *pr; 146: char *ap; 147: u32 *argp; 148: u32 argbuf[16]; 149: 150: /* notice that you can't call rmonPrintf() until you set 151: * up the rmon thread. 152: */ 153: 154: osInitialize(); [ 154] 0x80200058: 0c 08 04 c4 jal osInitialize [ 154] 0x8020005c: 00 00 00 00 nop 155: 156: argp = (u32 *)RAMROM_APP_WRITE_ADDR; [ 156] 0x80200060: 3c 0e 00 ff lui t6,0xff [ 156] 0x80200064: 35 ce b0 00 ori t6,t6,0xb000 [ 156] 0x80200068: af ae 00 60 sw t6,96(sp) 157: for (i=0; i<sizeof(argbuf)/4; i++, argp++) { [ 157] 0x8020006c: af a0 00 6c sw zero,108(sp) 158: osPiRawReadIo((u32)argp, &argbuf[i]); /* Assume no DMA */ [ 158] 0x80200070: 8f af 00 6c lw t7,108(sp) [ 158] 0x80200074: 8f a4 00 60 lw a0,96(sp) [ 158] 0x80200078: 27 b9 00 20 addiu t9,sp,32 [ 158] 0x8020007c: 00 0f c0 80 sll t8,t7,2 [ 158] 0x80200080: 0c 08 05 4c jal osPiRawReadIo [ 158] 0x80200084: 03 19 28 21 addu a1,t8,t9 [ 157] 0x80200088: 8f a8 00 6c lw t0,108(sp) [ 157] 0x8020008c: 8f aa 00 60 lw t2,96(sp) [ 157] 0x80200090: 25 09 00 01 addiu t1,t0,1 [ 157] 0x80200094: 2d 21 00 10 sltiu at,t1,16 [ 157] 0x80200098: 25 4b 00 04 addiu t3,t2,4 [ 157] 0x8020009c: af ab 00 60 sw t3,96(sp) [ 157] 0x802000a0: 14 20 ff f3 bne at,zero,0x80200070 [ 157] 0x802000a4: af a9 00 6c sw t1,108(sp) 159: } ...
Notice that the C source is interleaved with the disassembled code, and that the PC is given in the second column.
When your program crashes, you can look up the error PC listed in the crash dump (it is identified as "epc") to determine where the program crashed and find the corresponding line in the source/disassembly listing.
Note: The usage of "gdis" is different from the older "newdis".
The HW2 interrupt will be followed in 0.5 seconds by a non-maskable interrupt (NMI) to the R4300 CPU (unless the RESET switch is pushed and held for more than 0.5 seconds, in which case the NMI will occur when the switch is released).
After the NMI occurs, the hardware is reinitialized, and
If your game does not use the scheduler, it should set up to respond to the OS_EVENT_PRENMI event by associating a message queue with the event early in the game code. This is accomplished as follows:
osSetEventMesg(OS_EVENT_PRENMI, <some_message_queue>)
If your game does use the scheduler, it needs only to test for a message of type PRE_NMI_MSG on its client message queue. The scheduler performs the event initialization, and forwards the OS_EVENT_PRENMI message to the client message queue as soon as it is received.
When the game receives the OS_EVENT_PRENMI message it should do the following:
/* * Program to simulate pressing and releasing the RESET switch on the * Ultra 64. * * Copy this code to resetu64.c and type "make resetu64" * */ #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <sys/mman.h> #include <sys/u64gio.h> #include <PR/R4300.h> #define GIOBUS_BASE 0x1f400000 #define GIOBUS_SIZE 0x200000 /* 2 MB */ main() { int mmemFd; unsigned char *mapbase; struct u64_board *pBoard; if ((mmemFd = open("/dev/mmem", 2)) < 0) { perror("open of /dev/mmem failed"); return(1); } if ((mapbase = (unsigned char *)mmap(0, GIOBUS_SIZE, PROT_READ|PROT_WRITE, (MAP_PRIVATE), mmemFd, PHYS_TO_K1(GIOBUS_BASE))) == (unsigned char *)-1) { perror("mmap"); return(1); } pBoard = (struct u64_board *)(mapbase); pBoard->reset_control = _U64_RESET_CONTROL_NMI; sginap(10); pBoard->reset_control = 0; }