libspectrum 0.1.0
=================

libspectrum is a fairly simple library designed to make the handling
of various ZX Spectrum emulator-related file formats easy.  So far it
handles .sna and .z80 snapshots (.sna read only), .tap and .tzx tape
images and .rzx input recording files.

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

It would be nice if we could get it such that user code should never use
the libspectrum_* objects directly, but only pointers to them so binary
compatibility can be maintained if (when) the data structures change.
However, this hasn't really happened for anything other than the
libspectrum_rzx object, which is fully encapsulated.

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 three standard types which may be of use:

libspectrum_byte		An unsigned 8-bit type
libspectrum_word		An unsigned 16-bit type
libspectrum_dword		An unsigned 32-bit type

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_48		48K Spectrum
LIBSPECTRUM_MACHINE_TC2048	Timex TC2048
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_AY
  This machine has an AY-3-8912 sound chip (as in the 128K and later
  machines).

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

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

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

The `libspectrum_identify_file' 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( 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_RECORDING_RZX	A .rzx input recording
LIBSPECTRUM_ID_SNAPSHOT_SNA     A .sna snapshot 
LIBSPECTRUM_ID_SNAPSHOT_Z80	A .z80 snapshot
LIBSPECTRUM_ID_TAPE_TAP		A .tap tape file
LIBSPECTRUM_ID_TAPE_TZX		A .tzx tape file

`libspectrum_identify_file' 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.

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

Functions for dealing with snapshot files. These act on a
`libspectrum_snap' structure:

typedef struct libspectrum_snap {

  /* Which machine are we using here? */

  libspectrum_machine machine;

  /* Registers and the like */

  libspectrum_byte a , f ; libspectrum_word bc , de , hl ;
  libspectrum_byte a_, f_; libspectrum_word bc_, de_, hl_;

  libspectrum_word ix, iy; libspectrum_byte i, r;
  libspectrum_word sp, pc;

  libspectrum_byte iff1, iff2, im;

  /* RAM */

  libspectrum_byte *pages[8];

  /* Data from .slt files */

  libspectrum_byte *slt[256];   /* Level data */
  size_t slt_length[256];       /* Length of each level */

  libspectrum_byte *slt_screen; /* Loading screen */
  int slt_screen_level;         /* The id of the loading screen. Not used
                                   for anything AFAIK, but copy it
				   around just in case */

  /* Peripheral status */

  libspectrum_byte out_ula; libspectrum_dword tstates;

  libspectrum_byte out_128_memoryport;

  libspectrum_byte out_ay_registerport, ay_registers[16];

  libspectrum_byte out_plus3_memoryport;

  /* Timex-specific bits */
  libspectrum_byte out_scld_hsr, out_scld_dec;

} libspectrum_snap;

Most of that should be fairly self-explanatory; the a_, f_, bc_, de_
and hl_ members 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).

The actual routines for dealing with snaps are:

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

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

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.

Note that there is (currently) no function to serialise to a .sna
snapshot.

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

In libspectrum, a tape is represented by a singly linked list of tape
blocks, `libspectrum_tape':

typedef struct libspectrum_tape {

  /* All the blocks */
  GSList* blocks;

  /* The current block */
  GSList* current_block;

} libspectrum_tape;

For more details on the `GSList' type, see the `glib' documentation;
libspectrum will provide a (minimally functional) replacement if
`glib' was not available when it was compiled.

Each of the entries in the `blocks' list should be of
`libspectrum_tape_block' type; details on this are in `tape_blocks.txt'.

The actual 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 objects 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'.

int libspectrum_tape_present( libspectrum_tape *tape )

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

libspectrum_error libspectrum_tape_init_block( GSList *current_block )

Initialise the block pointed to by `current_block' so that it can be
used for playback.

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

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 :-(

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_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'.

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.

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 )

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.

libspectrum_error
libspectrum_rzx_write( libspectrum_byte **buffer, size_t *length,
		       libspectrum_rzx *rzx,
		       libspectrum_byte *snap, const char *program,
		       libspectrum_word major, libspectrum_word minor,
		       int compress )

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.

`program', `major' and `minor' give the values to be placed in the
.rzx header fields. If `compress' is non-zero (and `zlib' was
available when libspectrum was compiled), the resulting .rzx file will
have its snapshot and input recording data compressed.

$Id: libspectrum.txt,v 1.10 2002/12/04 16:48:56 pak21 Exp $
