An EEPROM may be built into the N64 Game Pak and used to save data during a game. A circuit is added to the EEPROM so that it can communicate with the serial interface of the RCP.
Figure 26.6.1 Hardware Configuration
There are two types of EEPROM according to their capacities. One is the 4k-EEPROM. The capacity of this type is 4 kilobits (64 words * 64 bits = 512 bytes). The other is the 16k-EEPROM. The capacity of this type is 16 kilobits (256 words * 64 bits = 2048 bytes).
Until now, only the 4k-EEPROM has been available. Because of this the EEPROM functions in Nintendo 64 OS/Library version 2.0H and earlier are not compatible with the 16k-EEPROM. Use Nintendo 64 OS/Library version 2.0I and later when creating applications which use the 16k-EEPROM. A 16k-EEPROM patch is necessary when using the 16k-EEPROM with 2.0H and earlier versions.
The EEPROMs currently used are capable of 100,000 write cycles. This means that data can be properly written to the various addresses (0~63 for a 4k-EEPROM, 0~255 for a 16k-EEPROM) up to 100,000 times each, but beyond that the memory cells deteriorate and the possibility arises that the data will not be written accurately.
In addition, it is possible to store data for 10 years. However, beyond that period, unless they are rewritten, there are cases in which the memory cell's charge gradually is lost, and the data cannot be properly read.
As a countermeasure to this potential problem, it is recommended that the reliability of the data be preserved with additional data, such as check sum or parity bits.
When performing a write (when osEepromWrite is called), once the write command has been received by the EEPROM interface circuit, it takes approximately 15 milliseconds for the data to actually be written to the memory cell. If the next osEepromWrite is called during this time, wasted read status polling loops will be repeated inside the function until the data write from the previous command is completed.
When performing consecutive writes, allow 15 milliseconds or more between write commands.
The osEepromLongWrite() function solves this problem and performs continuous writing. osEepromLongWrite() uses the CPU timer to count 15 milliseconds after writing every 8 bytes of data. The timer interrupt is set to occur after 15 milliseconds, and the thread that called this function waits until the interrupt occurs.
Following are descriptions of the library functions used to handle the EEPROM. These functions include:
Function
osEepromProbe
Checks whether or not an EEPROM is built into the N64 Game Pak and determines the type of EEPROM
Syntax
#include <ultra64.h> s32 osEepromProbe(OSMesgQueue *mq);
Description
This function checks to see if EEPROM is correctly loaded in an N64 Game Pak. If EEPROM is correctly loaded, this function returns the type of EEPROM. The return values are:
0x00 ... non EEPROM is loaded
0x01 (EEPROM_TYPE_4K) ... SIZE=4K bit
0x02 (EEPROM_TYPE_16K) ... SIZE=16K bit
"mq" is the initialized message queue associated with OS_EVENT_SI events. For details, see the osSetEventMesg() function in the N64 Online Function Reference Manual. Since "mq" is used inside the function to wait for messages, the application does not need to use "mq" to wait for an end-of-function message.
You must always call osContInit() to initialize low-level synchronization before you call osEepromProbe().
Whenever an application uses EEPROM, you must use osEepromProbe to ensure that EEPROM is correctly loaded.
If you find that EEPROM is not correctly loaded, Nintendo recommends that the game be terminated for security reasons.
Function
osEepromRead, osEepromWrite
Read from and write to EEPROM
Syntax
#include <ultra64.h> s32 osEepromRead(OSMesgQueue *mq, u8 address, u8 *buffer); s32 osEepromWrite(OSMesgQueue *mq, u8 address, u8 *buffer);
Description
The osEepromRead() function issues a read command to the EEPROM and reads 8 bytes of data from an address specified by the address variable. The osEepromWrite() function issues a write command to the EEPROM and writes 8 bytes of data to an address specified by the address variable.
The size of "buffer", which is the buffer used for storing the data, must be large enough to hold 8 bytes of data. Note that address is an EEPROM block address. Because blocks are in units of 8 bytes, you must specify a value that is divisible by 8.
The returned value is 0 if the command is properly sent to the interface circuit. The returned value is -1 if the value of address is not within the proper range. If no EEPROM exists or if the interface circuit does not respond, then the returned value is 8 (CONT_NO_RESPONSE_ERROR).
"mq" is the initialized message queue associated with OS_EVENT_SI events. For details, see the osSetEventMesg() function in the N64 Online Function Reference Manual. Since "mq" is used inside the function to wait for messages, the application does not need to use "mq" to wait for an end-of-function message.
The osEepromRead and osEepromWrite functions can only read and write data of 8 bytes (1 block). To read and write more than 8 bytes, use the osEepromLongRead and osEepromLongWrite functions.
The EEPROM command takes about 15 milliseconds to finish. To avoid a reduction in overall system performance, you need to set up a timer between calls to osEepromRead and osEepromWrite.
Function
osEepromLongRead, osEepromLongWrite
Read from and write to multiple blocks in EEPROM
Syntax
#include <ultra64.h> s32 osEepromLongRead (OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes); s32 osEepromLongWrite(OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes);
Description
The osEepromLongRead() function issues a read command to the EEPROM and reads n bytes of data from an address specified by the address variable. Also, the osEepromLongWrite() function issues a write command and writes n bytes of data specified by the address variable.
The size of "buffer," which is the buffer used for storing the data, must be large enough to hold "n bytes" of data. Also, due to hardware restrictions, the transfer byte size (n bytes) must be a multiple of 8. Note that address is an EEPROM block address. Because blocks are in units of 8 bytes, you must specify a value that is divisible by 8.
"mq" is the initialized message queue associated with OS_EVENT_SI events. For details, see the osSetEventMesg() function in the N64 Online Function Reference Manual. Since "mq" is used inside the function to wait for messages, the application does not need to use "mq" to wait for an end-of-function message.
It can take as long as 15 milliseconds for the EEPROM write command to complete a data transfer. The osEepromWrite() function is called numerous times inside the osEepromLongWrite() function, and the CPU timer is used to wait 15 milliseconds every time data is written with osEepromWrite(). The returned values are the same as for osEepromWrite(). That is to say, the returned value is 0 if all write commands (the amount for "length" (=nbytes/8) number of times) have been properly sent to the interface circuit. The returned value is -1 if the value of address is not within the proper range. The returned value is 8 (CONT_NO_RESPONSE_ERROR) if no EEPROM exists or if the interface circuit does not respond.
The osEepromLongRead() function internally calls osEepromRead() the "length" number of times. The returned values are the same as for the osEepromRead() function. A 0 is returned if all data (the amount for "length" number of times) is properly read. A -1 is returned if the value of address is not within the proper range. An 8 is returned (CONT_NO_RESPONSE_ERROR) if no EEPROM exists or if the interface circuit does not respond.
Following is a sample program which illustrates proper usage of the EEPROM functions.
#include <ultra64.h> OSMesgQueue siMesgQueue; /* SI message queue */ OSMesg siMesgBuf[1]; /* SI message buffer */ static OSContStatus sdata[MAXCONTROLLERS]; /* Controller Port status */ u8 save_buffer[EEPROM_MAXBLOCKS * EEPROM_BLOCK_SIZE]; /* Buffer for writing 512 bytes of data */ u8 load_buffer[EEPROM_MAXBLOCKS * EEPROM_BLOCK_SIZE]; /* Buffer for reading 512 bytes of data */ int save_eeprom(OSMesgQueue *mq, u8 *buffer) /* Function for writing data to all 512 bytes of 4k EEPROM */ { int i,ret = 0; if( osEepromProbe(mq) != 1 ) /* Check whether communications with EEPROM are possible */ return -1; for(i = 0; i < EEPROM_MAXBLOCKS ; i++) { ret |= osEepromWrite(mq, i, buffer); /* Write data to EEPROM */ WAIT_15_M_SEC(); /* Wait 15msec */ } return ret; } int load_eeprom(OSMesgQueue *mq, u8 *buffer) /* Function for reading all 512 bytes from EEPROM */ { int i,ret = 0; if( osEepromProbe(mq) != 1 ) /* Check whether communications with 4k EEPROM are possible */ return -1; for(i = 0; i < EEPROM_MAXBLOCKS ; i++) ret |= osEepromRead(mq, i, buffer); /* Read the data in EEPROM */ return ret; } void main_proc(void) { static OSContPad rdata[MAXCONTROLLERS]; /* Controller data */ int ret = 0; u8 p; int i; osCreateMesgQueue(&siMesgQueue, siMesgBuf, 1); /* Allocate siMesgBuf as si message queue area */ osSetEventMesg(OS_EVENT_SI, &siMesgQueue, NULL); /* Set message in si message queue when an SI interrupt occurs */ osContInit(&siMesgQueue, &p, &sdata[0]); /* Check Controller connection status */ while(1){ if(p) { /* Controller exists */ osContStartReadData(&siMesgQueue); /* Start reading data read */ osRecvMesg(&siMesgQueue, NULL, OS_MESG_BLOCK); /* Wait for message from si */ if(p & 0x1) { /* Controller No.0 exists */ osContGetReadData(&rdata[0]); /* Copy data received from Controller to rdata */ if( rdata[0].button & CONT_A ) { /* Controller No.0 A button has been pressed */ ret = save_eeprom(&siMesgQueue, save_buffer); /* Save save_buffer data to EEPROM */ if( ret ) ERROR_PROC_1(); /* Error process */ } if( rdata[0].button & CONT_B ) { /* Controller No.0 B button has been pressed. */ ret = load_eeprom(&siMesgQueue, load_buffer); /* Load data from EEPROM to load_buffer */ if( ret ) ERROR_PROC_2(); /* Error process */ } } /* if(p & 0x1) */ } /* if(p) */ } /* while(1) */ }