/*********************************************************************

    formats/atom_tap.c

    Cassette code for Acorn Atom tap files (Kansas City Standard)

*********************************************************************/

/*

    http://beebwiki.jonripley.com/Acorn_cassette_format#Atom

    The Atom supports the System series format (as a 'nameless file') and its own format. The data consists of files separated
    by carrier breaks and the appropriate leaders and trailers, and further subdivided into blocks separated by inter-block gaps.
    An Atom block consists of the following sequence of bytes:

        Four synchronisation bytes (&2A).
        File name (one to thirteen characters).
        One end of file name marker byte (&0D).
        Block flag, one byte.
        Block number, two bytes, high byte first.
        Data block length ? 1, one byte.
        Execution address, two bytes, high byte first.
        Load address, two bytes, high byte first.
        Data, 0 to 256 bytes.
        Checksum, one byte.

    The block flag is set as follows:

        Bit 7 is set if this block is not the last block of a file.
        Bit 6 is set if the block contains data. If clear then the 'data block length' field is invalid.
        Bit 5 is set if this block is not the first block of a file.
        Bits 4 to 0 are undefined. Normally their contents are:
        in the first block, bits 15 to 11 of the end address (=1 + the last address saved in the file);
        in subsequent blocks, bits 6 to 2 of the previous block flag.

    The checksum is a simple sum (modulo 256) of all bytes from the start of the filename to the end of the data.

*/

#include "atom_tap.h"
#include "uef_cas.h"
/***************************************************************************
    PARAMETERS
***************************************************************************/

#define LOG 1

/***************************************************************************
    IMPLEMENTATION
***************************************************************************/

/*-------------------------------------------------
    CassetteModulation atom_tap_modulation
-------------------------------------------------*/

static const struct CassetteModulation atom_tap_modulation =
{
	CASSETTE_MODULATION_SINEWAVE,
	1200.0 - 300, 1200.0, 1200.0 + 300,
	2400.0 - 600, 2400.0, 2400.0 + 600
};

/*-------------------------------------------------
    cassette_image_read_uint8 - read tape data
-------------------------------------------------*/

static UINT8 cassette_image_read_uint8(cassette_image *cassette, UINT64 offset)
{
	UINT8 data;
	cassette_image_read(cassette, &data, offset, 1);
	return data;
}

/*-------------------------------------------------
    atom_tap_identify - identify cassette
-------------------------------------------------*/

static casserr_t atom_tap_identify(cassette_image *cassette, struct CassetteOptions *opts)
{
	return cassette_modulation_identify( cassette, &atom_tap_modulation, opts);
}

/*-------------------------------------------------
    atom_tap_load - load cassette
-------------------------------------------------*/

#define MODULATE(_value) \
	for (int i = 0; i < (_value ? 8 : 4); i++) { \
		err = cassette_put_modulated_data_bit(cassette, 0, time_index, _value, &atom_tap_modulation, &time_displacement);\
		if (err) return err;\
		time_index += time_displacement;\
	}

#define BIT(x,n) (((x)>>(n))&1)

static casserr_t atom_tap_load(cassette_image *cassette)
{
	casserr_t err;
	UINT64 image_size = cassette_image_size(cassette);
	UINT64 image_pos = 0;
	double time_index = 0.0;
	double time_displacement;

	while (image_pos < image_size)
	{
		UINT8 data = cassette_image_read_uint8(cassette, image_pos);

		/* start bit */
		MODULATE(0);

		/* data bits */
		for (int bit = 0; bit < 8; bit++)
		{
			MODULATE(BIT(data, bit));
		}

		/* stop bits */
		MODULATE(1);
		MODULATE(1);

		image_pos++;
	}

	return CASSETTE_ERROR_SUCCESS;
}

/*-------------------------------------------------
    CassetteFormat atom_tap_cassette_format
-------------------------------------------------*/

const struct CassetteFormat atom_tap_format =
{
	"tap",
	atom_tap_identify,
	atom_tap_load,
	NULL
};

CASSETTE_FORMATLIST_START( atom_cassette_formats )
	CASSETTE_FORMAT(atom_tap_format)
	CASSETTE_FORMAT(uef_cassette_format)
CASSETTE_FORMATLIST_END
