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:

  1. Ground controller lines 1 through 4 (joystick directional lines).
  2. Wait at least 10ms. Notes:
  3. Change controller lines 3 and 4 back to inputs.
  4. Wait at least 1ms.
  5. Check the state of lines 3 and 4. If they are both grounded, a device using this protocol is attached.
  6. Bring the clock line, controller line 2, high. (This will cause lines 3 and 4 to return to a high state within 12us.)
  7. Wait at least 12us or until lines 3 and 4 are high.
  8. Bring the clock line low.
  9. Wait at least 24us.
  10. Send a device ID (0x10 for the memory module) to use over the synchonous serial lines.
  11. Wait at least 2ms.
  12. Check the !slave selected line (controller line 4).
All the delay times are estimated. Shorter delays may also work. Longer delays will not cause problems, except that each byte of data must be sent or received within 41.7ms from when the device is ready to send or receive data. Failure to move the byte (plus parity bit) within this time will cause the device to become deselected.

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
The controller pass through port has a 4016 switch that will disconnect lines 1-4 from any connected controller. This prevents a joystick from interfering with the communications.

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:

  1. A command is sent from console.
  2. Paramters for the command, if any, are sent.
  3. A result code is sent to the console. If it is an error, the device will expect another command (step 1) next.
  4. For write commands:
    1. The data to write is sent.
    2. A result code is sent to the console. If it is an error, the device will expect another command (step 1) next.
  5. Any return data is sent to the console.
Also, between data bytes (plus parity), the !slave busy line may be brought low. When this happens, the device is busy processing and the console should wait before sending or receiving more data. If a parity error occurs, the device will become deselected.

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:

  1. Signal the computer is ready by raising the DTR line.
  2. Send any character (optional) or send the device ID code (0x10).
  3. Recive a character. This should be the device ID code. It is possible that the data will be trash for the first attempt.
  4. If the character sent in step 2 does not match the response of step 3, go back to step 2, or stop if the device ID is for an unsupported device.
Once the device is summoned, the data exchange is the same as with the synchronous serial interface to the console, except that the data is sent differently and there is no need to deselect 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.

PlayStation controller interface

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).


The result codes
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.

The commands

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)
Bytes Origin Description
# bytes in this field Console or [controller] Port
The listed device drives the serial data lines
A comment about what this data is used for.

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:
Bytes Origin Description
Result code
1 Port Number of allocated blocks

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:
Bytes Origin Description
Result code
1 Port Number of free blocks

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:
Bytes Origin Description
Result code
1 Port Number of blocks allocated to the game

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:
Bytes Origin Description
1 Console Number of block in chain to deallocate. Zero indicates the first block.
Result code

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:
Bytes Origin Description
2 Console Unique 15-bit game ID. Maintain the MSB clear.
Result code

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:
Bytes Origin Description
1 Console Index of the byte that read and write operations will next work on. Must be less than 160.
Result code

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:
Bytes Origin Description
1 Console Index of the block to use
Result code

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:
Bytes Origin Description
1 Console Index of the byte that read and write operations will next work on. Must be less than 128.
Result code

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:
Bytes Origin Description
1 Console Number of bytes to read
Result code
variable Port Bytes read

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:
Bytes Origin Description
1 Console Number of bytes to read
Result code

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:
Bytes Origin Description
1 Console Number of bytes to write
Result code
variable Console Bytes to write
Result code

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:
Bytes Origin Description
1 Console Number of bytes to read
Result code

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:
Bytes Origin Description
1 Console Index of the block to use. 0 <= index <= 63
Result code

Seeks to any block by its absolute index in the directory structure. Use with care.


Get directory entry Debugging
Code: 0x11

Data format:
Bytes Origin Description
1 Console Index of the block for which directory information is to be retrieved. This index is absoulte and references a physical range on EEPROM. It is not the same index used to select one of several blocks allocated to the game. The value must satisfy this: 0 <= value <= 63.
Result code
2 Port The directory entry.

Retrievs the raw directory entry for a given block.


Set directory entry Debugging
Code: 0x12

Data format:
Bytes Origin Description
1 Console Index of the block for which directory information is to be retrieved. This index is absoulte and references a physical range on EEPROM. It is not the same index used to select one of several blocks allocated to the game. The value must satisfy this: 0 <= value <= 63.
2 Console The directory entry to be written. Whatever value is given will be written verbatim.
Result code

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.