summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/adam_cas.c
blob: 1e8e328b517a74cd43dd696758280307c9abdbb7 (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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#include "cassimg.h"
#include "adam_cas.h"

// This code will reproduce the timing of an adam tape played back on a standard tape deck with a 1 7/8ips speed.
#define CASS_AMP 0x1fffffff
#define SPEED_MULTIPLIER 20.0/1.875
#define COL_ADAM_PERIOD  0.00007*SPEED_MULTIPLIER
#define COL_ADAM_PERIOD1 0.000031*SPEED_MULTIPLIER
#define COL_ADAM_PERIOD2 0.000039*SPEED_MULTIPLIER
#define TYPE_HE 0
#define TYPE_GW 1

/***************************************************************
Coleco Adam Digital Data Pack format:
The packs are the same shape as a standard audio cassette, but with different alignment and capstan holes
The tape only has one side and has two widely separated mono data tracks (second track readable on an audio
cassette deck by flipping the tape over and reversing the recorded signal).

Header format:
2 byte header id: 0x48 0x45 (central directory HE type) or 0x47 0x57 (GW type)
2 byte block number
2 byte NOT block number
1 byte number of blocks (0x80)
1 byte sum of the above (checksum)


track format:
leading zeros: ~2753

block format:
sync byte 0x16
header
pad zero bytes ~21
sync byte 0x16
data(1k)
pad zero bytes ~21
sync byte 0x16
2 byte checksum-16
pad zero bytes ~2
sync bytes 0xaa ~922
pad zero bytes ~2

Data is phase encoded, with 70us bit cells with a transition at 31us for a 1 and no transition for a 0

****************************************************************/


static casserr_t coladam_ddp_identify ( cassette_image *cass, struct CassetteOptions *opts )
{
	opts -> bits_per_sample = 16;
	opts -> channels = 2;
	opts -> sample_frequency = 44100;
	return CASSETTE_ERROR_SUCCESS;
}


// Store byte of data
casserr_t coladam_put_byte(cassette_image *cass, int channel, double *time_index, int byte, int *prev_sign)
{
	casserr_t err = CASSETTE_ERROR_SUCCESS;
	for (int i = 0; i < 8; i++)
	{
		if(byte & 0x80)
		{
			err = cassette_put_sample( cass, channel, *time_index, COL_ADAM_PERIOD1, -CASS_AMP*(*prev_sign) );
			(*time_index) += COL_ADAM_PERIOD1;
			err = cassette_put_sample( cass, channel, *time_index, COL_ADAM_PERIOD2,  CASS_AMP*(*prev_sign) );
			(*time_index) += COL_ADAM_PERIOD2;
		}
		else
		{
			err = cassette_put_sample( cass, channel, *time_index, COL_ADAM_PERIOD, -CASS_AMP*(*prev_sign) );
			(*prev_sign) *=-1;
			(*time_index) += COL_ADAM_PERIOD;
		}
		byte <<= 1;
	}
	return err;
}

casserr_t coladam_put_block(cassette_image *cass, int channel, double *time_index, int *prev_sign, int block_index, UINT8 *buffer, int layout_type)
{
	int i, checksum_16=0;
	UINT8 header[] = { 0x16, 0x48, 0x45, 0x00, block_index, 0xff, (0xff - block_index), 0x00, 0x80, 0xf4 };
	casserr_t err;
	if (layout_type == TYPE_GW)
	{
		header[1] = 0x47;
		header[2] = 0x57;
		header[9] = 0xe3;
	}

	for (i = 0; i < 10; i++) // header
	{
		err = coladam_put_byte(cass, channel, time_index, header[i], prev_sign);
	}
	for (i = 0; i < 21; i++) // leading zero bytes
	{
		err = coladam_put_byte(cass, channel, time_index, 0x00, prev_sign);
	}
	err = coladam_put_byte(cass, channel, time_index, 0x16, prev_sign); // data start
	for ( i = 0; i < 0x400; i++ )
	{
		err = coladam_put_byte(cass, channel, time_index, buffer[i], prev_sign);
		checksum_16 += buffer[i];
	}
	for (i = 0; i < 21; i++) // trailing padding zeros
	{
		err = coladam_put_byte(cass, channel, time_index, 0x00, prev_sign);
	}
	err = coladam_put_byte(cass, channel, time_index, 0x16, prev_sign);
	err = coladam_put_byte(cass, channel, time_index, (checksum_16 & 0xff00) >> 8, prev_sign); // write checksum
	err = coladam_put_byte(cass, channel, time_index, (checksum_16 & 0xff), prev_sign);
	for (i = 0; i < 922; i++) // sync bytes
	{
		err = coladam_put_byte(cass, channel, time_index, 0xaa, prev_sign);
	}
	err = coladam_put_byte(cass, channel, time_index, 0x00, prev_sign);
	err = coladam_put_byte(cass, channel, time_index, 0x00, prev_sign);
	return err;
}


static casserr_t coladam_ddp_load( cassette_image *cass )
{
	double time = 0.;
	int i, block, prev_sign=-1;
	UINT8 buffer[0x400];
	casserr_t err = CASSETTE_ERROR_SUCCESS;

	// It would appear that data packs that originally had the type GW data layout and headers work fine when converted to type
	// HE. Thus we set all tapes to type HE.

	int layout_type = TYPE_HE;

	// Track 0
	for ( i = 0; i < 2753; i++ ) // leading zero bytes
	{
		err = coladam_put_byte(cass, 0, &time, 0x00, &prev_sign);
	}

	for (block = 0; block < 128; block++)
	{
		cassette_image_read( cass, buffer, 0x20000+0x400*block, 0x400 );
		err = coladam_put_block(cass, 0, &time, &prev_sign, block, buffer, layout_type);
	}
	for (block = 128; block < 131; block++)
	{
		cassette_image_read( cass, buffer, 0x3f400+0x400*(block-128), 0x400 );
		err = coladam_put_block(cass, 0, &time, &prev_sign, block, buffer, layout_type);
	}

	// Track 1
	time = 0.;
	for ( i = 0; i < 2753; i++ ) // leading zero bytes
	{
		err = coladam_put_byte(cass, 1, &time, 0x00, &prev_sign);
	}
	if (layout_type == TYPE_HE)
	{
		for (block = 0; block < 64; block++)
		{
			cassette_image_read( cass, buffer, 0x10000+0x400*block, 0x400 );
			err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type);
		}
		for (block = 64; block < 128; block++)
		{
			cassette_image_read( cass, buffer, 0x00000+0x400*(block-64), 0x400 );
			err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type);
		}
		for (block = 128; block < 131; block++)
		{
			cassette_image_read( cass, buffer, 0x0f400+0x400*(block-128), 0x400 );
			err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type);
		}
	}
	else
	{
		time = 0;
		for ( i = 0; i < 2753; i++ ) // leading zero bytes
		{
			err = coladam_put_byte(cass, 1, &time, 0x00, &prev_sign);
		}
		for (block = 0; block < 128; block++)
		{
			cassette_image_read( cass, buffer, 0x400*block, 0x400 );
			err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type);
		}
		for (block = 128; block < 131; block++)
		{
			cassette_image_read( cass, buffer, 0x1f400+0x400*(block-128), 0x400 );
			err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type);
		}
	}

	return err;
}



static const struct CassetteFormat coladam_ddp =
{ "ddp", coladam_ddp_identify, coladam_ddp_load, NULL /* no save */ };

CASSETTE_FORMATLIST_START(coleco_adam_cassette_formats)
	CASSETTE_FORMAT(coladam_ddp)
CASSETTE_FORMATLIST_END