/************************************************************************** sord_cas.c Format code for Sord M5 cassette files **************************************************************************/ #include #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 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