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