diff options
Diffstat (limited to 'src/lib/formats/vg5k_cas.cpp')
-rw-r--r-- | src/lib/formats/vg5k_cas.cpp | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src/lib/formats/vg5k_cas.cpp b/src/lib/formats/vg5k_cas.cpp new file mode 100644 index 00000000000..ef648707fd8 --- /dev/null +++ b/src/lib/formats/vg5k_cas.cpp @@ -0,0 +1,237 @@ +// license:BSD-3-Clause +// copyright-holders:Sandro Ronco +/******************************************************************** + + Support for VG-5000 .k7 cassette images + +********************************************************************/ +#include <assert.h> + +#include "vg5k_cas.h" + + +#define SMPLO -32768 +#define SILENCE 0 +#define SMPHI 32767 + + +static int k7_size; + +/******************************************************************* + Generate one high-low cycle of sample data +********************************************************************/ +INLINE int vg5k_cas_cycle(INT16 *buffer, int sample_pos, int len) +{ + int i = 0; + + if (buffer) + { + while( i < len) + { + buffer[ sample_pos + i ] = SMPHI; + i++; + } + + while( i < len * 2 ) + { + buffer[ sample_pos + i ] = SMPLO; + i++; + } + } + return len * 2; +} + + +/******************************************************************* + Generate n samples of silence +********************************************************************/ +INLINE int vg5k_cas_silence(INT16 *buffer, int sample_pos, int len) +{ + int i = 0; + + if ( buffer ) + for( i = 0; i < len; i++) + buffer[ sample_pos + i ] = SILENCE; + + return len; +} + + +/******************************************************************* + Generate the end-byte samples +********************************************************************/ +INLINE int vg5k_cas_eob(INT16 *buffer, int sample_pos) +{ + int i, samples = 0; + + for (i = 0; i < 4; i++) + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5); + + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 10); + + return samples; +} + + +INLINE int vg5k_cas_byte(INT16 *buffer, int sample_pos, UINT8 data) +{ +/* Writing an entire byte */ + int i, samples; + + samples = 0; + for ( i = 0; i < 8; i++ ) + { + if ( data & 0x01 ) + { + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5 ); + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5 ); + } + else + { + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 10 ); + } + + data >>= 1; + } + return samples; +} + + +/******************************************************************* + Generate n sample of synchro +********************************************************************/ +INLINE int vg5k_k7_synchro(INT16 *buffer, int sample_pos, int len) +{ + int i, samples = 0; + + for ( i = 0; i < len ; i++ ) + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5); + + samples += vg5k_cas_eob( buffer, sample_pos + samples); + + return samples; +} + + +static int vg5k_handle_tap(INT16 *buffer, const UINT8 *casdata) +{ + int data_pos, sample_count; + + data_pos = 0; + sample_count = 0; + + /* file has to start with an head block */ + if (casdata[0] != 0xd3 || casdata[1] != 0xd3 || casdata[2] != 0xd3) + return -1; + + /* on the entire file*/ + while( data_pos < k7_size ) + { + UINT16 block_size = 0; + + /* Identify type of block */ + if (casdata[data_pos] == 0xd3) + { + /* head block have fixed size of 32 byte */ + block_size = 0x20; + + /* 1 sec of silence before the head block */ + sample_count += vg5k_cas_silence(buffer, sample_count, 44100); + + /* head block starts with 30000 samples of synchro */ + sample_count += vg5k_k7_synchro( buffer, sample_count, 30000 ); + } + else if (casdata[data_pos] == 0xd6) + { + /* data block size is defined in head block */ + block_size = (casdata[data_pos - 4] | casdata[data_pos - 3]<<8) + 20; + + /* 10000 samples of silence before the data block */ + sample_count += vg5k_cas_silence(buffer, sample_count, 10000); + + /* data block starts with 7200 samples of synchro */ + sample_count += vg5k_k7_synchro( buffer, sample_count, 7200); + } + else + { + /* tries to handle files that do not respect the size declared in the head block */ + while (data_pos < k7_size && casdata[data_pos] != 0xd3 && casdata[data_pos] != 0xd6) + data_pos++; + } + + /* Data samples */ + for ( ; block_size ; data_pos++, block_size-- ) + { + /* Make sure there are enough bytes left */ + if (data_pos > k7_size) + return -1; + + sample_count += vg5k_cas_byte( buffer, sample_count, casdata[data_pos] ); + + /* generate the end-byte samples */ + sample_count += vg5k_cas_eob( buffer, sample_count); + } + } + + /* Finish with 10000 samples of silence */ + sample_count += vg5k_cas_silence(buffer, sample_count, 10000); + + return sample_count; +} + + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int vg5k_k7_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return vg5k_handle_tap(buffer, bytes); +} + + +/******************************************************************* + Calculate the number of samples needed for this tape image classical +********************************************************************/ +static int vg5k_k7_to_wav_size(const UINT8 *casdata, int caslen) +{ + k7_size = caslen ; + + return vg5k_handle_tap( NULL, casdata ); +} + + +static const struct CassetteLegacyWaveFiller vg5k_legacy_fill_wave = +{ + vg5k_k7_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + vg5k_k7_to_wav_size, /* chunk_sample_calc */ + 44100, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t vg5k_k7_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &vg5k_legacy_fill_wave); +} + + +static casserr_t vg5k_k7_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &vg5k_legacy_fill_wave); +} + + +static const struct CassetteFormat vg5k_k7_format = +{ + "k7", + vg5k_k7_identify, + vg5k_k7_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(vg5k_cassette_formats) + CASSETTE_FORMAT(vg5k_k7_format) +CASSETTE_FORMATLIST_END |