Sam Coupé Snapshot (SCS) File Format

version 30.3.2012

This document describes the Sam Coupé snapshot (SCS) file format. This version of document applies to emulator ASCD version 0.98 WIP 3, and describes also known issues in that version.

The SCS file starts with a fixed-length header, which is followed by data blocks. Blocks can have various sizes and can be present in any order. Each data block has an ID and length information, so it can be easily identified and possibly skipped on load.

ASCD always saves snapshots on flyback (i.e. at the frame interrupt, even when the interrupts are disabled). It never saves the snapshots in any other point in emulation time. Other emulators are strongly encouraged to do it the same way. See more details below.

ASCD saves and loads all SCS files with Zlib, which makes them GZ-compressed on save and they are GZ-uncompressed on load. It is mandatory to compress SCS files this way, but technically ASCD can load any uncompressed SCS files as well. ZIP compression is not supported.

File header (15 bytes)

There is a file header at the start of the SCS file. Its main purpose is to identify the file format, and give some basic information about the file.

struct SCS_Header {
  char fileid[8];     //this is "SamSnap!" (8 bytes)
  char emulid[4];     //this is "ASCD" (4 bytes)
  byte version_major; //0
  byte version_minor; //98 (ASCD version 0.98)
  byte hardware_id;   //0=Sam Coupé, 1=ZX Spectrum, 2=ZX Spectrum 128

Note: Emulator ID and version is ignored on load. It is there just for information. The hardware_id byte is always zero for Sam Coupé snapshots, 1 and 2 are for ZX Spectrum emulation. It is generally not needed to use SCS format for ZX Spectrum snapshots, but ASCD needs it for QuickSave feature.

Data blocks - general information

The rest of the SCS file is composed of data blocks. Blocks can have various sizes and can be present in any order. Each data block has an ID and length information, so it can be easily identified and possibly skipped on load.

struct Block_Header {
  byte id;    //block id 1-255, 0=end of file
  dword size; //size of block (4 bytes unsigned. This header isn't included in the size.)

The smallest block size is zero. It means that no additional data follow after this block header. The blocks are allowed to have any size. If emulator wants to load more bytes than present in the file, the rest of internal buffer is filled with zeros. If there are more data in the file than emulator wants to load, the rest of the block is skipped.

Here is the list of existing block ID's:
enum BlockID {
  ID_Error = -1,
  //00-31 reserved for general use
  ID_EOF = 0,
  //32-63 resered for ASCD 
  ID_ASCD_Timings = 32,
  //64-95 reserved for SimCoupé
  //96-254 reserved for future use

Block ID 0 (zero) has a special meaning: It is used as EOF (end-of-file) marker. When loader reaches this byte, it ends.

Basic data blocks

These data blocks must be present in the file in order to let it work in praxis: CPU, Ports, Memory. This is obvious, you need CPU, ports, and memory to let the emulator work.

Block CPU (ID=1)

This block stores all Z80 CPU registers and some important timing values.
struct Z80_Registers {
  BYTE a, f, b, c, d, e, h, l;
  BYTE a1, f1, b1, c1, d1, e1, h1, l1;
  WORD ix, iy, pc, sp;
  BYTE i, r;
  BYTE im, iff; //bit0=IFF1, bit1=IFF2, bit3=!DelayedEI
  WORD LineCycleCounter;
  WORD IntActiveCounter;
  WORD lineno; //current PAL line number

im = interrupt mode 0/1/2. Value 3 is invalid, bits 2-7 are ignored on load and set to zero on save.
iff = bit0=IFF1, bit1=IFF2, bit3=!DelayedEI, bits 4-7 are ignored on load and set to zero on save.
ASCD always set bit 2 to zero, and it is ignored in ASCD 0.98 WIP 2 on load.
This is how this bit may be used in the future:
If set to 0, emulator will process interrupts no sooner than after the next instruction
If set to 1, emulator should process interrupts even before the first instruction
(This can be safely ignored in most cases, but can cause emulation incompatibilities in some very rare circumstances.)
LineCycleCounter = number of t-states elapsed since the start of the current PAL scanline. (This is always near zero in files saved by ASCD.)
IntActiveCounter = number of t-states elapsed since interrupt signal was activated
ASCD keeps interrupt signal active for 44T in ZX Spectrum mode and 128T in Sam Coupé mode.
MouseActiveCounter = number of t-states elapsed since mouse was activated by reading port 254
lineno = PAL line number. This is always zero in files saved by current versions of ASCD.

Block Ports (ID=3)

This block contains the contents of important I/O ports. Either Sam Coupé or ZX Spectrum ports are saved, based on hardware_id byte (see above).

Note that keyboard state isn't saved to the snapshot file. ASCD resets all keys to unpressed state on load.

Sam Coupé ports
struct SamSnapPorts {
  BYTE sampal[16];  //16 palette entries (bit 7 ignored)
  BYTE saareg;      //currently selected SAA register (last out to port 511), bits 5-7 are 0 on save and ignored on load
  BYTE saaregs[29]; //current values of SAA registers 0-28
  BYTE vmpr, hmpr, lmpr, border, line_int, hepr, lepr, lpen, attr, status_reg;

The vmpr, hmpr, lmpr, border, line_int, help, lepr, lpen, status_reg are last OUT values to those I/O registers.
vmpr bit 7 ignored (MIDI is not supported in ASCD)
border is the whole border port, not just border colour
lpen only bits 1 and 0 are valid (used by MIDI), other bits are zero on save and ignored on load
attr is usually computed in real time, this value is needed only when screen is turned off
status_reg is interrupt status register. Its saved value is always 0xF7 (frame interrupt) in ASCD. ASCD ignores all bits on load, except bits 0 and 3 which are used for line and frame interrupt signals (the only interrupt signals currently supported by ASCD.)

ZX Spectrum ports
struct ZXSSnapPorts {
  BYTE border;  //border colour 0-7
  BYTE zxpage;  //last OUT to ZXS128 paging port
  BYTE ayreg;   //bits 0-3: active AY register, bits 4-7: always zero and ignored on load 
  BYTE ayregs[16];

The border value contains only border colour (0-7) in ASCD. Other bits are currently set to zero. (This may change in future versions.)

Note that ASCD always saves whole structure including AY data even in 48KB emulation mode. It is because the AY chip is always emulated even in 48KB mode.

Block Memory (ID=3)

Note that this block is always the first block in the file.

This block contains the base memory of emulated machine. Its size is always 48 KB, 128 KB, 256 KB or 512 KB.

It is legal to save this block with shorter length, but ASCD always save the whole memory. The size of this block is also used on load to determine the exact size of the installed memory on Sam Coupé (256 or 512 KB).

Data blocks of hardware devices

These data blocks describe additional hardware devices connected to the emulated computer. The presence of each particular block defines that the particular device is connected.

Blocks ExternalMemA to ExternalMemD (ID=4/5/6/7)

These blocks are similar to the Memory block, except that they contain Sam Coupé external memory. They aren't used in ZX Spectrum modes at the moment. The length of each of these blocks is always 1 MB. Note that Master DOS requires external memory to be placed to the upper banks at first (i.e. 1 MB memory should always be in bank D, 2 MB memory should be in banks C and D, 3 MB memory should be in banks B, C, and D, and 4 MB memory uses all banks). ASCD stores the banks in ascending order, but any order is legal.

The presence of these blocks also defines that external memory is present. If these blocks are not present, external memory isn't connected.

Blocks Floppy1 and Floppy2 (ID=8/9)

These blocks contain data of Sam Coupé floppy drive controller for drive 1 and drive 2 respectively. It is not used in ZX Spectrum modes. The presence of these blocks in SCS file also defines the presence of particular floppy drive on Sam Coupé.

struct VL1772_Regs {
  BYTE Command;
  BYTE Status;
  BYTE Track;
  BYTE Sector;
  BYTE Data;
  BYTE stepdir;  //stepdir bit: 0=going towards track 79, 1=going towards track 0

ASCD currently supports standard Sam Coupé floppy drives only. (ATOM or other devices ain't supported.)

Block Mouse (ID=10)

This block describes state of Sam mouse. It is not used in ZX Spectrum modes.

struct MouseData {
  WORD ActiveCounter;
  BYTE ByteIndex;

If ByteIndex = 0, then mouse isn't strobed and ActiveCounter is ignored.
If ByteIndex > 0, then mouse is strobed, ActiveCounter = number of t-states since strobe and ByteIndex is index of next byte to be read from mouse port (e.g. ByteIndex = 2 when next byte will be mouse buttons).

ASCD Specific blocks

These blocks are specific to ASCD.

Block ASCD_Timings (ID=32)

This block ID is reserved to store advanced emulator timings in future versions of ASCD. Currently it is not used.

Block ASCD_OpenAir (ID=33)

This block stores the state information of OpenAir library. This block is saved only when input recording is active; its purpose is to allow rewinding of input recording stream to the point where this snapshot file was saved. It is used only for QuickSave/QuickLoad feature.

struct AirState {
  int elapsed_frames; //number of elapsed frames since last keypress/keyrelease (4 bytes signed)
  int filepos;        //position in file (4 bytes signed)

Known issues

There are two known bugs in ASCD 0.98 WIP 2. Note that it is a development version and won't be supported in the future.

The first one will unlikely cause any errors in real world usage. ASCD 0.98 WIP2 saves MouseStrobeTime instead of lineno in CPU structure. It means that files saved by it have bad lineno value (it should always be zero on flyback), but only if mouse is strobed just in time of frame interrupt (which is highly improbable). In case of load, ASCD 0.98 WIP2 ignores lineno stored in the SCS file and sets it to zero. This bug will be fixed in next ASCD release.

The second bug is simple: ASCD 0.98 WIP 2 omits to save end-of-file mark.

These two bugs are fixed in ASCD 0.98 WIP 3.

There is a known bug in ASCD 0.98 and all previous versions. The load of SCS file fails when a datablock is longer than expected. This bug is fixed in ASCD 1.00

Copyright & Usage

This text was written by © Aley Keprt 2012. All rights reserved.

You can freely use the information provided here to implement the support of SCS files in other software. You should contact the author when you want to add new blocks for your software. You are also encouraged to inform the author when new software with SCS support will be created.

kontakt Valid HTML 4.01!