1 // license:BSD-3-Clause
2 // copyright-holders:Sandro Ronco
3 /********************************************************************
4 
5     Support for KC85 cassette images
6 
7     Supported formats:
8     - kcc: raw cassette image without ID and checksum
9     - tap: cassette image from KC-Emulator with head and ID
10     - tp2: cassette image with ID and checksum (130 bytes block)
11     - kcm: same as tp2 but without head
12     - sss: BASIC data without head (miss the first 11 bytes)
13 
14 ********************************************************************/
15 
16 #include "kc_cas.h"
17 
18 #include <cassert>
19 
20 #define SMPLO       -32768
21 #define SMPHI       32767
22 #define SILENCE     0
23 
24 #define KC_WAV_FREQUENCY        44100
25 
26 // from documentation
27 #define FREQ_BIT_0          2400
28 #define FREQ_BIT_1          1200
29 #define FREQ_SEPARATOR      600
30 
31 // file formats
32 enum
33 {
34 	KC_IMAGE_KCC,
35 	KC_IMAGE_TP2,
36 	KC_IMAGE_TAP,
37 	KC_IMAGE_KCM
38 };
39 
40 // image size
41 static int kc_image_size; // FIXME: global variable prevents multiple instances
42 
43 /*******************************************************************
44    Generate one high-low cycle of sample data
45 ********************************************************************/
kc_cas_cycle(int16_t * buffer,int sample_pos,int len)46 static inline int kc_cas_cycle(int16_t *buffer, int sample_pos, int len)
47 {
48 	int num_samples = KC_WAV_FREQUENCY / (len * 2);
49 
50 	if (buffer)
51 	{
52 		for (int i=0; i<num_samples; i++)
53 			buffer[ sample_pos + i ] = SMPHI;
54 
55 		for (int i=0; i<num_samples; i++)
56 			buffer[ sample_pos + num_samples + i ] = SMPLO;
57 	}
58 
59 	return num_samples * 2;
60 }
61 
62 
63 /*******************************************************************
64    Generate n samples of silence
65 ********************************************************************/
kc_cas_silence(int16_t * buffer,int sample_pos,int len)66 static inline int kc_cas_silence(int16_t *buffer, int sample_pos, int len)
67 {
68 	int i = 0;
69 
70 	if ( buffer )
71 		for( i = 0; i < len; i++)
72 			buffer[ sample_pos + i ] = SILENCE;
73 
74 	return len;
75 }
76 
77 
78 /*******************************************************************
79    Generate samples for 1 byte
80 ********************************************************************/
kc_cas_byte(int16_t * buffer,int sample_pos,uint8_t data)81 static inline int kc_cas_byte(int16_t *buffer, int sample_pos, uint8_t data)
82 {
83 	int samples = 0;
84 
85 	// write the byte
86 	for ( int i = 0; i < 8; i++ )
87 	{
88 		if ( data & 0x01 )
89 		{
90 			samples += kc_cas_cycle( buffer, sample_pos + samples, FREQ_BIT_1 );
91 		}
92 		else
93 		{
94 			samples += kc_cas_cycle( buffer, sample_pos + samples, FREQ_BIT_0 );
95 		}
96 
97 		data >>= 1;
98 	}
99 
100 	// byte separator
101 	samples += kc_cas_cycle( buffer, sample_pos + samples, FREQ_SEPARATOR);
102 
103 	return samples;
104 }
105 
kc_handle_cass(int16_t * buffer,const uint8_t * casdata,int type)106 static int kc_handle_cass(int16_t *buffer, const uint8_t *casdata, int type)
107 {
108 	int data_pos = (type == KC_IMAGE_KCC || type == KC_IMAGE_KCM) ? 0 : 16;
109 	int sample_count = 0;
110 	int block_id = 1;
111 
112 	// 1 sec of silence at start
113 	sample_count += kc_cas_silence(buffer, sample_count, KC_WAV_FREQUENCY);
114 
115 	// 8000 cycles of BIT_1 for synchronization
116 	for (int i=0; i<8000; i++)
117 		sample_count += kc_cas_cycle( buffer, sample_count, FREQ_BIT_1);
118 
119 	// on the entire file
120 	while( data_pos < kc_image_size )
121 	{
122 		uint8_t checksum = 0;
123 
124 		// 200 cycles of BIT_1 every block
125 		for (int i=0; i<200; i++)
126 			sample_count += kc_cas_cycle( buffer, sample_count, FREQ_BIT_1);
127 
128 		// separator
129 		sample_count += kc_cas_cycle( buffer, sample_count, FREQ_SEPARATOR);
130 
131 		// in TAP and TP2 file the first byte is the ID
132 		if (type == KC_IMAGE_TAP || type == KC_IMAGE_TP2 || type == KC_IMAGE_KCM)
133 			block_id = casdata[data_pos++];
134 
135 		// is the last block ?
136 		if (data_pos + 128 >= kc_image_size && type == KC_IMAGE_KCC)
137 			block_id = 0xff;
138 
139 		// write the block ID
140 		sample_count += kc_cas_byte( buffer, sample_count, block_id );
141 
142 		// write the 128 bytes of the block
143 		for (int i=0; i<128; i++)
144 		{
145 			uint8_t data = 0;
146 
147 			if (data_pos < kc_image_size)
148 				data = casdata[data_pos++];
149 
150 			// calculate the checksum
151 			checksum += data;
152 
153 			// write a byte
154 			sample_count += kc_cas_byte( buffer, sample_count, data );
155 		}
156 
157 		// TP2 and KCM files also have the checksum byte
158 		if (type == KC_IMAGE_TP2 || type == KC_IMAGE_KCM)
159 			checksum = casdata[data_pos++];
160 
161 		// 8bit checksum
162 		sample_count += kc_cas_byte( buffer, sample_count, checksum );
163 
164 		// more TAP and TP2 can be combined into the same file
165 		if ((type == KC_IMAGE_TAP || type == KC_IMAGE_TP2) && block_id == 0xff && data_pos < kc_image_size)
166 		{
167 			if (casdata[data_pos] == 0xc3 || casdata[data_pos] == 0x4b)
168 			{
169 				sample_count += kc_cas_silence(buffer, sample_count, KC_WAV_FREQUENCY/10);
170 
171 				data_pos += 16;
172 			}
173 		}
174 
175 		block_id++;
176 	}
177 
178 	sample_count += kc_cas_cycle( buffer, sample_count, FREQ_SEPARATOR);
179 
180 	// 1 sec of silence
181 	sample_count += kc_cas_silence(buffer, sample_count, KC_WAV_FREQUENCY);
182 
183 	return sample_count;
184 }
185 
186 
kc_handle_kcc(int16_t * buffer,const uint8_t * casdata)187 static int kc_handle_kcc(int16_t *buffer, const uint8_t *casdata)
188 {
189 	return kc_handle_cass(buffer, casdata, KC_IMAGE_KCC);
190 }
191 
192 
kc_handle_tap(int16_t * buffer,const uint8_t * casdata)193 static int kc_handle_tap(int16_t *buffer, const uint8_t *casdata)
194 {
195 	if (!strncmp((const char *)(casdata + 1), "KC-TAPE by AF", 13))
196 	{
197 		return kc_handle_cass(buffer, casdata, KC_IMAGE_TAP);
198 	}
199 	else if (!strncmp((const char *)(casdata), "KC85", 4))
200 	{
201 		return kc_handle_cass(buffer, casdata, KC_IMAGE_TP2);
202 	}
203 	else if (casdata[0] == 0x01)
204 	{
205 		return kc_handle_cass(buffer, casdata, KC_IMAGE_KCM);
206 	}
207 	else
208 	{
209 		return (int)cassette_image::error::INVALID_IMAGE;
210 	}
211 }
212 
kc_handle_sss(int16_t * buffer,const uint8_t * casdata)213 static int kc_handle_sss(int16_t *buffer, const uint8_t *casdata)
214 {
215 	std::vector<uint8_t> sss(kc_image_size + 11);
216 
217 	// tries to generate the missing head
218 	memset(&sss[0], 0xd3, 3);
219 	memset(&sss[3], 0x20, 8);
220 	memcpy(&sss[11], casdata, kc_image_size);
221 
222 	// set an arbitrary filename
223 	sss[3] = 'A';
224 
225 	int retval = kc_handle_cass(buffer, &sss[0], KC_IMAGE_KCC);
226 
227 	return retval;
228 }
229 
230 
231 
232 /*******************************************************************
233    Generate samples for the tape image
234 ********************************************************************/
kc_kcc_fill_wave(int16_t * buffer,int sample_count,uint8_t * bytes)235 static int kc_kcc_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
236 {
237 	return kc_handle_kcc(buffer, bytes);
238 }
239 
240 
241 /*******************************************************************
242    Calculate the number of samples needed for this tape image classical
243 ********************************************************************/
kc_kcc_to_wav_size(const uint8_t * casdata,int caslen)244 static int kc_kcc_to_wav_size(const uint8_t *casdata, int caslen)
245 {
246 	kc_image_size = caslen ;
247 
248 	return kc_handle_kcc( nullptr, casdata );
249 }
250 
251 
252 static const cassette_image::LegacyWaveFiller kc_kcc_legacy_fill_wave =
253 {
254 	kc_kcc_fill_wave,                       /* fill_wave */
255 	-1,                                     /* chunk_size */
256 	0,                                      /* chunk_samples */
257 	kc_kcc_to_wav_size,                     /* chunk_sample_calc */
258 	KC_WAV_FREQUENCY,                       /* sample_frequency */
259 	0,                                      /* header_samples */
260 	0                                       /* trailer_samples */
261 };
262 
kc_kcc_identify(cassette_image * cassette,cassette_image::Options * opts)263 static cassette_image::error kc_kcc_identify(cassette_image *cassette, cassette_image::Options *opts)
264 {
265 	return cassette->legacy_identify(opts, &kc_kcc_legacy_fill_wave);
266 }
267 
268 
kc_kcc_load(cassette_image * cassette)269 static cassette_image::error kc_kcc_load(cassette_image *cassette)
270 {
271 	return cassette->legacy_construct(&kc_kcc_legacy_fill_wave);
272 }
273 
274 
275 static const cassette_image::Format kc_kcc_format =
276 {
277 	"kcc,kcb",
278 	kc_kcc_identify,
279 	kc_kcc_load,
280 	nullptr
281 };
282 
283 
284 /*******************************************************************
285    Generate samples for the tape image
286 ********************************************************************/
kc_tap_fill_wave(int16_t * buffer,int sample_count,uint8_t * bytes)287 static int kc_tap_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
288 {
289 	return kc_handle_tap(buffer, bytes);
290 }
291 
292 
293 /*******************************************************************
294    Calculate the number of samples needed for this tape image classical
295 ********************************************************************/
kc_tap_to_wav_size(const uint8_t * casdata,int caslen)296 static int kc_tap_to_wav_size(const uint8_t *casdata, int caslen)
297 {
298 	kc_image_size = caslen ;
299 
300 	return kc_handle_tap( nullptr, casdata );
301 }
302 
303 
304 static const cassette_image::LegacyWaveFiller kc_tap_legacy_fill_wave =
305 {
306 	kc_tap_fill_wave,                       /* fill_wave */
307 	-1,                                     /* chunk_size */
308 	0,                                      /* chunk_samples */
309 	kc_tap_to_wav_size,                     /* chunk_sample_calc */
310 	KC_WAV_FREQUENCY,                       /* sample_frequency */
311 	0,                                      /* header_samples */
312 	0                                       /* trailer_samples */
313 };
314 
kc_tap_identify(cassette_image * cassette,cassette_image::Options * opts)315 static cassette_image::error kc_tap_identify(cassette_image *cassette, cassette_image::Options *opts)
316 {
317 	return cassette->legacy_identify(opts, &kc_tap_legacy_fill_wave);
318 }
319 
320 
kc_tap_load(cassette_image * cassette)321 static cassette_image::error kc_tap_load(cassette_image *cassette)
322 {
323 	return cassette->legacy_construct(&kc_tap_legacy_fill_wave);
324 }
325 
326 
327 static const cassette_image::Format kc_tap_format =
328 {
329 	"tap,853,854,855,tp2,kcm",
330 	kc_tap_identify,
331 	kc_tap_load,
332 	nullptr
333 };
334 
335 
336 /*******************************************************************
337    Generate samples for the tape image
338 ********************************************************************/
kc_sss_fill_wave(int16_t * buffer,int sample_count,uint8_t * bytes)339 static int kc_sss_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
340 {
341 	return kc_handle_sss(buffer, bytes);
342 }
343 
344 
345 /*******************************************************************
346    Calculate the number of samples needed for this tape image classical
347 ********************************************************************/
kc_sss_to_wav_size(const uint8_t * casdata,int caslen)348 static int kc_sss_to_wav_size(const uint8_t *casdata, int caslen)
349 {
350 	kc_image_size = caslen ;
351 
352 	return kc_handle_sss( nullptr, casdata );
353 }
354 
355 
356 static const cassette_image::LegacyWaveFiller kc_sss_legacy_fill_wave =
357 {
358 	kc_sss_fill_wave,                       /* fill_wave */
359 	-1,                                     /* chunk_size */
360 	0,                                      /* chunk_samples */
361 	kc_sss_to_wav_size,                     /* chunk_sample_calc */
362 	KC_WAV_FREQUENCY,                       /* sample_frequency */
363 	0,                                      /* header_samples */
364 	0                                       /* trailer_samples */
365 };
366 
kc_sss_identify(cassette_image * cassette,cassette_image::Options * opts)367 static cassette_image::error kc_sss_identify(cassette_image *cassette, cassette_image::Options *opts)
368 {
369 	return cassette->legacy_identify(opts, &kc_sss_legacy_fill_wave);
370 }
371 
372 
kc_sss_load(cassette_image * cassette)373 static cassette_image::error kc_sss_load(cassette_image *cassette)
374 {
375 	return cassette->legacy_construct(&kc_sss_legacy_fill_wave);
376 }
377 
378 static const cassette_image::Format kc_sss_format =
379 {
380 	"sss",
381 	kc_sss_identify,
382 	kc_sss_load,
383 	nullptr
384 };
385 
386 
387 CASSETTE_FORMATLIST_START(kc_cassette_formats)
388 	CASSETTE_FORMAT(kc_kcc_format)
389 	CASSETTE_FORMAT(kc_tap_format)
390 	CASSETTE_FORMAT(kc_sss_format)
391 CASSETTE_FORMATLIST_END
392