1 // license:BSD-3-Clause
2 // copyright-holders:Sandro Ronco
3 /********************************************************************
4
5 Support for VG-5000 .k7 cassette images
6
7 ********************************************************************/
8 #include "vg5k_cas.h"
9
10 #include <cassert>
11
12
13 #define SMPLO -32768
14 #define SILENCE 0
15 #define SMPHI 32767
16
17
18 static int k7_size; // FIXME: global variable prevents multiple instances
19
20 /*******************************************************************
21 Generate one high-low cycle of sample data
22 ********************************************************************/
vg5k_cas_cycle(int16_t * buffer,int sample_pos,int len)23 static inline int vg5k_cas_cycle(int16_t *buffer, int sample_pos, int len)
24 {
25 int i = 0;
26
27 if (buffer)
28 {
29 while( i < len)
30 {
31 buffer[ sample_pos + i ] = SMPHI;
32 i++;
33 }
34
35 while( i < len * 2 )
36 {
37 buffer[ sample_pos + i ] = SMPLO;
38 i++;
39 }
40 }
41 return len * 2;
42 }
43
44
45 /*******************************************************************
46 Generate n samples of silence
47 ********************************************************************/
vg5k_cas_silence(int16_t * buffer,int sample_pos,int len)48 static inline int vg5k_cas_silence(int16_t *buffer, int sample_pos, int len)
49 {
50 int i = 0;
51
52 if ( buffer )
53 for( i = 0; i < len; i++)
54 buffer[ sample_pos + i ] = SILENCE;
55
56 return len;
57 }
58
59
60 /*******************************************************************
61 Generate the end-byte samples
62 ********************************************************************/
vg5k_cas_eob(int16_t * buffer,int sample_pos)63 static inline int vg5k_cas_eob(int16_t *buffer, int sample_pos)
64 {
65 int i, samples = 0;
66
67 for (i = 0; i < 4; i++)
68 samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5);
69
70 samples += vg5k_cas_cycle( buffer, sample_pos + samples, 10);
71
72 return samples;
73 }
74
75
vg5k_cas_byte(int16_t * buffer,int sample_pos,uint8_t data)76 static inline int vg5k_cas_byte(int16_t *buffer, int sample_pos, uint8_t data)
77 {
78 /* Writing an entire byte */
79 int i, samples;
80
81 samples = 0;
82 for ( i = 0; i < 8; i++ )
83 {
84 if ( data & 0x01 )
85 {
86 samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5 );
87 samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5 );
88 }
89 else
90 {
91 samples += vg5k_cas_cycle( buffer, sample_pos + samples, 10 );
92 }
93
94 data >>= 1;
95 }
96 return samples;
97 }
98
99
100 /*******************************************************************
101 Generate n sample of synchro
102 ********************************************************************/
vg5k_k7_synchro(int16_t * buffer,int sample_pos,int len)103 static inline int vg5k_k7_synchro(int16_t *buffer, int sample_pos, int len)
104 {
105 int i, samples = 0;
106
107 for ( i = 0; i < len ; i++ )
108 samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5);
109
110 samples += vg5k_cas_eob( buffer, sample_pos + samples);
111
112 return samples;
113 }
114
115
vg5k_handle_tap(int16_t * buffer,const uint8_t * casdata)116 static int vg5k_handle_tap(int16_t *buffer, const uint8_t *casdata)
117 {
118 int data_pos, sample_count;
119
120 data_pos = 0;
121 sample_count = 0;
122
123 /* file has to start with an head block */
124 if (casdata[0] != 0xd3 || casdata[1] != 0xd3 || casdata[2] != 0xd3)
125 return -1;
126
127 /* on the entire file*/
128 while( data_pos < k7_size )
129 {
130 uint16_t block_size = 0;
131
132 /* Identify type of block */
133 if (casdata[data_pos] == 0xd3)
134 {
135 /* head block have fixed size of 32 byte */
136 block_size = 0x20;
137
138 /* 1 sec of silence before the head block */
139 sample_count += vg5k_cas_silence(buffer, sample_count, 44100);
140
141 /* head block starts with 30000 samples of synchro */
142 sample_count += vg5k_k7_synchro( buffer, sample_count, 30000 );
143 }
144 else if (casdata[data_pos] == 0xd6)
145 {
146 /* data block size is defined in head block */
147 block_size = (casdata[data_pos - 4] | casdata[data_pos - 3]<<8) + 20;
148
149 /* 10000 samples of silence before the data block */
150 sample_count += vg5k_cas_silence(buffer, sample_count, 10000);
151
152 /* data block starts with 7200 samples of synchro */
153 sample_count += vg5k_k7_synchro( buffer, sample_count, 7200);
154 }
155 else
156 {
157 /* tries to handle files that do not respect the size declared in the head block */
158 while (data_pos < k7_size && casdata[data_pos] != 0xd3 && casdata[data_pos] != 0xd6)
159 data_pos++;
160 }
161
162 /* Data samples */
163 for ( ; block_size ; data_pos++, block_size-- )
164 {
165 /* Make sure there are enough bytes left */
166 if (data_pos > k7_size)
167 return -1;
168
169 sample_count += vg5k_cas_byte( buffer, sample_count, casdata[data_pos] );
170
171 /* generate the end-byte samples */
172 sample_count += vg5k_cas_eob( buffer, sample_count);
173 }
174 }
175
176 /* Finish with 10000 samples of silence */
177 sample_count += vg5k_cas_silence(buffer, sample_count, 10000);
178
179 return sample_count;
180 }
181
182
183 /*******************************************************************
184 Generate samples for the tape image
185 ********************************************************************/
vg5k_k7_fill_wave(int16_t * buffer,int sample_count,uint8_t * bytes)186 static int vg5k_k7_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
187 {
188 return vg5k_handle_tap(buffer, bytes);
189 }
190
191
192 /*******************************************************************
193 Calculate the number of samples needed for this tape image classical
194 ********************************************************************/
vg5k_k7_to_wav_size(const uint8_t * casdata,int caslen)195 static int vg5k_k7_to_wav_size(const uint8_t *casdata, int caslen)
196 {
197 k7_size = caslen ;
198
199 return vg5k_handle_tap( nullptr, casdata );
200 }
201
202
203 static const cassette_image::LegacyWaveFiller vg5k_legacy_fill_wave =
204 {
205 vg5k_k7_fill_wave, /* fill_wave */
206 -1, /* chunk_size */
207 0, /* chunk_samples */
208 vg5k_k7_to_wav_size, /* chunk_sample_calc */
209 44100, /* sample_frequency */
210 0, /* header_samples */
211 0 /* trailer_samples */
212 };
213
vg5k_k7_identify(cassette_image * cassette,cassette_image::Options * opts)214 static cassette_image::error vg5k_k7_identify(cassette_image *cassette, cassette_image::Options *opts)
215 {
216 return cassette->legacy_identify(opts, &vg5k_legacy_fill_wave);
217 }
218
219
vg5k_k7_load(cassette_image * cassette)220 static cassette_image::error vg5k_k7_load(cassette_image *cassette)
221 {
222 return cassette->legacy_construct(&vg5k_legacy_fill_wave);
223 }
224
225
226 static const cassette_image::Format vg5k_k7_format =
227 {
228 "k7",
229 vg5k_k7_identify,
230 vg5k_k7_load,
231 nullptr
232 };
233
234
235 CASSETTE_FORMATLIST_START(vg5k_cassette_formats)
236 CASSETTE_FORMAT(vg5k_k7_format)
237 CASSETTE_FORMAT(cassette_image::wavfile_format)
238 CASSETTE_FORMATLIST_END
239