/**************************************************************************
sord_cas.c
Format code for Sord M5 cassette files
**************************************************************************/
#include <string.h>
#include "sord_cas.h"
#define SORDM5_WAVESAMPLES_HEADER 1
#define SORDM5_WAVESAMPLES_TRAILER 1
#define SORDM5_WAVESAMPLES_BLOCK 1
static const UINT8 SORDM5_CAS_HEADER[6] = { 'S', 'O', 'R', 'D', 'M', '5'};
static const struct CassetteModulation sordm5_cas_modulation =
{
CASSETTE_MODULATION_SINEWAVE,
1575.0 - 300, 1575.0, 1575.0 + 300,
3150.0 - 600, 3150.0, 3150.0 + 600
};
static UINT8 cassette_image_read_uint8( cassette_image *cassette, UINT64 offset)
{
UINT8 data;
cassette_image_read( cassette, &data, offset, 1);
return data;
}
static casserr_t sordm5_tap_identify( cassette_image *cassette, struct CassetteOptions *opts)
{
return cassette_modulation_identify( cassette, &sordm5_cas_modulation, opts);
}
static casserr_t sordm5_tap_load( cassette_image *cassette)
{
casserr_t err;
UINT64 image_size;
double time_index = 0.0;
double time_displacement;
UINT8 header[16];
UINT64 image_pos;
UINT8 block_type, byte, bit;
UINT32 block_size, i, j;
size_t filler_length;
// init
image_size = cassette_image_size(cassette);
image_pos = 0;
// read/check header
if (image_size < 16) return CASSETTE_ERROR_INVALIDIMAGE;
cassette_image_read( cassette, &header, image_pos, 16);
image_pos += 16;
if (memcmp( header, SORDM5_CAS_HEADER, sizeof(SORDM5_CAS_HEADER)) != 0) return CASSETTE_ERROR_INVALIDIMAGE;
// add silence (header)
err = cassette_put_sample( cassette, 0, time_index, SORDM5_WAVESAMPLES_HEADER, 0);
if (err) return err;
time_index += SORDM5_WAVESAMPLES_HEADER;
// process blocks
while (image_pos < image_size)
{
// read block type
block_type = cassette_image_read_uint8( cassette, image_pos + 0);
if ((block_type != 'H') && (block_type != 'D')) return CASSETTE_ERROR_INVALIDIMAGE;
// read block size
block_size = cassette_image_read_uint8( cassette, image_pos + 1);
if (block_size == 0) block_size = 0x100;
block_size += 3;
// add silence (header block)
if (block_type == 'H')
{
err = cassette_put_sample( cassette, 0, time_index, SORDM5_WAVESAMPLES_BLOCK, 0);
if (err) return err;
time_index += SORDM5_WAVESAMPLES_BLOCK;
}
// add sync
if (block_type == 'H') filler_length = 2.4 * (3150 / 8); else filler_length = 0.15 * (3150 / 8);
err = cassette_put_modulated_filler(cassette, 0, time_index, 0xFF, filler_length, &sordm5_cas_modulation, &time_displacement);
if (err) return err;
time_index += time_displacement;
// process block
for (i=0;i<block_size;i++)
{
// read byte
byte = cassette_image_read_uint8( cassette, image_pos + i);
// calc/check checksum
#if 0
if (i == block_size)
{
if (byte != (crc & 0xFF)) return CASSETTE_ERROR_INVALIDIMAGE;
}
if (i > 2) crc += byte;
#endif
// process byte
for (j=0;j<10;j++)
{
// get bit
if (j < 2)
{
bit = ((j==0)?1:0);
}
else
{
bit = (byte >> (j-2)) & 1;
}
// add bit
err = cassette_put_modulated_data_bit( cassette, 0, time_index, bit, &sordm5_cas_modulation, &time_displacement);
if (err) return err;
time_index += time_displacement;
}
}
// mark end of block
err = cassette_put_modulated_data_bit( cassette, 0, time_index, 1, &sordm5_cas_modulation, &time_displacement);
if (err) return err;
time_index += time_displacement;
// next block
image_pos += block_size;
}
// add silence (trailer)
err = cassette_put_sample( cassette, 0, time_index, SORDM5_WAVESAMPLES_TRAILER, 0);
if (err) return err;
time_index += SORDM5_WAVESAMPLES_TRAILER;
//
return CASSETTE_ERROR_SUCCESS;
}
static const struct CassetteFormat sordm5_cas_format =
{
"cas",
sordm5_tap_identify,
sordm5_tap_load,
NULL
};
CASSETTE_FORMATLIST_START(sordm5_cassette_formats)
CASSETTE_FORMAT(sordm5_cas_format)
CASSETTE_FORMATLIST_END