mame/src/lib/formats/oric_tap.cpp
Miodrag Milanovic ddb290d5f6 NOTICE (TYPE NAME CONSOLIDATION)
Use standard uint64_t, uint32_t, uint16_t or uint8_t instead of UINT64, UINT32, UINT16 or UINT8
also use standard int64_t, int32_t, int16_t or int8_t instead of INT64, INT32, INT16 or INT8
2016-10-22 13:13:17 +02:00

524 lines
11 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Kevin Thacker
#include <assert.h>
#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_t *oric_emit_level(int16_t *p, int count, int16_t wave_state)
{
int i;
for (i=0; i<count; i++)
{
*(p++) = wave_state;
}
return p;
}
/* 4 periods at 1200Hz */
static int16_t* oric_output_bit(int16_t *p, uint8_t 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_t 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_t byte)
{
int count;
int i;
uint8_t parity;
uint8_t 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_t 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_t *oric_output_byte(int16_t *p, uint8_t byte)
{
int i;
uint8_t parity;
uint8_t 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_t 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_t *oric_fill_pause(int16_t *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_t *bytes, int length)
{
unsigned char header[9];
int count;
const uint8_t *data_ptr;
int i;
uint8_t 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_t 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_t *buffer, int length, uint8_t *bytes)
{
unsigned char header[9];
uint8_t *data_ptr;
int16_t *p;
int i;
uint8_t 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_t 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 cassette_image::error oric_tap_identify(cassette_image *cassette, struct CassetteOptions *opts)
{
return cassette_legacy_identify(cassette, opts, &oric_legacy_fill_wave);
}
static cassette_image::error 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,
nullptr
};
CASSETTE_FORMATLIST_START(oric_cassette_formats)
CASSETTE_FORMAT(oric_tap_format)
CASSETTE_FORMATLIST_END