Memory Module for the Atari 2600 and 7800: Protocol |
Updated 2003-11-23 |
For consistancy across games, I recommend that the Memory Module be connected to the controller port that corresponds with the player whose data is to be accessed. If this doesn't apply (high score lists, two player simultaneous games, etc), then I recommend the left controller port be used for the Module. This is basicly the same rule that seems to be used by PlayStation games to decide where to find the right memory card. It also allows the player the option of using the Module's controller emulation.
Before initiating communication, the controller port will function normally as if only a regular controller is attached, or nothing is there. The process of initiating communication will not damage any of the standard controllers. The steps are:
The uses for the controller lines with the serial protocol:
Line 1 | Serial data |
Line 2 | Serial clock |
Line 3 | !slave busy |
Line 4 | !slave selected |
Line 5 | not used |
Line 6 | not used |
Line 9 | not used |
Communication is done using half duplex synchronous serial. 8 data bits are sent followed by a ninth bit for odd parity. Slave devices check and generate the parity value, so its use is not optional. Data is sent LSB and LSb (Byte & bit) first. Data is read on the falling edge of the clock and output on the rising edge. The clock must not exceed 1MHz.
The device ID sent in step 10 is sent using this method of communication. When a device recognizes its ID, it will bring the !slave selected line low. While it remains low, no other devices will respond, but they will wait for the !slave selected line to return to a high state before allowing step 1 to occur again. Once selected, communication continues in whatever way the device specifies.
In the case of the memory module, communication follows this pattern:
Whenever the slave selected line is low, a device claims to be in use. Other devices will not respond until the line returns to a high state. Send the deselect command (0xFF) to a device to make this happen. Within 32us of the !slave select line going high, devices will be able to respond to attempts to initiate communication.
Asynchronous serial is also supported for use with a computer. The data format is 8-N-1 (8 data bits, no parity, and 1 stop bit) at 19.2kbps with CTS flow control (RTS is not used). Because parity checking is not used, it is up to the computer software to detect and respond to errors. I would have left parity checking enabled, but I couldn't get the software to function properly and came across a bug in the serial support of the 2.4.19 Linux kernel. If these problems are resolved, I may reenable parity checking for asynchronous serial connections.
To summon the device:
Storage is divided into blocks, the atomic storage unit for this device. There are 64 blocks, each 128 bytes long. Blocks are allocated to files, which may be several blocks long and may be non-contiguous. Games provide a 15-bit value which functions as a file name and is used to find allocated blocks. A game can allocate multiple blocks and access each block through an index. The index starts with zero. Each increment of the index addresses the next allocated block in chronological order of allocation. Deallocating a block will cause the index of any following block(s) to decrease.
The directory information that details the usage of the serial EEPROM is stored on the EEPROM of the microcontroller. Each entry is 16-bits long. The upper bit is set if the corresponding block is not a head block (the first block for a game). The remaining 15 bits of a head block's entry is the game ID, which works like a file name. Other blocks are organized differently. In the lower byte, the first 7 bits store the absolute index of the next block in the file, and the MSb is set when this block is the end of the file. In the upper byte, the first 7 bits are the absolute index of the previous block in the file, and the MSb is always set because the block isn't a head block. This scheme requires a linear dierctory search to find the second block (bad), but allows a large game ID space (good) and allows files to grow until all the space is used (also good).
The memory module contains a 160 byte volitile RAM buffer that is accessible through the interface. All EEPROM data must go through the memory buffer. Data can only be read from the EEPROM into the buffer and only data in the buffer can be written to the EEPROM. Data in the buffer may be changed without EEPROM access. The data will be presevered between times when the module is summoned so long as power is maintained. As a result, the buffer can be used for temporary storage. The contents on power-up are undefined.
The memory module can send data from an attached PlayStation controller over an asynchronous serial connection. To use this feature, begin communication with the module as detailed above, then send 0x78 as a command code. No return code is sent. The module will continously send controller data until it is reset. Before the module can succeed in reading data from the controller, DTR must be dropped.
If no data is read from the controller, the module will send 0. If any data is read, the module will send 1 followed by 8 bytes. The first byte is the controller ID code. The third and fourth bytes are the button flags. The fifth through eighth bytes are the analog stick positions. This data is in the order the controller sends it and is documented in a few spots on the web. The module sends everything but the analog stick positions with the bits mirrored (bit 0 from the controller is sent as bit 7, bit 1 is sent as bit 6, etc).
Name | Result code | Meaning |
---|---|---|
CCOM_RES_OK | 0x00 | No error; everything is good and wonderful so far. |
CCOM_RES_PAST_EOF | 0xFE | Attempted to go past the end of the file. If writing, no more space is available. |
CCOM_RES_FAILURE | 0xFF | General error. If reading or writting to EEPROM, the parameters are out of bounds. |
|
||||||||||||||||
Command name |
Category Code: hexadecimal value |
|||||||||||||||
Data format: (present if there are any parameters to the command and/or any data returned by the command)
|
||||||||||||||||
A desciption of the command and how to use it. Any expressions will be given in C style. |
||||||||||||||||
|
||||||||||||||||
Deselect device / Terminate communications |
Protocol Code: 0xFF |
|||||||||||||||
Causes the device to stop driving the !slave select line low which will allow all conncted devices to respond to device selection and to a terminate communications command. WARNING: This command does send back a result code. If it is an error (which it cannot be with the memory module), then the device has some reason to remain selected. |
||||||||||||||||
|
||||||||||||||||
Get number of allocated blocks |
Storage management Code: 0x01 |
|||||||||||||||
Data format:
|
||||||||||||||||
Queries for the number of blocks (atomic allocation units) that are used on the module. |
||||||||||||||||
|
||||||||||||||||
Get number of free blocks |
Storage management Code: 0x02 |
|||||||||||||||
Data format:
|
||||||||||||||||
Queries for the number of blocks (atomic allocation units) that are not used on the module. |
||||||||||||||||
|
||||||||||||||||
Get number of game allocated blocks |
Storage management Code: 0x03 |
|||||||||||||||
Data format:
|
||||||||||||||||
Queries for the number of blocks (atomic allocation units) that are allocated to the game. Before this command will produce useful results, the game must first issue the Set Game ID command. A result of zero is valid and means that there are no blocks allocated to the currently set game ID. |
||||||||||||||||
|
||||||||||||||||
Allocate block |
Storage management Code: 0x04 |
|||||||||||||||
Allocates a block for the game. If the game ID has not yet been set, or if all blocks are used, then an error will result. The blocks allocated to a single game exist as a chain. Allocating a new block always adds to the end of the chain. |
||||||||||||||||
|
||||||||||||||||
Deallocate block |
Storage management Code: 0x05 |
|||||||||||||||
Data format:
|
||||||||||||||||
Deallocates the indicated block from the game's file. If the game ID has not yet been set, then an error will result. If too few blocks are availble, then an error will result and no blocks will be allocated. |
||||||||||||||||
|
||||||||||||||||
Set Game ID |
Storage management Code: 0x06 |
|||||||||||||||
Data format:
|
||||||||||||||||
Sets the game ID. This is used to find and access blocks allocated by a game. It is analagous to a file name. The ID is persistantly stored so long as power is maintained; if communications are terminated and later initiated again, the game ID will not need to be reset. The ID's will be given out by me because someone has to do it. I will probably assign them sequentially starting at 0x10, and leave the first 15 ID's unallocated for tesing and development use. |
||||||||||||||||
|
||||||||||||||||
Seek to memory offset |
Storage access Code: 0x07 |
|||||||||||||||
Data format:
|
||||||||||||||||
Seeks to a byte within the memory buffer. Read and write operations to the buffer will start on the indicated byte. |
||||||||||||||||
|
||||||||||||||||
Seek to block |
Storage access Code: 0x08 |
|||||||||||||||
Data format:
|
||||||||||||||||
Seeks to a block by its position in the game's chain of allocated blocks. Block 0 is the first block, block 1 is the second allocated block, etc . .. The block index does not have an array like relationship to the block's location in EEPROM. Read, write, and seek to offset I/O functions will occur on this block. The value is maintained in RAM so long as power is maintained. An attempt to seek to a non-existant block will result in an error, except when seeking to block 0. |
||||||||||||||||
|
||||||||||||||||
Seek to EEPROM offset |
Storage access Code: 0x09 |
|||||||||||||||
Data format:
|
||||||||||||||||
Seeks to a byte within the block. Read and write operations to EEPROM will start on the indicated byte. |
||||||||||||||||
|
||||||||||||||||
Read data from memory |
Storage access Code: 0x0A |
|||||||||||||||
Data format:
|
||||||||||||||||
Reads data from the memory buffer. An error will occur if the read would go past the end of the buffer. |
||||||||||||||||
|
||||||||||||||||
Read data from EEPROM |
Storage access Code: 0x0B |
|||||||||||||||
Data format:
|
||||||||||||||||
Reads data from the current block into the memory buffer. An error (CCOM_RES_PAST_EOF) will result if the read operation goes past the end of the file. If it does, it will still change the data in the buffer. If the request would go past the end of the buffer, no read will occur, the buffer will remain unchanged, and the error CCOM_RES_FAILURE will be returned. |
||||||||||||||||
|
||||||||||||||||
Write data to memory |
Storage access Code: 0x0C |
|||||||||||||||
Data format:
|
||||||||||||||||
Writes data to the memory buffer. An error will occur if the write would go past the end of the buffer. |
||||||||||||||||
|
||||||||||||||||
Write data to EEPROM |
Storage access Code: 0x0D |
|||||||||||||||
Data format:
|
||||||||||||||||
Reads data from the current block into the memory buffer. An error (CCOM_RES_PAST_EOF) will result if the read operation goes past the end of the file. If it does, it will still change the data in the buffer. If the request would go past the end of the buffer, no read will occur, the buffer will remain unchanged, and the error CCOM_RES_FAILURE will be returned. |
||||||||||||||||
|
||||||||||||||||
Seek to absolute block |
Debugging Code: 0x10 |
|||||||||||||||
Data format:
|
||||||||||||||||
Seeks to any block by its absolute index in the directory structure. Use with care. |
||||||||||||||||
|
||||||||||||||||
Get directory entry |
Debugging Code: 0x11 |
|||||||||||||||
Data format:
|
||||||||||||||||
Retrievs the raw directory entry for a given block. |
||||||||||||||||
|
||||||||||||||||
Set directory entry |
Debugging Code: 0x12 |
|||||||||||||||
Data format:
|
||||||||||||||||
Writes a raw directory entry to a given block. Use with great care, or not at all. If you don't have to use this command, then avoid it. |