diff options
-rw-r--r-- | .gitattributes | 2 | ||||
-rw-r--r-- | src/lib/formats/cassimg.c | 5 | ||||
-rw-r--r-- | src/lib/formats/cassimg.h | 1 | ||||
-rw-r--r-- | src/lib/formats/tvc_cas.c | 225 | ||||
-rw-r--r-- | src/lib/formats/tvc_cas.h | 16 | ||||
-rw-r--r-- | src/lib/formats/wavfile.c | 2 | ||||
-rw-r--r-- | src/lib/lib.mak | 1 |
7 files changed, 250 insertions, 2 deletions
diff --git a/.gitattributes b/.gitattributes index 1482b838760..54e82ba0de0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1591,6 +1591,8 @@ src/lib/formats/trs_cas.c svneol=native#text/plain src/lib/formats/trs_cas.h svneol=native#text/plain src/lib/formats/trs_dsk.c svneol=native#text/plain src/lib/formats/trs_dsk.h svneol=native#text/plain +src/lib/formats/tvc_cas.c svneol=native#text/plain +src/lib/formats/tvc_cas.h svneol=native#text/plain src/lib/formats/tzx_cas.c svneol=native#text/plain src/lib/formats/tzx_cas.h svneol=native#text/plain src/lib/formats/uef_cas.c svneol=native#text/plain diff --git a/src/lib/formats/cassimg.c b/src/lib/formats/cassimg.c index 809a60ce26a..f17f0f1906f 100644 --- a/src/lib/formats/cassimg.c +++ b/src/lib/formats/cassimg.c @@ -549,7 +549,10 @@ casserr_t cassette_put_samples(cassette_image *cassette, int channel, /* compute the value that we are writing */ switch(waveform_bytes_per_sample(waveform_flags)) { case 1: - dest_value = extrapolate8(*((INT8 *) source_ptr)); + if (waveform_flags & CASSETTE_WAVEFORM_UNSIGNED) + dest_value = extrapolate8((INT8)(*source_ptr - 128)); + else + dest_value = extrapolate8(*((INT8 *) source_ptr)); break; case 2: word = *((INT16 *) source_ptr); diff --git a/src/lib/formats/cassimg.h b/src/lib/formats/cassimg.h index cdbaff04ca6..9f665ff0d42 100644 --- a/src/lib/formats/cassimg.h +++ b/src/lib/formats/cassimg.h @@ -34,6 +34,7 @@ #define CASSETTE_WAVEFORM_32BIT 4 #define CASSETTE_WAVEFORM_32BIT_FLIP 5 #define CASSETTE_WAVEFORM_ENDIAN_FLIP 1 +#define CASSETTE_WAVEFORM_UNSIGNED 8 #define CASSETTE_MODULATION_SQUAREWAVE 0 #define CASSETTE_MODULATION_SINEWAVE 1 diff --git a/src/lib/formats/tvc_cas.c b/src/lib/formats/tvc_cas.c new file mode 100644 index 00000000000..5b7e6f8a422 --- /dev/null +++ b/src/lib/formats/tvc_cas.c @@ -0,0 +1,225 @@ +/******************************************************************** + + Support for Videoton TVC .cas files + + http://tvc.homeserver.hu/html/konvertformatum.html + +********************************************************************/ + +#include "tvc_cas.h" + +#define TVC64_BIT0_FREQ 1812 +#define TVC64_BIT1_FREQ 2577 +#define TVC64_PRE_FREQ 2128 +#define TVC64_SYNC_FREQ 1359 + +#define WAVE_AMPLITUDE 0x3fffffff +#define TVC64_HEADER_BYTES 0x90 +#define TVC64_HEADER_BYTES 0x90 + +static void tvc64_emit_level(cassette_image *cass, double &time, int freq, int level) +{ + double period = 1.0 / freq; + cassette_put_sample(cass, 0, time, period, level * WAVE_AMPLITUDE); + time += period; +} + +static casserr_t tvc64_output_byte(cassette_image *cass, double &time, UINT8 byte) +{ + for (int i=0; i<8; i++) + { + if ((byte>>i) & 0x01) + { + tvc64_emit_level(cass, time, TVC64_BIT1_FREQ*2, +1); + tvc64_emit_level(cass, time, TVC64_BIT1_FREQ*2, -1); + } + else + { + tvc64_emit_level(cass, time, TVC64_BIT0_FREQ*2, +1); + tvc64_emit_level(cass, time, TVC64_BIT0_FREQ*2, -1); + } + } + + return CASSETTE_ERROR_SUCCESS; +} + +static int tvc64_output_predata(cassette_image *cass, double &time, int number) +{ + for (int i=0; i<number; i++) + { + tvc64_emit_level(cass, time, TVC64_PRE_FREQ*2, +1); + tvc64_emit_level(cass, time, TVC64_PRE_FREQ*2, -1); + } + + return CASSETTE_ERROR_SUCCESS; +} + +static UINT16 tvc64_calc_crc(const UINT8 *bytes, int size) +{ + UINT16 crc = 0; + + for (int i=0; i<size; i++) + { + for (int b=0; b<8; b++) + { + UINT8 al = (bytes[i] & (1<<b)) ? 0x80 : 0x00; + + al ^= ((crc>>8) & 0xff); + + if (al & 0x80) + crc ^= 0x0810; + + crc <<= 1; + + if (al & 0x80) crc += 1; + } + } + + return crc; +} + +static casserr_t tvc64_cassette_load(cassette_image *cassette) +{ + UINT8 tmp_buff[512]; + int buff_idx = 0; + double time = 0.0; + + UINT8 header[TVC64_HEADER_BYTES]; + cassette_image_read(cassette, header, 0, TVC64_HEADER_BYTES); + UINT16 cas_size = (header[0x83]<<8) | header[0x82]; + + // tape header + tmp_buff[buff_idx++] = 0x00; + tmp_buff[buff_idx++] = 0x6a; + tmp_buff[buff_idx++] = 0xff; // head sector + tmp_buff[buff_idx++] = 0x11; // not puffered + tmp_buff[buff_idx++] = 0x00; // not write protected + tmp_buff[buff_idx++] = 0x01; // 1 sector + tmp_buff[buff_idx++] = 0x00; // sector number + tmp_buff[buff_idx++] = 0x12; // head size + tmp_buff[buff_idx++] = 0x01; // name size + tmp_buff[buff_idx++] = ' '; // name + tmp_buff[buff_idx++] = 0x00; + tmp_buff[buff_idx++] = header[0x81]; // type + tmp_buff[buff_idx++] = header[0x82]; // size LSB + tmp_buff[buff_idx++] = header[0x83]; // size MSB + tmp_buff[buff_idx++] = header[0x84]; // autostart + + // sector fill + for (int i=0; i<10 ; i++) + tmp_buff[buff_idx++] = 0x00; + + tmp_buff[buff_idx++] = 0x00; // file version + tmp_buff[buff_idx++] = 0x00; // no last sector + + // updates the header CRC + UINT16 crc = tvc64_calc_crc(tmp_buff, buff_idx); + tmp_buff[buff_idx++] = crc & 0xff; + tmp_buff[buff_idx++] = (crc>>8) & 0xff; + + // 2 sec silence + tvc64_emit_level(cassette, time, 2, 0); + + // 10240 pre data cycles + tvc64_output_predata(cassette, time, 10240); + + // 1 synchro cycle + tvc64_emit_level(cassette, time, TVC64_SYNC_FREQ*2, +1); + tvc64_emit_level(cassette, time, TVC64_SYNC_FREQ*2, -1); + + // header data + for (int i=0; i<buff_idx; i++) + tvc64_output_byte(cassette, time, tmp_buff[i]); + + // 5 post data cycles + tvc64_output_predata(cassette, time, 5); + + // 1 sec silence + tvc64_emit_level(cassette, time, 1, 0); + + // 5120 pre data cycles + tvc64_output_predata(cassette, time, 5120); + + // 1 synchro cycle + tvc64_emit_level(cassette, time, TVC64_SYNC_FREQ*2, +1); + tvc64_emit_level(cassette, time, TVC64_SYNC_FREQ*2, -1); + + // first data sector contain the data header + buff_idx = 0; + tmp_buff[buff_idx++] = 0x00; + tmp_buff[buff_idx++] = 0x6a; + tmp_buff[buff_idx++] = 0x00; // data sector + tmp_buff[buff_idx++] = 0x11; // not puffered + tmp_buff[buff_idx++] = 0x00; // not write protected + tmp_buff[buff_idx++] = (UINT8)((cas_size / 256) + ((cas_size % 256) > 0 ? 1 : 0)); // number of sectors + + UINT8 sect_num = 1; + int sector_num = cas_size / 256; + for (int i=0; i<=sector_num; i++) + { + tmp_buff[buff_idx++] = sect_num++; // sector number + + // sector size + if (i == sector_num) + tmp_buff[buff_idx++] = cas_size % 256; + else + tmp_buff[buff_idx++] = 0x00; + + // sector data + int sector_size = (i == sector_num) ? (cas_size % 256) : 256; + for (int z=0; z < sector_size; z++) + cassette_image_read(cassette, &tmp_buff[buff_idx++], TVC64_HEADER_BYTES + i*256 + z, 1); + + if (i == sector_num || ((i+1) == sector_num && (cas_size % 256 ) == 0)) + tmp_buff[buff_idx++] = 0xff; // last sector + else + tmp_buff[buff_idx++] = 0x00; // no last sector + + // sector crc + crc = tvc64_calc_crc(tmp_buff, buff_idx); + tmp_buff[buff_idx++] = crc & 0xff; + tmp_buff[buff_idx++] = (crc>>8) & 0xff; + + // output the sector + for (int z=0; z<buff_idx; z++) + tvc64_output_byte(cassette, time, tmp_buff[z]); + + buff_idx = 0; + } + + // 5 post data cycles + tvc64_output_predata(cassette, time, 5); + + // 1 sec silence + tvc64_emit_level(cassette, time, 1, 0); + + return CASSETTE_ERROR_SUCCESS; +} + +static casserr_t tvc64_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + UINT8 byte; + cassette_image_read(cassette, &byte, 0, 1); + + if (byte == 0x11) + { + opts->bits_per_sample = 16; + opts->channels = 1; + opts->sample_frequency = 44100; + return CASSETTE_ERROR_SUCCESS; + } + + return CASSETTE_ERROR_INVALIDIMAGE; +} + +static const struct CassetteFormat tvc64_cassette_image_format = +{ + "cas", + tvc64_cassette_identify, + tvc64_cassette_load, + NULL +}; + +CASSETTE_FORMATLIST_START(tvc64_cassette_formats) + CASSETTE_FORMAT(tvc64_cassette_image_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/tvc_cas.h b/src/lib/formats/tvc_cas.h new file mode 100644 index 00000000000..7dc7a67e10c --- /dev/null +++ b/src/lib/formats/tvc_cas.h @@ -0,0 +1,16 @@ +/********************************************************************* + + tvc_cas.h + + Format code for Videoton TVC casette files + +*********************************************************************/ + +#ifndef TVC_CAS_H +#define TVC_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(tvc64_cassette_formats); + +#endif /* TVC_CAS_H */ diff --git a/src/lib/formats/wavfile.c b/src/lib/formats/wavfile.c index da3615a9382..2577a908025 100644 --- a/src/lib/formats/wavfile.c +++ b/src/lib/formats/wavfile.c @@ -119,7 +119,7 @@ static casserr_t wavfile_process(cassette_image *cassette, struct CassetteOption switch(opts->bits_per_sample) { case 8: - waveform_flags = CASSETTE_WAVEFORM_8BIT; + waveform_flags = CASSETTE_WAVEFORM_8BIT | CASSETTE_WAVEFORM_UNSIGNED; // 8-bits wav are stored unsigned break; case 16: waveform_flags = CASSETTE_WAVEFORM_16BITLE; diff --git a/src/lib/lib.mak b/src/lib/lib.mak index a2eb8f237c9..3dbe5fcef40 100644 --- a/src/lib/lib.mak +++ b/src/lib/lib.mak @@ -155,6 +155,7 @@ FORMATSOBJS = \ $(LIBOBJ)/formats/trd_dsk.o \ $(LIBOBJ)/formats/trs_cas.o \ $(LIBOBJ)/formats/trs_dsk.o \ + $(LIBOBJ)/formats/tvc_cas.o \ $(LIBOBJ)/formats/tzx_cas.o \ $(LIBOBJ)/formats/uef_cas.o \ $(LIBOBJ)/formats/vg5k_cas.o \ |