/src/lib/formats/oric_tap.c
https://github.com/groovybits/groovymame · C · 519 lines · 343 code · 107 blank · 69 comment · 35 complexity · 595d3c13cba4c372605a61c7957272bf MD5 · raw file
- #include "oric_tap.h"
-
- #define ORIC_WAV_DEBUG 0
- #define LOG(x) do { if (ORIC_WAV_DEBUG) printf x; } while (0)
-
- /* this code based heavily on tap2wav by Fabrice Frances */
- #define ORIC_SYNC_BYTE 0x016
-
- /* frequency of wave */
- /* tapes use 1200Hz and 2400Hz samples */
- #define ORIC_WAV_FREQUENCY 4800
-
- /* 13 bits define a byte on the cassette */
- /* 1 start bit, 8 data bits, 1 parity bit and 3 stop bits */
- #define ORIC_BYTE_TO_BITS_ON_CASSETTE 13
-
- #define ORIC_WAVESAMPLES_HEADER 3000
- #define ORIC_WAVESAMPLES_TRAILER 1000
-
- enum
- {
- ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE,
- ORIC_CASSETTE_GOT_SYNC_BYTE,
- ORIC_CASSETTE_READ_HEADER,
- ORIC_CASSETTE_READ_FILENAME,
- ORIC_CASSETTE_WRITE_DATA
- };
-
- #define WAVEENTRY_LOW -32768
- #define WAVEENTRY_HIGH 32767
- #define WAVEENTRY_NULL 0
-
- #define ORIC_LEADER_LENGTH 512
-
- struct oric_t
- {
- int cassette_state;
- int data_count;
- int data_length;
- int tap_size;
- };
- static oric_t oric;
-
- /* to write a bit to the tape, the rom routines output either 4 periods at 1200 Hz for a 0 or 8 periods at 2400 Hz for a 1 */
- /* 4800 is twice 2400Hz */
-
- /* 8 periods at 2400Hz */
- /* hi,lo, hi,lo, hi,lo, hi,lo */
-
- static INT16 *oric_emit_level(INT16 *p, int count, INT16 wave_state)
- {
- int i;
-
- for (i=0; i<count; i++)
- {
- *(p++) = wave_state;
- }
- return p;
- }
-
- /* 4 periods at 1200Hz */
- static INT16* oric_output_bit(INT16 *p, UINT8 b)
- {
- p = oric_emit_level(p, 1, WAVEENTRY_HIGH);
- p = oric_emit_level(p, b ? 1 : 2, WAVEENTRY_LOW);
-
- return p;
- }
-
- static int oric_get_bit_size_in_samples(UINT8 b)
- {
- int count;
-
- count = 1;
-
- if (b)
- {
- count++;
- }
- else
- {
- count+=2;
- }
-
- return count;
- }
-
-
- /* each byte on cassette is stored as:
-
- start bit 0 * 1
- data bits 8 * x (x is 0 or 1, and depends on data-bit value)
- parity bit 1 * x (x is 0 or 1, and depends on the parity of the data bits)
- stop bits 1 * 3
-
- if data has even parity, parity bit will be 1.
- if data has odd parity, parity bit will be 0.
- */
-
- /*
- 512 * data byte 0x016 -> leader
- 1 * data byte 0x024 -> sync byte
- 9 * data byte -> header
- delay (of last pulse written)
- x * data byte -> length
-
-
- header structure:
- 3 * ? -> ???
- 1 * ? -> ???
- 1 * x -> end address high byte
- 1 * x -> end address low byte
- 1 * x -> start address high byte
- 1 * x -> start address low byte
- 1 * ? -> ???
- */
-
- static int oric_calculate_byte_size_in_samples(UINT8 byte)
- {
- int count;
- int i;
- UINT8 parity;
- UINT8 data;
-
- count = 0;
-
-
- /* start bit */
- count+=oric_get_bit_size_in_samples(0);
-
- /* set initial parity */
- parity = 1;
-
- /* data bits, written bit 0, bit 1...bit 7 */
- data = byte;
- for (i=0; i<8; i++)
- {
- UINT8 data_bit;
-
- data_bit = data & 0x01;
-
- parity = parity + data_bit;
-
- count+=oric_get_bit_size_in_samples(data_bit);
- data = data>>1;
- }
-
- /* parity */
- count+=oric_get_bit_size_in_samples((parity & 0x01));
-
- /* stop bits */
- count+=oric_get_bit_size_in_samples(1);
- count+=oric_get_bit_size_in_samples(1);
- count+=oric_get_bit_size_in_samples(1);
- count+=oric_get_bit_size_in_samples(1);
-
- return count;
- }
-
-
- static INT16 *oric_output_byte(INT16 *p, UINT8 byte)
- {
- int i;
- UINT8 parity;
- UINT8 data;
-
- /* start bit */
- p = oric_output_bit(p, 0);
-
- /* set initial parity */
- parity = 1;
-
- /* data bits, written bit 0, bit 1...bit 7 */
- data = byte;
- for (i=0; i<8; i++)
- {
- UINT8 data_bit;
-
- data_bit = data & 0x01;
-
- parity = parity + data_bit;
-
- p = oric_output_bit(p, data_bit);
- data = data>>1;
- }
-
- /* parity */
- p = oric_output_bit(p, parity & 0x01);
-
- /* stop bits */
- p = oric_output_bit(p, 1);
- p = oric_output_bit(p, 1);
- p = oric_output_bit(p, 1);
- p = oric_output_bit(p, 1);
-
- return p;
- }
-
- static INT16 *oric_fill_pause(INT16 *p, int sample_count)
- {
- int i;
-
- for (i=0; i<sample_count; i++)
- {
- *(p++) = WAVEENTRY_NULL;
- }
-
- return p;
- }
-
- static int oric_seconds_to_samples(float seconds)
- {
- return (int)((float)seconds*(float)ORIC_WAV_FREQUENCY);
- }
-
- /* length is length of .tap file! */
- static int oric_cassette_calculate_size_in_samples(const UINT8 *bytes, int length)
- {
- unsigned char header[9];
- int count;
-
- const UINT8 *data_ptr;
- int i;
- UINT8 data;
-
- oric.tap_size = length;
-
- oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE;
- count = 0;
- data_ptr = bytes;
-
- while (data_ptr<(bytes+length))
- {
- data = data_ptr[0];
- data_ptr++;
-
- switch (oric.cassette_state)
- {
- case ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE:
- {
- if (data==ORIC_SYNC_BYTE)
- {
- LOG_FORMATS("found sync byte!\n");
- /* found first sync byte */
- oric.cassette_state = ORIC_CASSETTE_GOT_SYNC_BYTE;
- }
- }
- break;
-
- case ORIC_CASSETTE_GOT_SYNC_BYTE:
- {
- if (data!=ORIC_SYNC_BYTE)
- {
- /* 0.25 second pause */
- count += oric_seconds_to_samples(0.25);
-
- LOG_FORMATS("found end of sync bytes!\n");
-
- /* oric writes approx 512 bytes */
- /* found end of sync bytes */
- for (i=0; i<ORIC_LEADER_LENGTH; i++)
- {
- count+=oric_calculate_byte_size_in_samples(0x016);
- }
-
- if (data==0x024)
- {
- //LOG_FORMATS("reading header!\n");
- count+=oric_calculate_byte_size_in_samples(0x024);
-
- oric.cassette_state = ORIC_CASSETTE_READ_HEADER;
- oric.data_count = 0;
- oric.data_length = 9;
- }
- }
- }
- break;
-
- case ORIC_CASSETTE_READ_HEADER:
- {
- header[oric.data_count] = data;
- count+=oric_calculate_byte_size_in_samples(data);
-
- oric.data_count++;
-
- if (oric.data_count==oric.data_length)
- {
- //LOG_FORMATS("finished reading header!\n");
- oric.cassette_state = ORIC_CASSETTE_READ_FILENAME;
- }
- }
- break;
-
- case ORIC_CASSETTE_READ_FILENAME:
- {
- count+=oric_calculate_byte_size_in_samples(data);
-
- /* got end of filename? */
- if (data==0)
- {
- UINT16 end, start;
- LOG_FORMATS("got end of filename\n");
-
- /* 100 1 bits to seperate header from data */
- for (i=0; i<100; i++)
- {
- count+=oric_get_bit_size_in_samples(1);
- }
-
- oric.cassette_state = ORIC_CASSETTE_WRITE_DATA;
- oric.data_count = 0;
-
- end = (((header[4] & 0x0ff)<<8) | (header[5] & 0x0ff));
- start = (((header[6] & 0x0ff)<<8) | (header[7] & 0x0ff));
- LOG(("start (from header): %02x\n",start));
- LOG(("end (from header): %02x\n",end));
- oric.data_length = end - start + 1;
- }
- }
- break;
-
- case ORIC_CASSETTE_WRITE_DATA:
- {
- count+=oric_calculate_byte_size_in_samples(data);
- oric.data_count++;
-
- if (oric.data_count==oric.data_length)
- {
- LOG_FORMATS("finished writing data!\n");
- oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE;
- }
- }
- break;
-
- }
- }
-
- return count;
- }
-
- /* length is length of sample buffer to fill! */
- static int oric_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes)
- {
- unsigned char header[9];
- UINT8 *data_ptr;
- INT16 *p;
- int i;
- UINT8 data;
-
- p = buffer;
-
-
- /* header and trailer act as pauses */
- /* the trailer is required so that the via sees the last bit of the last
- byte */
- if (bytes == CODE_HEADER) {
- for (i = 0; i < ORIC_WAVESAMPLES_HEADER; i++)
- *(p++) = WAVEENTRY_NULL;
- }
- else if (bytes == CODE_TRAILER) {
- for (i = 0; i < ORIC_WAVESAMPLES_TRAILER; i++)
- *(p++) = WAVEENTRY_NULL;
- }
- else
- {
- /* the length is the number of samples left in the buffer and NOT the number of bytes for the input file */
- length = length - ORIC_WAVESAMPLES_TRAILER;
-
- oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE;
- data_ptr = bytes;
-
- while ((data_ptr<(bytes + oric.tap_size)) && (p < (buffer+length)) )
- {
- data = data_ptr[0];
- data_ptr++;
-
- switch (oric.cassette_state)
- {
- case ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE:
- {
- if (data==ORIC_SYNC_BYTE)
- {
- LOG_FORMATS("found sync byte!\n");
- /* found first sync byte */
- oric.cassette_state = ORIC_CASSETTE_GOT_SYNC_BYTE;
- }
- }
- break;
-
- case ORIC_CASSETTE_GOT_SYNC_BYTE:
- {
- if (data!=ORIC_SYNC_BYTE)
- {
- /* 0.25 second pause */
- p = oric_fill_pause(p, oric_seconds_to_samples(0.25));
-
- LOG_FORMATS("found end of sync bytes!\n");
- /* found end of sync bytes */
- for (i=0; i<ORIC_LEADER_LENGTH; i++)
- {
- p = oric_output_byte(p,0x016);
- }
-
- if (data==0x024)
- {
- //LOG_FORMATS("reading header!\n");
- p = oric_output_byte(p,data);
- oric.cassette_state = ORIC_CASSETTE_READ_HEADER;
- oric.data_count = 0;
- oric.data_length = 9;
- }
- }
- }
- break;
-
- case ORIC_CASSETTE_READ_HEADER:
- {
- header[oric.data_count] = data;
- p = oric_output_byte(p, data);
- oric.data_count++;
-
- if (oric.data_count==oric.data_length)
- {
- //LOG_FORMATS("finished reading header!\n");
- oric.cassette_state = ORIC_CASSETTE_READ_FILENAME;
- }
- }
- break;
-
- case ORIC_CASSETTE_READ_FILENAME:
- {
- p = oric_output_byte(p, data);
-
- /* got end of filename? */
- if (data==0)
- {
- UINT16 end, start;
- LOG_FORMATS("got end of filename\n");
-
- /* oric includes a small delay, but I don't see
- it being 1 bits */
- for (i=0; i<100; i++)
- {
- p = oric_output_bit(p,1);
- }
-
- oric.cassette_state = ORIC_CASSETTE_WRITE_DATA;
- oric.data_count = 0;
-
- end = (((header[4] & 0x0ff)<<8) | (header[5] & 0x0ff));
- start = (((header[6] & 0x0ff)<<8) | (header[7] & 0x0ff));
- LOG(("start (from header): %02x\n",start));
- LOG(("end (from header): %02x\n",end));
- oric.data_length = end - start + 1;
- }
- }
- break;
-
- case ORIC_CASSETTE_WRITE_DATA:
- {
- p = oric_output_byte(p, data);
- oric.data_count++;
-
- if (oric.data_count==oric.data_length)
- {
- LOG_FORMATS("finished writing data!\n");
- oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE;
- }
- }
- break;
-
- }
- }
- }
- return p - buffer;
- }
-
-
-
- static const struct CassetteLegacyWaveFiller oric_legacy_fill_wave =
- {
- oric_cassette_fill_wave, /* fill_wave */
- -1, /* chunk_size */
- 0, /* chunk_samples */
- oric_cassette_calculate_size_in_samples, /* chunk_sample_calc */
- ORIC_WAV_FREQUENCY, /* sample_frequency */
- ORIC_WAVESAMPLES_HEADER, /* header_samples */
- ORIC_WAVESAMPLES_TRAILER /* trailer_samples */
- };
-
-
-
- static casserr_t oric_tap_identify(cassette_image *cassette, struct CassetteOptions *opts)
- {
- return cassette_legacy_identify(cassette, opts, &oric_legacy_fill_wave);
- }
-
-
-
- static casserr_t oric_tap_load(cassette_image *cassette)
- {
- return cassette_legacy_construct(cassette, &oric_legacy_fill_wave);
- }
-
-
-
- static const struct CassetteFormat oric_tap_format =
- {
- "tap",
- oric_tap_identify,
- oric_tap_load,
- NULL
- };
-
-
-
- CASSETTE_FORMATLIST_START(oric_cassette_formats)
- CASSETTE_FORMAT(oric_tap_format)
- CASSETTE_FORMATLIST_END