summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/pmd_pmd.c
blob: 2d6cf67f3782aa886593cd31be81ecb070e5f50b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/* .PMD tape images */
#include "pmd_pmd.h"

#define WAVEENTRY_LOW  -32768
#define WAVEENTRY_HIGH  32767

#define PMD85_WAV_FREQUENCY	7200
#define PMD85_TIMER_FREQUENCY	1200
#define PMD85_BIT_LENGTH	(PMD85_WAV_FREQUENCY/PMD85_TIMER_FREQUENCY)
#define PMD85_PILOT_BITS	(PMD85_TIMER_FREQUENCY*3)
#define PMD85_PAUSE_BITS	(PMD85_TIMER_FREQUENCY/2)
#define PMD85_HEADER_BYTES	63
#define PMD85_BITS_PER_BYTE	11

static INT16 *pmd85_emit_level(INT16 *p, int count, int level)
{
	int i;

	for (i=0; i<count; i++)	*(p++) = level;

	return p;
}

static INT16* pmd85_output_bit(INT16 *p, UINT8 bit)
{
	if (bit)
	{
		p = pmd85_emit_level (p, PMD85_BIT_LENGTH/2, WAVEENTRY_LOW);
		p = pmd85_emit_level (p, PMD85_BIT_LENGTH/2, WAVEENTRY_HIGH);
	}
	else
	{
		p = pmd85_emit_level (p, PMD85_BIT_LENGTH/2, WAVEENTRY_HIGH);
		p = pmd85_emit_level (p, PMD85_BIT_LENGTH/2, WAVEENTRY_LOW);
	}
    	return p;
}

static INT16* pmd85_output_byte(INT16 *p, UINT8 byte)
{
	int i;

	/* start */
	p = pmd85_output_bit (p, 0);

	/* data */
	for (i=0; i<8; i++)
		p = pmd85_output_bit(p,(byte>>(7-i)) & 0x01);

	/* stop */
	p = pmd85_output_bit (p, 1);
	p = pmd85_output_bit (p, 1);

	return p;
}

static int pmd85_cassette_calculate_size_in_samples(const UINT8 *bytes, int length)
{
	return PMD85_BIT_LENGTH * (length * PMD85_BITS_PER_BYTE + PMD85_PILOT_BITS + PMD85_PAUSE_BITS);
}

static int pmd85_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes)
{
	int i;
	INT16 * p = buffer;

	int data_size = ((length/PMD85_BIT_LENGTH-PMD85_PILOT_BITS-PMD85_PAUSE_BITS)/PMD85_BITS_PER_BYTE)-PMD85_HEADER_BYTES;

	/* pilot */
	for (i=0; i<PMD85_PILOT_BITS; i++)
		p = pmd85_output_bit(p, 1);

	/* header */
	for (i=0; i<PMD85_HEADER_BYTES; i++)
		p = pmd85_output_byte(p, bytes[i]);

	/* pause */
	for (i=0; i<PMD85_PAUSE_BITS; i++)
		p = pmd85_output_bit(p, 1);

	/* data */
	for (i=0; i<data_size; i++)
		p = pmd85_output_byte(p, bytes[i+PMD85_HEADER_BYTES]);

	return p - buffer;
}

static const struct CassetteLegacyWaveFiller pmd85_legacy_fill_wave =
{
	pmd85_cassette_fill_wave,			/* fill_wave */
	-1,											/* chunk_size */
	0,											/* chunk_samples */
	pmd85_cassette_calculate_size_in_samples,	/* chunk_sample_calc */
	PMD85_WAV_FREQUENCY,										/* sample_frequency */
	0,											/* header_samples */
	0											/* trailer_samples */
};

static casserr_t pmd85_pmd_identify(cassette_image *cassette, struct CassetteOptions *opts)
{
	return cassette_legacy_identify(cassette, opts, &pmd85_legacy_fill_wave);
}

static casserr_t pmd85_pmd_load(cassette_image *cassette)
{
	return cassette_legacy_construct(cassette, &pmd85_legacy_fill_wave);
}

static const struct CassetteFormat pmd85_pmd_image_format =
{
	"pmd",
	pmd85_pmd_identify,
	pmd85_pmd_load,
	NULL
};

CASSETTE_FORMATLIST_START(pmd85_pmd_format)
	CASSETTE_FORMAT(pmd85_pmd_image_format)
CASSETTE_FORMATLIST_END