7.10 Non-maskable Interrupts and PreNMI

When the console RESET switch is pushed, the hardware generates a HW2 interrupt to the R4300 CPU. The interrupt is serviced by the OS event handler which sends a message of type OS_EVENT_PRENMI to the message queue associated with that event.

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:

Note: There are some minor differences between power on reset and NMI reset. After power on reset, the caches are invalidated. After NMI reset, the caches are flushed and then invalidated. Also, the power on reset configures the RAM, while NMI reset leaves RAM alone.

After NMI reset, the contents of memory, except for the 1 MB that is copied in, are the same as before the NMI occurred. The global variable, osResetType, is set to 0 on a power up reset and to 1 on an NMI.

If your game does not use the scheduler (please see Section 22, "Scheduling Audio and Graphics", 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.

Exactly how a game should behave when it receives OS_EVENT_PRENMI is addressed in Nintendo's policies on game consistency (such as fading the screen to black or ramping the audio volume down), but from a technical standpoint, when the game receives the OS_EVENT_PRENMI message it should do the following:

To test this, you can generate an NMI on the development board by running the following program on the Indy. This is equivalent to pushing the RESET switch on the Nintendo 64 machine.

/*
 * 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;
}
UP