Common 2600 File Format Proposal

Version 3

22nd September 1997

Contents


Introduction

This format has been proposed due to the multitude of different banking schemes for 2600 cartridges, along with the need to select an appropriate control device for each game. Using the .c26 format you will be able to load games without giving loads of command line switches, as you do with the current .bin system.

The extension .c26 is proposed for these files. For those on systems with upper and lower case filenames the c will be lower case, as is common with other file formats.


Philosophy

To avoid the format splitting into several competing ones, please do not alter the format without discussing it first. I'm not trying to be bossy, just to keep the common format truly common.

I want to keep the format as open as possible, and to be available for use in GPLed, Freeware, Shareware and Commercial 2600 emulators. Sample source code for reading and writting .c26 is available as part of Virtual 2600, and will hopefully help other emulator writers to implement the format.


Tags

Although the large number of tags defined may look daunting, the beauty of using them is that it is possible to implement a subset of them and yet still retain compatibilty with a wide range of cartridges. For example most 2 and 4k .bin's can be translated with just a DATA tag, but the format is flexible enough to allow the addition of tags for paddles, keypads, bank switching, saved games etc.

The format is tagged so as to be extensible and allow both forward and backward compatibility. It also means that information that is not needed or known does not have to be stored. e.g. If the cartridge image is not a saved game then I do not need the game state tags.

Each tag contains a tag type and the length of data in that section. If a tag is not recognised then it should be ignored. In C syntax a tag is defined as:

/* Tag structure */
struct c26_tag {
    int type;
    int len;
};

with the type being set to the value of an enum.

The tag type is positive for normal tags with a following data block of length len, or negative for tags for which the length integer is sufficient to store the relevant data. This ensures that any unknown tag and its data can be skipped if encountered.


Cross Platform Issues

As a somewhat biased choice integers are stored as 32bit signed words in the Intel/DEC Alpha least significant byte (LSB) first system.

Also for anyone on really wierd systems, or writing an emulator in Pascal, all strings are zero terminated .


Defined Tags

The tags are split up into sections for ease of reference. The number that each tag represents is shown in the enum statement in the following example code section.

Index of tag definitions

Audit tags

All files should include these tags at the start of the file. The version number in brackets is the version at which that tag was introduced.
C26MAGIC (v2)
A magic identifier for the file(1) command and others, The length field contains 0x2e633236 (".c26" in hex). This is not a code sequence according to d65. And so should not occur at the start of .BIN files. This sequence should be written and read as bytes to avoid any MSB/LSB problems changing the byte ordering.
VERSION (v1)
Gives file format version as an integer. Currently 1
WRITER (v1)
Name of the program that wrote the file. e.g "FooConv v6.9"

Cartridge Information tags

Useful for collectors.
CARTNAME (v1)
Name of cartridge. e.g. "Pitfall!"
CARTMAN (v1)
Manufacturer of cartridge. e.g. "Activision"
CARTAUTHOR (v1)
Name of programmer/programming team who wrote cartridge. e.g. "David Crane"
CARTSERIAL (v1)
Serial number of the cartridge. e.g. "AX-018"

Cartridge Operation tags

Which ever are necessary for the running of the game should be included. The very minimum is simply a DATA tag.
TVTYPE (v1)
integer, 0=NTSC 1=PAL, 3=SECAM.
CONTROLLERS (v1)
Left controller BYTE then Right controller BYTE.
0=Joystick, 1=Paddle, 2=Keypad.
BANKING (v1)
Bank switching scheme. Integer.
0=none
1=Atari 8k (F8)
2=Atari 16k (F6)
3=Parker Brothers 8k (E0)
4=CBS Ram Plus 12K (FA)
5=Atari 16k + Super Chip RAM (F6SC)
6=Robot Tank and Decathlon (FE). Cartridges are 8k in length. 7=Tigervision (3F). Cartridges are 8k in length. 8=M-Network (E7). Cartridges are 16k in length. 9=The `standard' method for implementing 32k cartridges (F4). (Only one cart is known to use this: Fatal Run.) 10=F8+SC. Like F8, but with Super Chip RAM. 11=F4+SC. Like F4, but with Super Chip RAM. (Fatal Run uses this.)
DATA
Cartridge ROM data.
DATACHECKSUM
Cart data check sum.
XSTART
integer, first pixel in scanline to display
WIDTH
integer, number of pixels per scanline to display
YSTART
integer, first scanline to display
HEIGHT
integer, number of scanlines to display

Game state tags

Used for saving games.
CPUREGS (v1)
Processor registers.
BYTE AC, XR, YR, SP, STATUS;
ADDRESS PC;
long clock; (for use by timer etc.)
GAMEREGS (v1)
TIA and PIA registers. Stored as:
BYTE TIAwrite[0x00..0x2c],
BYTE TIAread[0x00..0x0d],
BYTE PIAwrite[0x280..0x287],
BYTE PIAread[0x280..0x287].
BYTE electron beam vertical state:
0=VSYNC
1=VBLANK
2=DRAWABLE (Includes overscan area as this depends on window size).
Electron beam position:
INT X (range -68 to 160)
INT Y (range -38 0 to 2xx) 0=start of visible picture
PIARAM (v1)
The 128 bytes of RAM from the PIA.
CARTRAM (v1)
Cartridge RAM, if supported by the BANKING type

Optimisation tags (v2)

Tags that optimizing emulators can use to get better speed/accuracy. I am a bit concerned about these as it is a possible area of incompatibilty and inconvience for users. I am looking for proposals now, but an example of the type of tag to AVOID is: SKIPFRAME(frame skipping) If I have a DX2-66 and produce an image with SKIPFRAME set to 5 it would be rather annoying to those with a P-200.

So, only include tags for things that will not vary from one user's machine to another. e.g. Combat never requires mid line collision detection.

CPUCOMP
integer, required CPU compatibility
0=low
1=high

Example Code


/* The tag definitions */
enum TAGTYPE { VERSION=-1, WRITER=1, 
	       CARTNAME=2, CARTMAN=3, CARTAUTHOR=4, CARTSERIAL=5,
	       TVTYPE=-2, CONTROLLERS=-3, BANKING=-4, DATA=6,
	       CPUREGS=7, GAMEREGS=8, PIARAM=9, CARTRAM=10,
	       C26MAGIC=-5,	       
	       /* Added v3 */
	       DATACHECKSUM=-6, 
	       XSTART=-7, WIDTH=-8, YSTART=-9, HEIGHT=-10,
	       CPUCOMP=-11 };

/* Tag structure */
struct c26_tag {
    int type;
    int len;
};

/* Load from the common 2600 format */
/* Uses the tag structure from c26def.h */
/* Doesn't handle all tags yet. But the cool thing is that it doesn't matter!*/
int 
loadc26( FILE *fp, long flen)
{
    char databuf[65535];
    struct c26_tag tag;

    while(fread(&tag, sizeof(struct c26_tag), 1, fp))
    {	
	/* If positive then it has a data block */ 
	if(tag.type>=0)
	{
	    if(fread(databuf, sizeof(BYTE), tag.len, fp)) {
		perror("x2600 loadc26:");
		fprintf(stderr, "Error reading data.\n");
	    }
	}	    

	switch(tag.type) {

	case VERSION:
	    if(Verbose)
		printf("File version %d\n", tag.len);
	    break;

	case WRITER:
	    if(Verbose)
		printf("Written by: %s\n", databuf);
	    break;

	case TVTYPE:
	    base_opts.tvtype=tag.len;
	    break;

	case BANKING:
	    base_opts.bank=tag.len;
	    break;

	case DATA:
	    if(tag.len<=16384)
		memcpy(&theCart[0],databuf, tag.len);
	    else {
		fprintf(stderr, "Data larger that 16k!\n");
		exit(-1);
	    }
	    rom_size=tag.len;
	    if(tag.len==2048) {
		memcpy(&theCart[0],databuf, tag.len);
		memcpy(&theCart[2048],databuf, tag.len);
	    }
	    break;

	default:
	    fprintf(stderr, "Unknown tag %d\n. Ignoring.", tag.type);
	    break;
	}
    }
}


Comments to Alex Hornby