libspectrum 0.2.1
=================

libspectrum is a fairly simple library designed to make the handling
of various ZX Spectrum emulator-related file formats easy.  So far it
handles:

* Snapshots: .z80, .szx, .sna (all read/write), .zxs, .sp., .snp and
  +D snapshots (read only).
* Tape images: .tzx, .tap (read/write) and Warajevo .tap (read only).
* Input recordings: .rzx (read/write).
* Timex cartridges: .dck (read only).

There are also some subsidiary functions which may be generally useful
for Spectrum-related utilities.

General conventions
===================

Naming conventions:

*_alloc: give us a new object
*_free:  we're done with this object
*_read:  restore object from serialised form
*_write: serialise object

Calling conventions:

* In general, all output parameters (those which may be changed by
  the function) should be before all input parameters

Defined types
=============

libspectrum defines eight standard types which may be of use:

libspectrum_byte		An unsigned 8-bit integer
libspectrum_signed_byte		A signed 8-bit integer
libspectrum_word		An unsigned 16-bit integer
libspectrum_signed_word		A signed 16-bit integer
libspectrum_dword		An unsigned 32-bit integer
libspectrum_signed_dword	A signed 32-bit integer
libspectrum_qword		An unsigned 64-bit integer
libspectrum_signed_qword	A signed 64-bit integer

Initialisation etc
==================

libspectrum_error libspectrum_init( void )

This routine must be called before any other libspectrum routines,
other than `libspectrum_version' and `libspectrum_check_version' to
initialise the library. If it isn't called, undefined behaviour may
result.

const char *libspectrum_version( void )

This routine returns the version of libspectrum in use, in a "x.y.z.a"
format.

const char *libspectrum_gcrypt_version( void )

This routine returns the version of libgcrypt being used by
libspectrum, or NULL if an appropriate version of libgcrypt is not
available.

int libspectrum_check_version( const char *version )

This routine checks whether the version of libspectrum is at least
`version', which should be specified in an "x.y.z.a" format. It
returns non-zero if the libspectrum version in use is at least
`version' or zero if it is not.

Error handling
==============

All libspectrum functions signal errors in two ways: by returning an
non-zero error code of type `libspectrum_error' and by calling
`libspectrum_error_function'.  The `libspectrum_error' enum can take the
following values:

LIBSPECTRUM_ERROR_NONE		No error; guaranteed to have value 0
LIBSPECTRUM_ERROR_WARNING	A warning, rather than a real error
LIBSPECTRUM_ERROR_MEMORY	Out of memory
LIBSPECTRUM_ERROR_UNKNOWN	Data not recognised
LIBSPECTRUM_ERROR_CORRUPT	Invalid data
LIBSPECTRUM_ERROR_SIGNATURE	File does not have the right signature
LIBSPECTRUM_ERROR_SLT		Ugly kludge used to indicate that a .z80
				file contains .slt data
LIBSPECTRUM_ERROR_LOGIC		An internal logic error has occured;
				should never be seen

`libspectrum_error_function' is an object of type
`libspectrum_error_function_t':

libspectrum_error (*libspectrum_error_function_t)(
  libspectrum_error error, const char *format, va_list ap
)

On error, `libspectrum_error_function' will be called, with `error'
being one of the standard codes, and `format' and `ap' are the standard
arguments suitable for passing to one of the v*printf functions to
create a text message giving more details on the error. If
`libspectrum_error_function' is not set by the user code,
`libspectrum_default_error_function' will be used: this simply outputs
the message to stderr, prefixed with "libspectrum error: " and ending
with a newline.  (Additionally, it will call abort() if the error was of
type LIBSPECTRUM_ERROR_LOGIC, but that shouldn't happen...)

General functions
=================

Functions which don't relate directly to one file type or another.

Machine types and capabilities
------------------------------

In some places (notably in the `libspectrum_snap' structure),
libspectrum needs to identify the Spectrum variant in use. This is done
with the `libspectrum_machine' enum, which can take the following
values:

LIBSPECTRUM_MACHINE_16		16K Spectrum
LIBSPECTRUM_MACHINE_48		48K Spectrum
LIBSPECTRUM_MACHINE_TC2048	Timex TC2048
LIBSPECTRUM_MACHINE_TC2068	Timex TC2068
LIBSPECTRUM_MACHINE_128		(Original) 128K Spectrum
LIBSPECTRUM_MACHINE_PLUS2	Spectrum +2 (the grey one)
LIBSPECTRUM_MACHINE_PENT	Pentagon 128
LIBSPECTRUM_MACHINE_PLUS2A	Spectrum +2A (the black one)
LIBSPECTRUM_MACHINE_PLUS3	Spectrum +3

The `libspectrum_machine_name' function:

const char* libspectrum_machine_name( libspectrum_machine type )

will return a text string giving the name of the machine (or "unknown"
if it doesn't recognise the type). This string is statically allocated
and so should not be modified by user code in any way.

The `libspectrum_machine_capabilities' function:

int libspectrum_machine_capabilities( libspectrum_machine type )

will tell you which features (above and beyond those found on the base
48K machine) a given `libspectrum_machine' has. It returns a bitwise OR
of the following constants:

LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY
  This machine has 128K of memory accessible as on the 128K machine.

LIBSPECTRUM_MACHINE_CAPABILITY_AY
  This machine has an AY-3-8912 sound chip (as in the 128K and later
  machines).

LIBSPECTRUM_MACHINE_CAPABILITY_KEMPSTON_JOYSTICK
  This machine has built-in Kempston joystick ports as on the TC2048.

LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY
  This machine can change into all-RAM configurations as the +2A/+3.

LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_DISK
  This machine has a disk drive similar to that on the +3.

LIBSPECTRUM_MACHINE_CAPABILITY_SINCLAIR_JOYSTICK
  This machine has built-in Sinclair joystick ports as on the +2/+2A/+3.

LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_DOCK
  This machine has a `dock' (cartridge port) similar to that found on
  the TC2068.

LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY
  This machine has memory paging capabilities similar to that of the
  TC2048 and TC2068.

LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_VIDEO
  This machine has additional video modes as found on the TC2048 and
  TC2068.

LIBSPECTRUM_MACHINE_CAPABILITY_TRDOS_DISK
  This machine has a disk drive similar to a TR-DOS device

File identification
-------------------

The `libspectrum_identify_file_raw' function will, if given a file's
contents and optionally its filename, will make a `best guess' as to
what sort of file it is:

libspectrum_error
libspectrum_identify_file_raw( libspectrum_id_t *type, const char *filename,
			       const unsigned char *buffer, size_t length )

`filename' should be the name of the file to be identified, or NULL if
it is unknown. `buffer' and `length' are the contents and length of the
file respectively.

The type of file will be returned in the `type' parameter, and can take
the following values:

LIBSPECTRUM_ID_UNKNOWN		Couldn't identify this file

LIBSPECTRUM_ID_CARTRIDGE_DCK	A .dck Timex dock image

LIBSPECTRUM_ID_COMPRESSED_BZ2	A bzip2 compressed file
LIBSPECTRUM_ID_COMPRESSED_GZ	A gzip compressed file

LIBSPECTRUM_ID_DISK_DSK		A .dsk +3 disk file
LIBSPECTRUM_ID_DISK_SCL		A .scl TRDOS disk file
LIBSPECTRUM_ID_DISK_TRD		A .trd TRDOS disk file

LIBSPECTRUM_ID_RECORDING_RZX	A .rzx input recording

LIBSPECTRUM_ID_SNAPSHOT_PLUSD   A +D snapshot 
LIBSPECTRUM_ID_SNAPSHOT_SNA     A .sna snapshot 
LIBSPECTRUM_ID_SNAPSHOT_SNP     A .snp snapshot 
LIBSPECTRUM_ID_SNAPSHOT_SP      A .sp snapshot 
LIBSPECTRUM_ID_SNAPSHOT_SZX     A .szx snapshot (as used by Spectaculator)
LIBSPECTRUM_ID_SNAPSHOT_Z80	A .z80 snapshot
LIBSPECTRUM_ID_SNAPSHOT_ZXS	A .zxs snapshot (as used by zx32)

LIBSPECTRUM_ID_TAPE_TAP		A `normal' (Z80-style) .tap tape image
LIBSPECTRUM_ID_TAPE_TZX		A .tzx tape image
LIBSPECTRUM_ID_TAPE_WARAJEVO	A Warajevo-style .tap tape image

`libspectrum_identify_file_raw' looks for defined signatures in the
file as well as the extension of the filename and a couple of
heuristics in its attempts to identify the file. It's not perfect
(especially when given files with the wrong extension), but it should
work in most cases.

When dealing with compressed files, you are probably interested in the
data after it has been decompressed rather the original file. This can
be accomplished with the `libspectrum_identify_file' function:

libspectrum_error
libspectrum_identify_file( libspectrum_id_t *type, const char *filename,
			   const unsigned char *buffer, size_t length )

The parameters are the same as for `libspectrum_identify_file_raw'. 

What in many cases may be more useful than the specific type of the
file is whether the file is a snapshot, a tape image or whatever. This
can be done with the `libspectrum_identify_class' function:

libspectrum_error libspectrum_identify_class( libspectrum_class_t *class,
                                              libspectrum_id_t type )

which returns the type of file represented by the `type' parameter in
`*class'. The available values are:

LIBSPECTRUM_CLASS_UNKNOWN		An unknown file type

LIBSPECTRUM_CLASS_CARTRIDGE_TIMEX	A Timex dock cartridge
LIBSPECTRUM_CLASS_DISK_PLUS3		A +3 disk image
LIBSPECTRUM_CLASS_DISK_TRDOS		A TRDOS disk image
LIBSPECTRUM_CLASS_RECORDING		An input recording
LIBSPECTRUM_CLASS_SNAPSHOT		A snapshot
LIBSPECTRUM_CLASS_TAPE			A tape image

One final routine, `libspectrum_identify_file_with_class',

libspectrum_error
libspectrum_identify_file_with_class(
  libspectrum_id_t *type, libspectrum_class_t *libspectrum_class,
  const char *filename, const unsigned char *buffer, size_t length )

simply combines the calls to `libspectrum_identify_file' and
`libspectrum_identify_class', returning the file type in `*type' and
the file class in `*class'.

Machine timings
---------------

The `libspectrum_machine_timings_*' functions give information about
the speed and various timings constants used by the machines. Some of
these timings are almost certainly wrong; if you have any corrections,
please send them in. Each of the functions takes one of the
LIBSPECTRUM_MACHINE_* constants listed above and returns a timing for
that machine.

libspectrum_dword
libspectrum_timings_processor_speed( libspectrum_machine machine )
libspectrum_dword
libspectrum_timings_ay_speed( libspectrum_machine machine )

The speed in Hz of the main processor and of the AY clock.

libspectrum_word
libspectrum_timings_left_border( libspectrum_machine machine )
libspectrum_word
libspectrum_timings_horizontal_screen( libspectrum_machine machine )
libspectrum_word
libspectrum_timings_right_border( libspectrum_machine machine )
libspectrum_word
libspectrum_timings_horizontal_retrace( libspectrum_machine machine )

The length in tstates of the the different parts of one scanline.

libspectrum_word
libspectrum_timings_tstates_per_line( libspectrum_machine machine )

The sum of the previous four numbers.

libspectrum_word
libspectrum_timings_top_border( libspectrum_machine machine )
libspectrum_word
libspectrum_timings_vertical_screen( libspectrum_machine machine )
libspectrum_word
libspectrum_timings_bottom_border( libspectrum_machine machine )
libspectrum_word
libspectrum_timings_vertical_retrace( libspectrum_machine machine )

The number of scanlines in the different parts of the screen.

libspectrum_word
libspectrum_timings_lines_per_frame( libspectrum_machine machine )

The sum of the previous four numbers.

libspectrum_dword
libspectrum_timings_tstates_per_frame( libspectrum_machine machine )

tstates_per_line * lines_per_frame.

libspectrum_word
libspectrum_timings_top_left_pixel( libspectrum_machine machine )

How many tstates after interrupt is the top-left pixel of the screen
displayed.

Creator information
-------------------

Some formats (.szx snapshots and .rzx input recordings) allow
utilities to store some information regarding the creator of the file.
libspectrum provides a `libspectrum_creator' structure to store this
information.

libspectrum_error libspectrum_creator_alloc( libspectrum_creator **creator );

Allocate a new `libspectrum_creator' structure.

libspectrum_error libspectrum_creator_free( libspectrum_creator *creator );

Free the memory used by a `libspectrum_creator' structure.

libspectrum_error
libspectrum_creator_set_program( libspectrum_creator *creator,
				 const libspectrum_byte *program );
const libspectrum_byte*
libspectrum_creator_program( libspectrum_creator *creator );

Set and retrieve the name of the program which created this file.

libspectrum_error libspectrum_creator_set_major( libspectrum_creator *creator,
						 libspectrum_word major );
libspectrum_word
libspectrum_creator_major( libspectrum_creator *creator );

Set and retrieve the major version number of the program which created
this file.

libspectrum_error libspectrum_creator_set_minor( libspectrum_creator *creator,
						 libspectrum_word minor );
libspectrum_word
libspectrum_creator_minor( libspectrum_creator *creator );

Set and retrieve the minor version number of the program which created
this file.

libspectrum_error
libspectrum_creator_set_competition_code( libspectrum_creator *creator,
					  libspectrum_dword competition_code );
libspectrum_dword
libspectrum_creator_competition_code( libspectrum_creator *creator );

Set and retrieve the `competition code' of the program which created
this file. The competition code can be used for on-line tournaments to
determine that a certain file was made after a specific code was
released. If you don't understand the previous, you almost certainly
don't need to worry about it!

Snapshot functions
==================

Functions for dealing with snapshot files. These act on an opaque
`libspectrum_snap' structure, which can be accessed via the following
routines:

libspectrum_error libspectrum_snap_alloc( libspectrum_snap **snap )

Allocate a new libspectrum_snap structure.

libspectrum_error libspectrum_snap_free( libspectrum_snap *snap )

Release a structure allocated with `libspectrum_snap_alloc'.

There is a family of functions which can be used to retrieve and set
the properties of a snapshot. The `retrieve' functions have the form

<type> libspectrum_snap_<name>( libspectrum_snap *snap )

which retrieves the value of the property <name>, while the `set'
functions have the form

void libspectrum_snap_set_<name>( libspectrum_snap *snap, <type> new_value )

which sets the value of <name> to `new_value'. For array properties,
the retrieval function has the form

<type> libspectrum_snap_<name>( libspectrum_snap *snap, int idx )

which retrieves the <name>[`idx'] and the set function has the form

void libspectrum_snap_set_<name>( libspectrum_snap *snap,
				  int idx, <type> new_value )

which sets <name>[`idx'].

The available properties (along with their types) are:

* libspectrum_machine machine

* libspectrum_byte a
* libspectrum_byte f
* libspectrum_word bc
* libspectrum_word de
* libspectrum_word hl
* libspectrum_byte a_
* libspectrum_byte f_
* libspectrum_word bc_
* libspectrum_word de_
* libspectrum_word hl_
* libspectrum_word ix
* libspectrum_word iy
* libspectrum_byte i
* libspectrum_byte r
* libspectrum_word sp
* libspectrum_word pc
* libspectrum_byte iff1
* libspectrum_byte iff2
* libspectrum_byte im

* libspectrum_dword tstates

* int halted
* int last_instruction_ei

* libspectrum_byte out_ula

* libspectrum_byte out_128_memoryport
* libspectrum_byte out_plus3_memoryport

* libspectrum_byte out_ay_registerport
* libspectrum_byte ay_registers[16]

* libspectrum_byte out_scld_hsr
* libspectrum_byte out_scld_dec

* libspectrum_byte* pages[8]

* libspectrum_byte* slt[256]
* size_t slt_length[256]
* libspectrum_byte* slt_screen
* int slt_screen_level

Most of those should be fairly self-explanatory; the `a_', `f_',
`bc_', `de_' and `hl_' functions represent the A', F', BC', DE' and
HL' registers. For 48K snaps, 0x4000 to 0x7fff is stored in
`pages[5]', 0x8000 to 0xbfff in `pages[2]' and 0xc000 to 0xffff in
`pages[0]' (This is equivalent to the default mapping on the 128K
machines). `last_instruction_ei' being non-zero signals that the
opcode previously executed was an EI and thus interrupts should not be
accepted at this point, but will be after the next opcode.

With all those housekeeping routines out of the way, there are two
main workhorses of the snapshot routines:

libspectrum_error
libspectrum_snap_read( libspectrum_snap *snap, const libspectrum_byte *buffer,
                       size_t length, libspectrum_id_t type,
                       const char *filename )

Take the snapshot of type `type' of `length' bytes starting at
`buffer' and convert it to a `libspectrum_snap' structure. If `type'
is `LIBSPECTRUM_ID_UNKNOWN', guess the file format via
`libspectrum_identify_file'; `filename' is used only to help with the
identification process and can be set to NULL (or anything else) if
`type' is not `LIBSPECTRUM_ID_UNKNOWN'. Snapshots compressed with
bzip2 or gzip will be automatically and transparently decompressed.

libspectrum_error
libspectrum_snap_write( libspectrum_byte **buffer, size_t *length,
			int *out_flags, libspectrum_snap *snap,
	 		libspectrum_id_t type, libspectrum_creator *creator,
			int in_flags );

Take the snapshot in `snap' and serialise it into `*buffer' as a
snapshot of `type'. On entry, '*buffer' is assumed to be allocated
'*length' bytes, and will grow if necessary; if '*length' is zero,
'*buffer' can be uninitialised on entry. `in_flags' can be used
specify minor changes to the snapshot; currently the only available
option is LIBSPECTRUM_FLAG_SNAPSHOT_NO_COMPRESSION, which specifies
that the snapshot should not be compressed for formats where it would
normally be (.z80 and .szx). `out_flags' will return the logical OR of
some extra information from the serialisation:

LIBSPECTRUM_FLAG_SNAPSHOT_MINOR_INFO_LOSS
  A small amount of information was lost in serialisation. The
  resultant snapshot may not work correctly.

LIBSPECTRUM_FLAG_SNAPSHOT_MAJOR_INFO_LOSS
  A large amount of information was lost in serialisation. It is
  highly likely that the resultant snapshot won't work.

`creator' gives the information which will be written into the
snapshot to specify the creator of the file. This can be NULL, in
which case no information will be written. Currently, only the .szx
format will make any use of this information.

The only formats for which serialisation is supported are .sna, .szx
and .z80.

Deprecated snapshot routines
----------------------------

There are also three format-specific functions. However, *these
functions are deprecated and should not be used in new code*:

libspectrum_error libspectrum_sna_read( libspectrum_snap *snap,
		                        const libspectrum_byte *buffer,
				        size_t buffer_length )

Take the .sna snapshot of length `buffer_length' bytes at `buffer' and
convert it to a `libspectrum_snap' structure.

libspectrum_error libspectrum_z80_read( libspectrum_snap *snap,
		                        const libspectrum_byte *buffer,
					size_t buffer_length )

Similarly for a .z80 snapshot.

libspectrum_error libspectrum_z80_write( libspectrum_byte **buffer,
					 size_t *length,
					 libspectrum_snap *snap )

Take the snapshot in `snap' and serialise it into a .z80 file at
'*buffer'. On entry, '*buffer' is assumed to be allocated '*length'
bytes, and will grow if necessary; if '*length' is zero, '*buffer' can
be uninitialised on entry.

Tape functions
==============

libspectrum uses the opaque `libspectrum_tape' structure to represent
a tape image. Essentially, a `libspectrum_tape' is a list of tape
blocks, which are of the `libspectrum_tape_block' type detailed below.

The routines for dealing with tapes are:

libspectrum_error libspectrum_tape_alloc( libspectrum_tape **tape )

Allocate a new libspectrum_tape object.

libspectrum_error libspectrum_tape_free( libspectrum_tape *tape )

Free the memory used by a libspectrum_tape objects;

libspectrum_error libspectrum_tape_clear( libspectrum_tape *tape )

Free the memory used by the blocks in a libspectrum_tape object, but
not the object itself; useful if you're about to read a new tape into
a current object.

libspectrum_error
libspectrum_tape_read( libspectrum_tape *tape, const libspectrum_byte *buffer,
                       size_t length, libspectrum_id_t type,
                       const char *filename )

Take the tape image of type `type' of `length' bytes starting at
`buffer' and convert it to a `libspectrum_tape' structure. If `type'
is `LIBSPECTRUM_ID_UNKNOWN', guess the file format via
`libspectrum_identify_file'; `filename' is used only to help with the
identification process and can be set to NULL (or anything else) if
`type' is not `LIBSPECTRUM_ID_UNKNOWN'. Tape images compressed with
bzip2 or gzip will be automatically and transparently decompressed.

libspectrum_error
libspectrum_tap_read( libspectrum_tape *tape, const libspectrum_byte *buffer,
	              const size_t length )

Form a tape object in `tape' from the .tap file of `length' bytes
starting at `buffer'. This routine deals with the `normal' (Z80-style)
.tap files, not with Warajevo .tap files.

libspectrum_error
libspectrum_tap_write( libspectrum_byte **buffer, size_t *length,
		       libspectrum_tape *tape )

Attempt to convert the tape in `tape' to a .tap file in `*buffer', which
has previously been allocated `*length' bytes by user code.  The .tap
format can handle only standard speed loading blocks; a best guess
attempt will be made to convert other blocks, but the resultant .tap
file probably won't work.

libspectrum_error
libspectrum_tzx_read( libspectrum_tape *tape, const libspectrum_byte *buffer,
		      const size_t length )

Just as `libspectrum_tap_read', but for .tzx format files.

libspectrum_error
libspectrum_tzx_write( libspectrum_byte **buffer, size_t *length,
		       libspectrum_tape *tape )

Just as `libspectrum_tap_write', but for .tzx files. The conversion to
.tzx format is not lossy as it is with converting to .tap.

libspectrum_error
libspectrum_warajevo_read( libspectrum_tape *tape,
			   const libspectrum_byte *buffer,
			   const size_t length )

Just as `libspectrum_tap_read', but for Warajevo-style .tap files.
There is no currently no function to write a Warajevo-style .tap file.

libspectrum_error libspectrum_tape_get_next_edge( libspectrum_dword *tstates,
						  int *flags,
						  libspectrum_tape *tape )

This is the main workhorse function of the tape routines and will
return in `tstates' the number of tstates until the next edge should
occur from `tape'. `flags' will be set to the bitwise or of the
following:

LIBSPECTRUM_TAPE_FLAGS_BLOCK	The current block ends with this edge
LIBSPECTRUM_TAPE_FLAGS_STOP	User code should stop playing the tape
				after this edge
LIBSPECTRUM_TAPE_FLAGS_STOP48   User code should stop playing the tape
				after this edge if it was emulating a
				48K machine. The desired behaviour for
				things like the TC2048 is undefined in
				the .tzx format :-(

int libspectrum_tape_present( libspectrum_tape *tape )

Returns non-zero if `tape' currently contains a tape image and zero
otherwise.

libspectrum_error libspectrum_tape_position( int *n, libspectrum_tape *tape )

Return in `n' the position of the current block on the tape. The first
block is block 0, the second block 1, etc.

libspectrum_error libspectrum_tape_nth_block( libspectrum_tape *tape, int n )

Set the current block on the tape to be the `n'th block and initialise
it. Again, the first block on the tape is block 0.

libspectrum_error
libspectrum_tape_block_description( char *buffer, size_t length,
				    libspectrum_tape_block *block )

Copy into `buffer' (which has been allocated at least `length' bytes by
the user code) a text description of the type of `block'.

libspectrum_error
libspectrum_tape_append_block( libspectrum_tape *tape,
                               libspectrum_tape_block *block )

Append `block' to `tape'.

libspectrum_tape_block*
libspectrum_tape_current_block( libspectrum_tape *tape )

Get the currently active block on the tape.

libspectrum_tape_block*
libspectrum_tape_peek_next_block( libspectrum_tape *tape );

Get the next block on the tape, but don't move the tape along or
initialise the block.

libspectrum_tape_block*
libspectrum_tape_select_next_block( libspectrum_tape *tape );

Move the tape along so it points to the next block, initialise that
block and return it.

Tape iterators
--------------

In some circumstances, a program may wish to look through all the
blocks in a tape, but not actually change the state of the tape at
all. This can be done with a `libspectrum_tape_iterator'.

There are two routines for dealing with iterators:

libspectrum_tape_block*
libspectrum_tape_iterator_init( libspectrum_tape_iterator *iterator,
				libspectrum_tape *tape )

Initialise `iterator' to point to the first block of `tape' and return
that block. Returns NULL if the tape has no blocks.

libspectrum_tape_block*
libspectrum_tape_iterator_next( libspectrum_tape_iterator *iterator )

Make the already initialised `iterator' point to the next block of
`tape' and return that block (or NULL if there are no more blocks).

Tape blocks
-----------

The block format used by libspectrum is very similar to that used by
the TZX format itself; see http://www.worldofspectrum.org/TZXformat.html

The block types supported by libspectrum (along with their block ID in
the TZX format) are the following:

LIBSPECTRUM_TAPE_BLOCK_ROM		0x10
LIBSPECTRUM_TAPE_BLOCK_TURBO		0x11
LIBSPECTRUM_TAPE_BLOCK_PURE_TONE	0x12
LIBSPECTRUM_TAPE_BLOCK_PULSES		0x13
LIBSPECTRUM_TAPE_BLOCK_PURE_DATA	0x14
LIBSPECTRUM_TAPE_BLOCK_RAW_DATA		0x15

LIBSPECTRUM_TAPE_BLOCK_PAUSE		0x20
LIBSPECTRUM_TAPE_BLOCK_GROUP_START	0x21
LIBSPECTRUM_TAPE_BLOCK_GROUP_END	0x22
LIBSPECTRUM_TAPE_BLOCK_JUMP		0x23
LIBSPECTRUM_TAPE_BLOCK_LOOP_START	0x24
LIBSPECTRUM_TAPE_BLOCK_LOOP_END		0x25

LIBSPECTRUM_TAPE_BLOCK_SELECT		0x28

LIBSPECTRUM_TAPE_BLOCK_STOP48		0x2a

LIBSPECTRUM_TAPE_BLOCK_COMMENT		0x30
LIBSPECTRUM_TAPE_BLOCK_MESSAGE		0x31
LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO	0x32
LIBSPECTRUM_TAPE_BLOCK_HARDWARE		0x33

LIBSPECTRUM_TAPE_BLOCK_CUSTOM		0x35

LIBSPECTRUM_TAPE_BLOCK_CONCAT		0x5a

These values are defined in the `libspectrum_tape_type' enumeration.
The `concatenation' block (0x5a) is recognised on input, but just
skipped; hence it wil never appear in a libspectrum_tape_block.

The basic routines for dealing with tape blocks are:

libspectrum_error
libspectrum_tape_block_alloc( libspectrum_tape_block **block,
			      libspectrum_tape_type type )

Allocate a new tape block of `type'.

libspectrum_error libspectrum_tape_block_free( libspectrum_tape_block *block )

Free a tape block.

libspectrum_tape_type
libspectrum_tape_block_type( libspectrum_tape_block *block )

Return the type of `block'.

libspectrum_error
libspectrum_tape_block_set_type( libspectrum_tape_block *block,
				 libspectrum_tape_type type )

Set the type of `block' to `type'.

libspectrum_error libspectrum_tape_block_init( libspectrum_tape_block *block )

Initialise `block' such that it is ready for playback (with
`libspectrum_tape_get_next_edge').

Similar to the snapshot structure, there is a large family of routines
for setting and retrieving the parameters of individual block types.
For tape blocks, this becomes more complicated still as different
block types possess different parameters. If a `set' function is
called for a parameter which is not relevant to the current block
type, an error will be printed and LIBSPECTRUM_ERROR_INVALID returned.
For the `get' functions, an error will be printed and an undefined
value returned.

The `get' and `set' functions follow the same pattern as for the
snapshot routines: the `get' functions are like

<type> libspectrum_tape_block_<name>( libspectrum_tape_block *block )

or

<type> libspectrum_tape_block_<name>( libspectrum_tape_block *block,
	                              size_t index )

for array values and the `set' functions like

libspectrum_error
libspectrum_tape_block_set_<name>( libspectrum_tape_block *block,
				   <type> new_value )

or

libspectrum_error
libspectrum_tape_block_set_<name>( libspectrum_tape_block *block,
				   size_t index, <type> new_value )

The parameters, their types, and the LIBSPECTRUM_TAPE_BLOCK_* types
for which it is relevant are:

libspectrum_dword bit_length		RAW_DATA

libspectrum_dword bit0_length		PURE_DATA
					TURBO

libspectrum_dword bit1_length		PURE_DATA
					TURBO

size_t bits_in_last_byte		PURE_DATA
					RAW_DATA
					TURBO

size_t count				PURE_TONE
					PULSES
					LOOP_START
					SELECT
					ARCHIVE_INFO
					HARDWARE

libspectrum_byte* data			CUSTOM
					PURE_DATA
					RAW_DATA
					ROW
					TURBO

size_t data_length			CUSTOM
					PURE_DATA
					RAW_DATA
					ROM
					TURBO

int ids[]				ARCHIVE_INFO
					HARDWARE

int offset				JUMP

int offsets[]				SELECT

libspectrum_dword pause			MESSAGE
					PAUSE
					PURE_DATA
					RAW_DATA
					ROM
					TURBO

libspectrum_dword pilot_length		TURBO

size_t pilot_pulses			TURBO

libspectrum_dword pulse_length		LENGTH

libspectrum_dword pulse_lengths[]	PULSES

libspectrum_dword sync1_length		TURBO

libspectrum_dword sync2_length		TURBO

libspectrum_byte* text			GROUP_START
					COMMENT
					MESSAGE
					CUSTOM

libspectrum_byte* texts[]		ARCHIVE_INFO
					SELECT

int types[]				HARDWARE

int values[]				HARDWARE

There is one further parameter which can be accessed for PURE_DATA,
RAW_DATA, ROM and TURBO blocks, which is `libspectrum_tape_state_type
state'. This determines the part of the block which is currently being
played, and can have the following values:

LIBSPECTRUM_TAPE_STATE_INVALID  /* Should never be seen */

LIBSPECTRUM_TAPE_STATE_PILOT 	/* Pilot pulses */
LIBSPECTRUM_TAPE_STATE_SYNC1 	/* First sync pulse */
LIBSPECTRUM_TAPE_STATE_SYNC2 	/* Second sync pulse */
LIBSPECTRUM_TAPE_STATE_DATA1 	/* First edge of a data bit */
LIBSPECTRUM_TAPE_STATE_DATA2 	/* Second edge of a data bit */
LIBSPECTRUM_TAPE_STATE_PAUSE 	/* The pause at the end of a block */

Setting this value to anything other than
`LIBSPECTRUM_TAPE_STATE_PAUSE' will produce undefined results.
Exposing this parameter is an ugly hack needed by Fuse to allow for
flash-loading of tape blocks, and setting it should not be used unless
absolutely necessary.

Input recording functions
=========================

All input recording routines are accessed through the opaque
`libspectrum_rzx' structure.

libspectrum_error libspectrum_rzx_alloc( libspectrum_rzx **rzx )

Allocate a new input recording object.

libspectrum_error libspectrum_rzx_free( libspectrum_rzx *rzx )

Free the memory used by an input recording object as allocated by
`libspectrum_rzx_alloc'.

libspectrum_error libspectrum_rzx_store_frame( libspectrum_rzx *rzx,
					       size_t instructions, 
					       size_t count,
					       libspectrum_byte *in_bytes )

Add a frame to `rzx' in which `instructions' opcodes where fetched,
and `count' bytes, specified in `in_bytes', were read from the IO
ports.

libspectrum_error libspectrum_rzx_start_playback( libspectrum_rzx *rzx )

Prepare to start playback of the input recording `rzx'.

libspectrum_error libspectrum_rzx_playback_frame( libspectrum_rzx *rzx,
                                                  int *finished )

Move onto the next frame of playback from the input recording
`rzx'. If the last frame has now been played, `*finished' will be
non-zero, otherwise it will be zero.

If the correct number of bytes were not read from `rzx' during the
frame via `libspectrum_rzx_playback', an error will be given.

libspectrum_error libspectrum_rzx_playback( libspectrum_rzx *rzx,
                                            libspectrum_byte *byte )

Return in `*byte' the next byte to be read from the IO ports from the
current frame of `rzx'.

size_t libspectrum_rzx_tstates( libspectrum_rzx *rzx );

Return the 'starting tstates' field of `rzx'.

size_t libspectrum_rzx_set_tstates( libspectrum_rzx *rzx, size_t tstates )

Set (and return the new value of) the 'starting tstates' field of `rzx'.

size_t libspectrum_rzx_instructions( libspectrum_rzx *rzx )

Return the number of opcode fetches to be performed during the current
frame of `rzx'.

libspectrum_error
libspectrum_rzx_read( libspectrum_rzx *rzx, libspectrum_snap **snap,
		      const libspectrum_byte *buffer, const size_t length,
		      libspectrum_rzx_signature *signature )

Given a .rzx file of `length' bytes starting at `buffer', extract the
input recording data into `rzx' and the embedded snapshot into
`*snap'. If there is no embedded snapshot, `*snap' will be NULL after
the call. If the RZX file contains a digital signature, `*signature'
will contain the information from that signature; see below for
information on this. Files compressed with bzip2 or gzip will be
automatically and transparently decompressed.

libspectrum_error
libspectrum_rzx_write( libspectrum_byte **buffer, size_t *length,
		       libspectrum_rzx *rzx,
		       libspectrum_byte *snap, libspectrum_creator *creator,
		       int compress, libspectrum_rzx_dsa_key *key )

Given input recording data in `rzx' and a snapshot in `snap', create a
.rzx file in `*buffer'. If no embedded snapshot is required, set
`snap' to be NULL. Before the call, `*buffer' should be allocated at
least `*length' bytes (can be zero). After the call, `*length'
contains the length of the .rzx file.

`creator' contains the creator information which should be written
into the RZX file. If `key' is non-NULL, the RZX file will be
digitally signed using the specified DSA key; see below for more
details.

Digital signatures in RZX files
-------------------------------

One use of input recording files is to allow a `best Spectrum games
player' tournament to be held: everybody records themselves playing a
game, sends in input recordings of themselves doing this, and then
everyone can see who is the best player. When using an emulator, it is
obviously rather easy to cheat at this by using snapshots, slowing
down the emulator and all sorts of other ways. Fundamentally, there's
no way round this problem except by running a the emulator on trusted
hardware, which is a whole different kettle of fish.

One such `best player' tournament, the Speccy Tour 2003 (see
http://www.speccy.org/SpeccyTour03/ ) required that for an emulator to
be `allowable' for the Tour, it must implement a `competition mode' in
which snapshot saving etc is forbidden and must then `prove' that the
RZX file was made in competition mode by digitally signing the RZX
file. This is clearly still insecure as the key must be present in the
emulator to allow it to sign files, but that's what the organisers
wanted, so it's implemented in libspectrum. What information you wish
to draw from the presence or absence of a digitial signature on an RZX
file is entirely up to you.

The digital signature routines use libgcrypt to provide the necessary
support. If this is not present when libspectrum is compiled, the
signature routines will not available.

If you're not aware of the DSA algorithm, the rest of this section
probably won't make much sense to you; go and read a good cryptography
textbook :-)

To sign an RZX file, all that is required is a DSA key. libspectrum
uses a `libspectrum_rzx_dsa_key' structure to represent this:

typedef struct libspectrum_rzx_dsa_key {

  const char *p, *q, *g, *y, *x;

} libspectrum_rzx_dsa_key;

Each of the fields is one of the standard DSA parameters, stored as a
hex string with the MSB first. `x' should be set to NULL for a public
key.

When a digitally signed RZX file is read, the signature information
will be read into a `libspectrum_rzx_signature' structure:

typedef struct libspectrum_rzx_signature {

  libspectrum_dword key_id;

  const libspectrum_byte *start; ptrdiff_t length;

  GcryMPI r, s;

} libspectrum_rzx_signature;

`key_id' is the low 32-bits of the `y' parameter of the key used to
sign this file; you'll have to implement your own lookup table to find
the rest of the key. `start' points to the signed data, which is
`length' bytes long. `r' and `s' are the standard DSA signature
parameters, stored in libgcrypt's native MPI format. (If libgcrypt is
not available, these parameters are simply not present in the
structure).

To verify a signature, simply call `libspectrum_verify_signature':

libspectrum_error
libspectrum_verify_signature( libspectrum_rzx_signature *signature,
			      libspectrum_rzx_dsa_key *key )

This will return LIBSPECTRUM_ERROR_NONE if the signature is valid or
LIBSPECTRUM_ERROR_SIGNATURE if it is invalid.

Once you're done with a signature, `libspectrum_signature_free' will
release the memory it was using:

libspectrum_error
libspectrum_signature_free( libspectrum_rzx_signature *signature )

Note this will not free the data pointed to by `start'.

Timex dock/exrom handling functions
===================================

The Timex TC2068 featured a cartridge port (the `dock') for which a
few pieces of software were made available. The Warajevo emulator
includes support for this feature, and uses the `.dck' extension for
images of these cartridges. Documentation on this format is available
at http://www.worldofspectrum.org/warajevo/Fformats.html#dck

Each .dck file can hold multiple 64Kb RAM banks, which are stored in a
`libspectrum_dck' structure:

typedef struct libspectrum_dck {
  libspectrum_dck_block *dck[256];
} libspectrum_dck;

Each 64Kb bank is stored in a `libspectrum_dck_block' structure:

typedef struct libspectrum_dck_block {
  libspectrum_dck_bank bank;
  libspectrum_dck_page_type access[8];
  libspectrum_byte *pages[8];
} libspectrum_dck_block;

The `bank' field specifies which type of memory this bank represents,
and takes one of the following values:

LIBSPECTRUM_DCK_BANK_DOCK	The dock
LIBSPECTRUM_DCK_BANK_EXROM	The EXROM 
LIBSPECTRUM_DCK_BANK_HOME	The normal memory space

The `access' field gives the type of memory stored in each 8Kb page
within the 64 Kb bank, and take be:

LIBSPECTRUM_DCK_PAGE_NULL	Not present in this bank
LIBSPECTRUM_DCK_PAGE_RAM_EMPTY  Uninitialised RAM
LIBSPECTRUM_DCK_PAGE_ROM	ROM
LIBSPECTRUM_DCK_PAGE_RAM	Initialised RAM

Any ROM or initialised RAM banks are in the `pages' field.

The actual routines for handling dock files:

libspectrum_error libspectrum_dck_alloc( libspectrum_dck **dck )

Allocate a dock structure.

libspectrum_error libspectrum_dck_free( libspectrum_dck *dck, int keep_pages );

Free a dock structure; if `keep_pages' is non-zero, any memory
allocated to the structure will not be freed and can then be used by
the calling program. Do remember to free it when you're finished with it!

libspectrum_error
libspectrum_dck_read( libspectrum_dck *dck, const libspectrum_byte *buffer,
                      size_t length );

Read in a dock structure from the `length' byte long `buffer'. Images
compressed with bzip2 or gzip will be automatically and transparently
decompressed.

$Id: libspectrum.txt,v 1.19.4.1 2004/02/05 15:40:26 pak21 Exp $
