summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/lib/formats/oric_tap.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/lib/formats/oric_tap.c')
-rw-r--r--trunk/src/lib/formats/oric_tap.c519
1 files changed, 519 insertions, 0 deletions
diff --git a/trunk/src/lib/formats/oric_tap.c b/trunk/src/lib/formats/oric_tap.c
new file mode 100644
index 00000000000..3f69ab3b034
--- /dev/null
+++ b/trunk/src/lib/formats/oric_tap.c
@@ -0,0 +1,519 @@
+#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 separate 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